mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
Compare commits
160 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6384ecace0 | |||
| 38881a2850 | |||
| 0f1cf14743 | |||
| 7c34f7704e | |||
| 377415a6c3 | |||
| 1e13c6d18f | |||
| 14121f4b0e | |||
| 7768195835 | |||
| c095b7e9c4 | |||
| 1f6d34d995 | |||
| 9951eb9990 | |||
| c8f77d26a8 | |||
| 8eaa9bd8d0 | |||
| 6061f3db92 | |||
| 410aa0e0d8 | |||
| c66836cf3a | |||
| ca9211b582 | |||
| cb7d470ba4 | |||
| 5baee9a8ee | |||
| c47f52b546 | |||
| a2588d8db3 | |||
| f529b6071d | |||
| 5557e97888 | |||
| d04b07fa8b | |||
| 7486277012 | |||
| 36d60b8349 | |||
| c55191283b | |||
| ad30085b93 | |||
| e9ca41735a | |||
| df1f83f4e1 | |||
| 5879d785a9 | |||
| 186018abeb | |||
| 828b9614c2 | |||
| f5e933cd4d | |||
| 0952b1b984 | |||
| ed55f56d85 | |||
| edb8766aab | |||
| 413bb55e2c | |||
| 0db1a4bec1 | |||
| 2867da164d | |||
| 7b8ae2d6b6 | |||
| 5b8127fd4e | |||
| cc3e785692 | |||
| 0ea5cfc8fe | |||
| 70f1a5429a | |||
| 2fb2113168 | |||
| cb39611389 | |||
| 45764325b4 | |||
| bb66232164 | |||
| 9679e7f1cc | |||
| 6cdfc298cf | |||
| 9a4bfd0e96 | |||
| ee3106be98 | |||
| 57f49af726 | |||
| 38a80b45d3 | |||
| e9003e2deb | |||
| 204659bdcd | |||
| 08ca1d13d0 | |||
| 53db436a7d | |||
| 0ad15551c8 | |||
| afd65e224a | |||
| 5f50c3b60b | |||
| 8858d49989 | |||
| cf39d8b2c8 | |||
| d92a31b440 | |||
| 9c77a38358 | |||
| d0445d6f83 | |||
| a40b11f535 | |||
| 8c90f4facf | |||
| c7c9a76af5 | |||
| 6984af43a0 | |||
| 81c56c3ab8 | |||
| 3d9a1ca22a | |||
| c2083014b3 | |||
| 603a81f194 | |||
| cfc32794a7 | |||
| 1d9c53d00e | |||
| 8d2a07b927 | |||
| ffe6dfd21b | |||
| cf21380264 | |||
| 534b3b0c0b | |||
| ceeb233ff6 | |||
| de3f0af829 | |||
| a511f18c64 | |||
| 731bd5cd1b | |||
| 4acf59546e | |||
| 2ee4d9aaa1 | |||
| b58b7c59ae | |||
| e14b6a7009 | |||
| a1a2d1ab81 | |||
| 07591178cd | |||
| 43bdc888dd | |||
| 4229e670ce | |||
| 76a7ad2a3e | |||
| e4f551ac7f | |||
| 338590cc75 | |||
| 22d70604d8 | |||
| c6add1e769 | |||
| c6609a02fa | |||
| a449c4813a | |||
| 19fb1bfafc | |||
| f91b6ad0df | |||
| 94459cefe5 | |||
| 803ba4f54d | |||
| 2919fa8b14 | |||
| 6420e90124 | |||
| 2d289addb7 | |||
| 4df145dcb4 | |||
| e065da20cb | |||
| d7c77b6761 | |||
| f51029484a | |||
| 03cfcbc17c | |||
| 649f49e000 | |||
| 3bcd4dc3e0 | |||
| 772c71ec27 | |||
| 9b896dc981 | |||
| d4189feca6 | |||
| 777048cfa9 | |||
| 2b215955ca | |||
| ce25f00769 | |||
| 1a59aecb63 | |||
| cca32e67a9 | |||
| 174d492355 | |||
| 5355473dce | |||
| a531ed614a | |||
| 24ae6fdf29 | |||
| 938ce22307 | |||
| 9fb5a5b9a4 | |||
| 9fd9d9c05a | |||
| fd50227a3d | |||
| a90295430c | |||
| fa4ca63ff2 | |||
| 7019c4e8fc | |||
| b79e0e8528 | |||
| 51bed3afae | |||
| 4d4ccddad8 | |||
| f55f5315cc | |||
| 7468a00ef4 | |||
| 8d77278c6b | |||
| dde9b5ad3a | |||
| 2d4307647e | |||
| 935a293106 | |||
| 72b715fa99 | |||
| d801d0cd53 | |||
| c2f1965e03 | |||
| 75bba42b60 | |||
| 18b50db0dc | |||
| 499fce9cee | |||
| 88b29589d6 | |||
| 8cbfbcae5a | |||
| 5cd8ba2421 | |||
| 2320b18953 | |||
| 3b1913ac57 | |||
| 1da1abe314 | |||
| c9721197d5 | |||
| 738747bd05 | |||
| 0bb4a8abeb | |||
| 6ef8fcd537 | |||
| a896f7f218 | |||
| 081d2e58c6 |
+22
-13
@@ -5,17 +5,17 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
name: "Pull submodules"
|
name: "Pull submodules"
|
||||||
command: git submodule update --init
|
command: git submodule update --init --recursive
|
||||||
llvm-source-linux:
|
llvm-source-linux:
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-16-v3
|
- llvm-source-17-v1
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-16-v3
|
key: llvm-source-17-v1
|
||||||
paths:
|
paths:
|
||||||
- llvm-project/clang/lib/Headers
|
- llvm-project/clang/lib/Headers
|
||||||
- llvm-project/clang/include
|
- llvm-project/clang/include
|
||||||
@@ -55,7 +55,7 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install --no-install-recommends -y \
|
apt-get install --no-install-recommends -y \
|
||||||
@@ -69,16 +69,16 @@ commands:
|
|||||||
- build-binaryen-linux
|
- build-binaryen-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
- go-cache-v3-{{ checksum "go.mod" }}
|
- go-cache-v4-{{ checksum "go.mod" }}
|
||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- wasi-libc-sysroot-systemclang-v6
|
- wasi-libc-sysroot-systemclang-v7
|
||||||
- run: make wasi-libc
|
- run: make wasi-libc
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: wasi-libc-sysroot-systemclang-v6
|
key: wasi-libc-sysroot-systemclang-v7
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
- when:
|
- when:
|
||||||
@@ -92,18 +92,25 @@ commands:
|
|||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest XTENSA=0
|
- run: make smoketest XTENSA=0
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
- ~/.cache/go-build
|
- ~/.cache/go-build
|
||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-llvm14-go118:
|
test-llvm15-go118:
|
||||||
docker:
|
docker:
|
||||||
- image: golang:1.18-buster
|
- image: golang:1.18-bullseye
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "14"
|
llvm: "15"
|
||||||
|
resource_class: large
|
||||||
|
test-llvm17-go122:
|
||||||
|
docker:
|
||||||
|
- image: golang:1.22-bullseye
|
||||||
|
steps:
|
||||||
|
- test-linux:
|
||||||
|
llvm: "17"
|
||||||
resource_class: large
|
resource_class: large
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
@@ -111,4 +118,6 @@ workflows:
|
|||||||
jobs:
|
jobs:
|
||||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||||
# least the smoke tests still pass.
|
# least the smoke tests still pass.
|
||||||
- test-llvm14-go118
|
- test-llvm15-go118
|
||||||
|
# This tests LLVM 17 support when linking against system libraries.
|
||||||
|
- test-llvm17-go122
|
||||||
|
|||||||
@@ -14,26 +14,36 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
build-macos:
|
build-macos:
|
||||||
name: build-macos
|
name: build-macos
|
||||||
runs-on: macos-11
|
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 }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-16-macos-v1
|
key: llvm-source-17-${{ matrix.os }}-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -44,7 +54,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -55,10 +65,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore LLVM build cache
|
- name: Restore LLVM build cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-16-macos-v1
|
key: llvm-build-17-${{ matrix.os }}-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -73,16 +83,16 @@ jobs:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save LLVM build cache
|
- name: Save LLVM build cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache wasi-libc sysroot
|
- name: Cache wasi-libc sysroot
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-v4
|
key: wasi-libc-sysroot-${{ matrix.os }}-v1
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
@@ -98,7 +108,7 @@ jobs:
|
|||||||
run: make tinygo-test
|
run: make tinygo-test
|
||||||
- name: Make release artifact
|
- name: Make release artifact
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cp -p build/release.tar.gz build/tinygo.darwin-amd64.tar.gz
|
run: cp -p build/release.tar.gz build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
# Note: this release artifact is double-zipped, see:
|
# Note: this release artifact is double-zipped, see:
|
||||||
# https://github.com/actions/upload-artifact/issues/39
|
# https://github.com/actions/upload-artifact/issues/39
|
||||||
@@ -106,29 +116,44 @@ jobs:
|
|||||||
# - have a double-zipped artifact when downloaded from the UI
|
# - have a double-zipped artifact when downloaded from the UI
|
||||||
# - have a very slow artifact upload
|
# - have a very slow artifact upload
|
||||||
# We're doing the former here, to keep artifact uploads fast.
|
# We're doing the former here, to keep artifact uploads fast.
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: darwin-amd64-double-zipped
|
name: darwin-${{ matrix.goarch }}-double-zipped
|
||||||
path: build/tinygo.darwin-amd64.tar.gz
|
path: build/tinygo.darwin-${{ matrix.goarch }}.tar.gz
|
||||||
- name: Smoke tests
|
- name: Smoke tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
run: make smoketest TINYGO=$(PWD)/build/tinygo
|
||||||
test-macos-homebrew:
|
test-macos-homebrew:
|
||||||
name: homebrew-install
|
name: homebrew-install
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
version: [16, 17]
|
||||||
steps:
|
steps:
|
||||||
- name: Install LLVM
|
- name: Set up Homebrew
|
||||||
shell: bash
|
uses: Homebrew/actions/setup-homebrew@master
|
||||||
|
- name: Fix Python symlinks
|
||||||
run: |
|
run: |
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@16
|
# 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
|
||||||
|
run: |
|
||||||
|
brew install llvm@${{ matrix.version }}
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Build TinyGo
|
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||||
run: go install
|
run: go install -tags=llvm${{ matrix.version }}
|
||||||
- name: Check binary
|
- name: Check binary
|
||||||
run: tinygo version
|
run: tinygo version
|
||||||
|
- name: Build TinyGo (default LLVM)
|
||||||
|
if: matrix.version == 17
|
||||||
|
run: go install
|
||||||
|
- name: Check binary
|
||||||
|
if: matrix.version == 17
|
||||||
|
run: tinygo version
|
||||||
|
|||||||
@@ -19,15 +19,26 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
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
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
tinygo/tinygo-dev
|
tinygo/tinygo-dev
|
||||||
@@ -36,24 +47,23 @@ jobs:
|
|||||||
type=sha,format=long
|
type=sha,format=long
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
- name: Log in to Github Container Registry
|
- name: Log in to Github Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-16
|
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
- name: Trigger Drivers repo build on Github Actions
|
- name: Trigger Drivers repo build on Github Actions
|
||||||
|
|||||||
+73
-177
@@ -18,10 +18,10 @@ jobs:
|
|||||||
# statically linked binary.
|
# statically linked binary.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: golang:1.21-alpine
|
image: golang:1.22-alpine
|
||||||
steps:
|
steps:
|
||||||
- name: Install apk dependencies
|
- name: Install apk dependencies
|
||||||
# tar: needed for actions/cache@v3
|
# tar: needed for actions/cache@v4
|
||||||
# git+openssh: needed for checkout (I think?)
|
# git+openssh: needed for checkout (I think?)
|
||||||
# ruby: needed to install fpm
|
# ruby: needed to install fpm
|
||||||
run: apk add tar git openssh make g++ ruby
|
run: apk add tar git openssh make g++ ruby
|
||||||
@@ -29,21 +29,21 @@ jobs:
|
|||||||
# We're not on a multi-user machine, so this is safe.
|
# We're not on a multi-user machine, so this is safe.
|
||||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }}
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-16-linux-alpine-v1
|
key: llvm-source-17-linux-alpine-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -65,10 +65,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore LLVM build cache
|
- name: Restore LLVM build cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-16-linux-alpine-v1
|
key: llvm-build-17-linux-alpine-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -83,13 +83,13 @@ jobs:
|
|||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save LLVM build cache
|
- name: Save LLVM build cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-alpine-v1
|
key: binaryen-linux-alpine-v1
|
||||||
@@ -100,10 +100,10 @@ jobs:
|
|||||||
apk add cmake samurai python3
|
apk add cmake samurai python3
|
||||||
make binaryen STATIC=1
|
make binaryen STATIC=1
|
||||||
- name: Cache wasi-libc
|
- name: Cache wasi-libc
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-linux-alpine-v1
|
key: wasi-libc-sysroot-linux-alpine-v2
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
cp -p build/release.deb /tmp/tinygo_amd64.deb
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
path: |
|
path: |
|
||||||
@@ -131,20 +131,20 @@ jobs:
|
|||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
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
|
curl https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
|
||||||
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/*
|
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
|
||||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||||
- name: Download release artifact
|
- name: Download release artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
- name: Extract release tarball
|
- name: Extract release tarball
|
||||||
@@ -161,7 +161,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
@@ -176,25 +176,25 @@ jobs:
|
|||||||
simavr \
|
simavr \
|
||||||
ninja-build
|
ninja-build
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
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
|
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
|
||||||
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/*
|
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
|
||||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-16-linux-asserts-v1
|
key: llvm-source-17-linux-asserts-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -205,7 +205,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -216,10 +216,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore LLVM build cache
|
- name: Restore LLVM build cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-16-linux-asserts-v1
|
key: llvm-build-17-linux-asserts-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -232,13 +232,13 @@ jobs:
|
|||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save LLVM build cache
|
- name: Save LLVM build cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-asserts-v1
|
key: binaryen-linux-asserts-v1
|
||||||
@@ -247,10 +247,10 @@ jobs:
|
|||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
run: make binaryen
|
run: make binaryen
|
||||||
- name: Cache wasi-libc
|
- name: Cache wasi-libc
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-linux-asserts-v5
|
key: wasi-libc-sysroot-linux-asserts-v6
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
@@ -267,7 +267,7 @@ jobs:
|
|||||||
- run: make smoketest
|
- run: make smoketest
|
||||||
- run: make wasmtest
|
- run: make wasmtest
|
||||||
- run: make tinygo-baremetal
|
- run: make tinygo-baremetal
|
||||||
build-linux-arm:
|
build-linux-cross:
|
||||||
# Build ARM Linux binaries, ready for release.
|
# Build ARM Linux binaries, ready for release.
|
||||||
# This intentionally uses an older Linux image, so that we compile against
|
# This intentionally uses an older Linux image, so that we compile against
|
||||||
# an older glibc version and therefore are compatible with a wide range of
|
# an older glibc version and therefore are compatible with a wide range of
|
||||||
@@ -276,28 +276,38 @@ jobs:
|
|||||||
# in that process to avoid doing lots of duplicate work and to avoid
|
# in that process to avoid doing lots of duplicate work and to avoid
|
||||||
# complications around precompiled libraries such as compiler-rt shipped as
|
# complications around precompiled libraries such as compiler-rt shipped as
|
||||||
# part of the release tarball.
|
# 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
|
runs-on: ubuntu-20.04
|
||||||
needs: build-linux
|
needs: build-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends \
|
sudo apt-get install --no-install-recommends \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
g++-arm-linux-gnueabihf \
|
g++-${{ matrix.toolchain }} \
|
||||||
libc6-dev-armhf-cross
|
libc6-dev-${{ matrix.libc }}-cross
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-16-linux-v1
|
key: llvm-source-17-linux-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -308,7 +318,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save LLVM source cache
|
- name: Save LLVM source cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -319,10 +329,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore LLVM build cache
|
- name: Restore LLVM build cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-16-linux-arm-v1
|
key: llvm-build-17-linux-${{ matrix.goarch }}-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -333,27 +343,27 @@ jobs:
|
|||||||
# Install build dependencies.
|
# Install build dependencies.
|
||||||
sudo apt-get install --no-install-recommends ninja-build
|
sudo apt-get install --no-install-recommends ninja-build
|
||||||
# build!
|
# build!
|
||||||
make llvm-build CROSS=arm-linux-gnueabihf
|
make llvm-build CROSS=${{ matrix.toolchain }}
|
||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save LLVM build cache
|
- name: Save LLVM build cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache Binaryen
|
- name: Cache Binaryen
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-arm-v1
|
key: binaryen-linux-${{ matrix.goarch }}-v3
|
||||||
path: build/wasm-opt
|
path: build/wasm-opt
|
||||||
- name: Build Binaryen
|
- name: Build Binaryen
|
||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install --no-install-recommends ninja-build
|
sudo apt-get install --no-install-recommends ninja-build
|
||||||
git submodule update --init lib/binaryen
|
git submodule update --init --recursive lib/binaryen
|
||||||
make CROSS=arm-linux-gnueabihf binaryen
|
make CROSS=${{ matrix.toolchain }} binaryen
|
||||||
- name: Install fpm
|
- name: Install fpm
|
||||||
run: |
|
run: |
|
||||||
sudo gem install --version 4.0.7 public_suffix
|
sudo gem install --version 4.0.7 public_suffix
|
||||||
@@ -361,9 +371,9 @@ jobs:
|
|||||||
sudo gem install --no-document fpm
|
sudo gem install --no-document fpm
|
||||||
- name: Build TinyGo binary
|
- name: Build TinyGo binary
|
||||||
run: |
|
run: |
|
||||||
make CROSS=arm-linux-gnueabihf
|
make CROSS=${{ matrix.toolchain }}
|
||||||
- name: Download amd64 release
|
- name: Download amd64 release
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: linux-amd64-double-zipped
|
name: linux-amd64-double-zipped
|
||||||
- name: Extract amd64 release
|
- name: Extract amd64 release
|
||||||
@@ -374,129 +384,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp -p build/tinygo build/release/tinygo/bin
|
cp -p build/tinygo build/release/tinygo/bin
|
||||||
cp -p build/wasm-opt build/release/tinygo/bin
|
cp -p build/wasm-opt build/release/tinygo/bin
|
||||||
- name: Create arm release
|
- name: Create ${{ matrix.goarch }} release
|
||||||
run: |
|
run: |
|
||||||
make release deb RELEASEONLY=1 DEB_ARCH=armhf
|
make release deb RELEASEONLY=1 DEB_ARCH=${{ matrix.libc }}
|
||||||
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
|
cp -p build/release.tar.gz /tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
|
||||||
cp -p build/release.deb /tmp/tinygo_armhf.deb
|
cp -p build/release.deb /tmp/tinygo_${{ matrix.libc }}.deb
|
||||||
- name: Publish release artifact
|
- name: Publish release artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: linux-arm-double-zipped
|
name: linux-${{ matrix.goarch }}-double-zipped
|
||||||
path: |
|
path: |
|
||||||
/tmp/tinygo.linux-arm.tar.gz
|
/tmp/tinygo.linux-${{ matrix.goarch }}.tar.gz
|
||||||
/tmp/tinygo_armhf.deb
|
/tmp/tinygo_${{ matrix.libc }}.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
|
|
||||||
|
|||||||
@@ -25,18 +25,18 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
tinygo/llvm-16
|
tinygo/llvm-17
|
||||||
ghcr.io/${{ github.repository_owner }}/llvm-16
|
ghcr.io/${{ github.repository_owner }}/llvm-17
|
||||||
tags: |
|
tags: |
|
||||||
type=sha,format=long
|
type=sha,format=long
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
@@ -46,13 +46,13 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
- name: Log in to Github Container Registry
|
- name: Log in to Github Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
target: tinygo-llvm-build
|
target: tinygo-llvm-build
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
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-17-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"
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# 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-17 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-17-dev \
|
||||||
|
clang-17 \
|
||||||
|
libclang-17-dev \
|
||||||
|
lld-17
|
||||||
@@ -18,32 +18,24 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: ./.github/workflows/sizediff-install-pkgs.sh
|
||||||
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
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-16-sizediff-v1
|
key: llvm-source-17-sizediff-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/compiler-rt
|
llvm-project/compiler-rt
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Cache Go
|
- name: Cache Go
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
key: go-cache-linux-sizediff-v1-${{ hashFiles('go.mod') }}
|
key: go-cache-linux-sizediff-v1-${{ hashFiles('go.mod') }}
|
||||||
path: |
|
path: |
|
||||||
@@ -55,22 +47,25 @@ jobs:
|
|||||||
- name: Save HEAD
|
- name: Save HEAD
|
||||||
run: git branch github-actions-saved-HEAD HEAD
|
run: git branch github-actions-saved-HEAD HEAD
|
||||||
|
|
||||||
# Compute sizes for the dev branch
|
|
||||||
- name: Checkout dev branch
|
|
||||||
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
|
# 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
|
- name: Build tinygo binary for the PR branch
|
||||||
run: go install
|
run: go install
|
||||||
- name: Determine binary sizes on the PR branch
|
- name: Determine binary sizes on the PR branch
|
||||||
run: (cd drivers; make smoke-test XTENSA=0 | tee sizes-pr.txt)
|
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
|
||||||
|
- 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)
|
||||||
|
|
||||||
# Create comment
|
# Create comment
|
||||||
# TODO: add a summary, something like:
|
# TODO: add a summary, something like:
|
||||||
# - overall size difference (percent)
|
# - overall size difference (percent)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
steps:
|
steps:
|
||||||
- name: Configure pagefile
|
- name: Configure pagefile
|
||||||
uses: al-cheb/configure-pagefile-action@v1.3
|
uses: al-cheb/configure-pagefile-action@v1.4
|
||||||
with:
|
with:
|
||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
@@ -29,19 +29,19 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install ninja binaryen
|
scoop install ninja binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore cached LLVM source
|
- name: Restore cached LLVM source
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-16-windows-v1
|
key: llvm-source-17-windows-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
run: make llvm-source
|
run: make llvm-source
|
||||||
- name: Save cached LLVM source
|
- name: Save cached LLVM source
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||||
@@ -63,10 +63,10 @@ jobs:
|
|||||||
llvm-project/lld/include
|
llvm-project/lld/include
|
||||||
llvm-project/llvm/include
|
llvm-project/llvm/include
|
||||||
- name: Restore cached LLVM build
|
- name: Restore cached LLVM build
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-16-windows-v2
|
key: llvm-build-17-windows-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -80,23 +80,23 @@ jobs:
|
|||||||
# Remove unnecessary object files (to reduce cache size).
|
# Remove unnecessary object files (to reduce cache size).
|
||||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||||
- name: Save cached LLVM build
|
- name: Save cached LLVM build
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache wasi-libc sysroot
|
- name: Cache wasi-libc sysroot
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
with:
|
with:
|
||||||
key: wasi-libc-sysroot-v4
|
key: wasi-libc-sysroot-v5
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- name: Build wasi-libc
|
- name: Build wasi-libc
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||||
run: make wasi-libc
|
run: make wasi-libc
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
scoop install wasmtime
|
scoop install wasmtime@14.0.4
|
||||||
- name: make gen-device
|
- name: make gen-device
|
||||||
run: make -j3 gen-device
|
run: make -j3 gen-device
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
@@ -116,7 +116,7 @@ jobs:
|
|||||||
# - have a dobule-zipped artifact when downloaded from the UI
|
# - have a dobule-zipped artifact when downloaded from the UI
|
||||||
# - have a very slow artifact upload
|
# - have a very slow artifact upload
|
||||||
# We're doing the former here, to keep artifact uploads fast.
|
# We're doing the former here, to keep artifact uploads fast.
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: windows-amd64-double-zipped
|
name: windows-amd64-double-zipped
|
||||||
path: build/release/release.zip
|
path: build/release/release.zip
|
||||||
@@ -126,7 +126,7 @@ jobs:
|
|||||||
needs: build-windows
|
needs: build-windows
|
||||||
steps:
|
steps:
|
||||||
- name: Configure pagefile
|
- name: Configure pagefile
|
||||||
uses: al-cheb/configure-pagefile-action@v1.3
|
uses: al-cheb/configure-pagefile-action@v1.4
|
||||||
with:
|
with:
|
||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
@@ -139,14 +139,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install binaryen
|
scoop install binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: windows-amd64-double-zipped
|
name: windows-amd64-double-zipped
|
||||||
path: build/
|
path: build/
|
||||||
@@ -163,20 +163,20 @@ jobs:
|
|||||||
needs: build-windows
|
needs: build-windows
|
||||||
steps:
|
steps:
|
||||||
- name: Configure pagefile
|
- name: Configure pagefile
|
||||||
uses: al-cheb/configure-pagefile-action@v1.3
|
uses: al-cheb/configure-pagefile-action@v1.4
|
||||||
with:
|
with:
|
||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
disk-root: "C:"
|
disk-root: "C:"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: windows-amd64-double-zipped
|
name: windows-amd64-double-zipped
|
||||||
path: build/
|
path: build/
|
||||||
@@ -192,7 +192,7 @@ jobs:
|
|||||||
needs: build-windows
|
needs: build-windows
|
||||||
steps:
|
steps:
|
||||||
- name: Configure pagefile
|
- name: Configure pagefile
|
||||||
uses: al-cheb/configure-pagefile-action@v1.3
|
uses: al-cheb/configure-pagefile-action@v1.4
|
||||||
with:
|
with:
|
||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
@@ -203,16 +203,16 @@ jobs:
|
|||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
scoop install binaryen wasmtime
|
scoop install binaryen && scoop install wasmtime@14.0.4
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: windows-amd64-double-zipped
|
name: windows-amd64-double-zipped
|
||||||
path: build/
|
path: build/
|
||||||
|
|||||||
+7
-2
@@ -9,10 +9,11 @@
|
|||||||
url = https://github.com/avr-rust/avr-mcu.git
|
url = https://github.com/avr-rust/avr-mcu.git
|
||||||
[submodule "lib/cmsis-svd"]
|
[submodule "lib/cmsis-svd"]
|
||||||
path = lib/cmsis-svd
|
path = lib/cmsis-svd
|
||||||
url = https://github.com/tinygo-org/cmsis-svd
|
url = https://github.com/cmsis-svd/cmsis-svd-data.git
|
||||||
|
branch = main
|
||||||
[submodule "lib/wasi-libc"]
|
[submodule "lib/wasi-libc"]
|
||||||
path = lib/wasi-libc
|
path = lib/wasi-libc
|
||||||
url = https://github.com/CraneStation/wasi-libc
|
url = https://github.com/WebAssembly/wasi-libc
|
||||||
[submodule "lib/picolibc"]
|
[submodule "lib/picolibc"]
|
||||||
path = lib/picolibc
|
path = lib/picolibc
|
||||||
url = https://github.com/keith-packard/picolibc.git
|
url = https://github.com/keith-packard/picolibc.git
|
||||||
@@ -34,3 +35,7 @@
|
|||||||
[submodule "lib/renesas-svd"]
|
[submodule "lib/renesas-svd"]
|
||||||
path = lib/renesas-svd
|
path = lib/renesas-svd
|
||||||
url = https://github.com/tinygo-org/renesas-svd.git
|
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
|
||||||
|
|||||||
+2
-1
@@ -19,6 +19,7 @@ LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
|||||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||||
|
|
||||||
* Go (1.18+)
|
* Go (1.18+)
|
||||||
|
* GNU Make
|
||||||
* Standard build tools (gcc/clang)
|
* Standard build tools (gcc/clang)
|
||||||
* git
|
* git
|
||||||
* CMake
|
* CMake
|
||||||
@@ -84,7 +85,7 @@ Now that we have a working static build, it's time to make a release tarball:
|
|||||||
|
|
||||||
If you did not clone the repository with the `--recursive` option, you will get errors until you initialize the project submodules:
|
If you did not clone the repository with the `--recursive` option, you will get errors until you initialize the project submodules:
|
||||||
|
|
||||||
git submodule update --init
|
git submodule update --init --recursive
|
||||||
|
|
||||||
The release tarball is stored in build/release.tar.gz, and can be extracted with
|
The release tarball is stored in build/release.tar.gz, and can be extracted with
|
||||||
the following command (for example in ~/lib):
|
the following command (for example in ~/lib):
|
||||||
|
|||||||
+102
@@ -1,3 +1,105 @@
|
|||||||
|
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
|
0.30.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+22
-19
@@ -1,10 +1,15 @@
|
|||||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||||
FROM golang:1.21 AS tinygo-llvm
|
FROM golang:1.22 AS tinygo-llvm
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y apt-utils make cmake clang-15 ninja-build
|
apt-get install -y apt-utils make cmake clang-15 ninja-build && \
|
||||||
|
rm -rf \
|
||||||
|
/var/lib/apt/lists/* \
|
||||||
|
/var/log/* \
|
||||||
|
/var/tmp/* \
|
||||||
|
/tmp/*
|
||||||
|
|
||||||
COPY ./Makefile /tinygo/Makefile
|
COPY ./GNUmakefile /tinygo/GNUmakefile
|
||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
make llvm-source
|
make llvm-source
|
||||||
@@ -15,26 +20,24 @@ FROM tinygo-llvm AS tinygo-llvm-build
|
|||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
make llvm-build
|
make llvm-build
|
||||||
|
|
||||||
# tinygo-compiler stage builds the compiler itself
|
# tinygo-compiler-build stage builds the compiler itself
|
||||||
FROM tinygo-llvm-build AS tinygo-compiler
|
FROM tinygo-llvm-build AS tinygo-compiler-build
|
||||||
|
|
||||||
COPY . /tinygo
|
COPY . /tinygo
|
||||||
|
|
||||||
# update submodules
|
# build the compiler and tools
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
rm -rf ./lib/*/ && \
|
git submodule update --init --recursive && \
|
||||||
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 gen-device -j4 && \
|
||||||
cp build/* $GOPATH/bin/
|
make build/release
|
||||||
|
|
||||||
|
# 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"]
|
CMD ["tinygo"]
|
||||||
|
|||||||
+31
-19
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
|||||||
|
|
||||||
# Try to autodetect LLVM build tools.
|
# Try to autodetect LLVM build tools.
|
||||||
# Versions are listed here in descending priority order.
|
# Versions are listed here in descending priority order.
|
||||||
LLVM_VERSIONS = 16 15 14 13 12 11
|
LLVM_VERSIONS = 17 16 15
|
||||||
errifempty = $(if $(1),$(1),$(error $(2)))
|
errifempty = $(if $(1),$(1),$(error $(2)))
|
||||||
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
||||||
toolSearchPathsVersion = $(1)-$(2)
|
toolSearchPathsVersion = $(1)-$(2)
|
||||||
@@ -32,9 +32,6 @@ export GOROOT = $(shell $(GO) env GOROOT)
|
|||||||
# Flags to pass to go test.
|
# Flags to pass to go test.
|
||||||
GOTESTFLAGS ?=
|
GOTESTFLAGS ?=
|
||||||
|
|
||||||
# md5sum binary
|
|
||||||
MD5SUM = md5sum
|
|
||||||
|
|
||||||
# tinygo binary for tests
|
# tinygo binary for tests
|
||||||
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
||||||
|
|
||||||
@@ -130,14 +127,14 @@ ifeq ($(OS),Windows_NT)
|
|||||||
USE_SYSTEM_BINARYEN ?= 1
|
USE_SYSTEM_BINARYEN ?= 1
|
||||||
|
|
||||||
else ifeq ($(shell uname -s),Darwin)
|
else ifeq ($(shell uname -s),Darwin)
|
||||||
MD5SUM = md5
|
MD5SUM ?= md5
|
||||||
|
|
||||||
CGO_LDFLAGS += -lxar
|
CGO_LDFLAGS += -lxar
|
||||||
|
|
||||||
USE_SYSTEM_BINARYEN ?= 1
|
USE_SYSTEM_BINARYEN ?= 1
|
||||||
|
|
||||||
else ifeq ($(shell uname -s),FreeBSD)
|
else ifeq ($(shell uname -s),FreeBSD)
|
||||||
MD5SUM = md5
|
MD5SUM ?= md5
|
||||||
START_GROUP = -Wl,--start-group
|
START_GROUP = -Wl,--start-group
|
||||||
END_GROUP = -Wl,--end-group
|
END_GROUP = -Wl,--end-group
|
||||||
else
|
else
|
||||||
@@ -145,6 +142,9 @@ else
|
|||||||
END_GROUP = -Wl,--end-group
|
END_GROUP = -Wl,--end-group
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# md5sum binary default, can be overridden by an environment variable
|
||||||
|
MD5SUM ?= md5sum
|
||||||
|
|
||||||
# Libraries that should be linked in for the statically linked Clang.
|
# Libraries that should be linked in for the statically linked Clang.
|
||||||
CLANG_LIB_NAMES = clangAnalysis 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++
|
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||||
@@ -190,7 +190,7 @@ gen-device: gen-device-stm32
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
gen-device-avr:
|
gen-device-avr:
|
||||||
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init --recursive"; exit 1; fi
|
||||||
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||||
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||||
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||||
@@ -238,7 +238,7 @@ gen-device-renesas: build/gen-device-svd
|
|||||||
|
|
||||||
# Get LLVM sources.
|
# Get LLVM sources.
|
||||||
$(LLVM_PROJECTDIR)/llvm:
|
$(LLVM_PROJECTDIR)/llvm:
|
||||||
git clone -b xtensa_release_16.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
git clone -b xtensa_release_17.0.1 --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
||||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||||
|
|
||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
@@ -256,7 +256,7 @@ ifneq ($(USE_SYSTEM_BINARYEN),1)
|
|||||||
binaryen: build/wasm-opt$(EXE)
|
binaryen: build/wasm-opt$(EXE)
|
||||||
build/wasm-opt$(EXE):
|
build/wasm-opt$(EXE):
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
|
||||||
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
|
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -264,23 +264,23 @@ endif
|
|||||||
.PHONY: wasi-libc
|
.PHONY: wasi-libc
|
||||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init --recursive"; 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)
|
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.
|
# Check for Node.js used during WASM tests.
|
||||||
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
||||||
MIN_NODEJS_VERSION=16
|
MIN_NODEJS_VERSION=18
|
||||||
|
|
||||||
.PHONY: check-nodejs-version
|
.PHONY: check-nodejs-version
|
||||||
check-nodejs-version:
|
check-nodejs-version:
|
||||||
ifeq (, $(shell which node))
|
ifeq (, $(shell which node))
|
||||||
@echo "Install NodeJS version 16+ to run tests."; exit 1;
|
@echo "Install NodeJS version 18+ to run tests."; exit 1;
|
||||||
endif
|
endif
|
||||||
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 16+ to run tests."; exit 1; fi
|
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 18+ to run tests."; exit 1; fi
|
||||||
|
|
||||||
# Build the Go compiler.
|
# Build the Go compiler.
|
||||||
tinygo:
|
tinygo:
|
||||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(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
|
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" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||||
@@ -506,6 +506,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
|
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
|
||||||
@$(MD5SUM) test.hex
|
@$(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
|
# test simulated boards on play.tinygo.org
|
||||||
ifneq ($(WASM), 0)
|
ifneq ($(WASM), 0)
|
||||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||||
@@ -668,6 +670,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
|
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=thumby examples/echo
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
# test pwm
|
# test pwm
|
||||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -682,6 +686,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/serial
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
ifneq ($(STM32), 0)
|
ifneq ($(STM32), 0)
|
||||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -715,7 +721,11 @@ ifneq ($(STM32), 0)
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=mksnanov3 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
endif
|
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/serial
|
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||||
@@ -752,9 +762,7 @@ ifneq ($(XTENSA), 0)
|
|||||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
|
$(TINYGO) build -size short -o test.bin -target mch2022 examples/serial
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
endif
|
endif
|
||||||
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
$(TINYGO) build -size short -o test.bin -target=qtpy-esp32c3 examples/serial
|
||||||
@$(MD5SUM) test.bin
|
|
||||||
$(TINYGO) build -size short -o test.bin -target=esp32c3-12f examples/serial
|
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
|
$(TINYGO) build -size short -o test.bin -target=m5stamp-c3 examples/serial
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
@@ -767,6 +775,7 @@ endif
|
|||||||
ifneq ($(WASM), 0)
|
ifneq ($(WASM), 0)
|
||||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
||||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
||||||
|
$(TINYGO) build -size short -o wasm.wasm -target=wasm-unknown examples/hello-wasm-unknown
|
||||||
endif
|
endif
|
||||||
# test various compiler flags
|
# test various compiler flags
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
|
||||||
@@ -775,6 +784,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
||||||
@$(MD5SUM) test.hex
|
@$(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
|
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||||
@$(MD5SUM) test.nro
|
@$(MD5SUM) test.nro
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
@@ -834,6 +845,7 @@ endif
|
|||||||
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/linux build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
||||||
@@ -878,7 +890,7 @@ deb:
|
|||||||
@mkdir -p build/release-deb/usr/local/lib
|
@mkdir -p build/release-deb/usr/local/lib
|
||||||
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
|
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
|
||||||
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
|
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
|
||||||
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
fpm -f -s dir -t deb -n tinygo -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)
|
ifneq ($(RELEASEONLY), 1)
|
||||||
release: build/release
|
release: build/release
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# TinyGo - Go compiler for small places
|
# TinyGo - Go compiler for small places
|
||||||
|
|
||||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [](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.
|
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
|
||||||
|
|
||||||
|
|||||||
+47
-60
@@ -83,8 +83,7 @@ type packageAction struct {
|
|||||||
FileHashes map[string]string // hash of every file that's part of the package
|
FileHashes map[string]string // hash of every file that's part of the package
|
||||||
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
|
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
|
||||||
Imports map[string]string // map from imported package to action ID hash
|
Imports map[string]string // map from imported package to action ID hash
|
||||||
OptLevel int // LLVM optimization level (0-3)
|
OptLevel string // LLVM optimization level (O0, O1, O2, Os, Oz)
|
||||||
SizeLevel int // LLVM optimization for size level (0-2)
|
|
||||||
UndefinedGlobals []string // globals that are left as external globals (no initializer)
|
UndefinedGlobals []string // globals that are left as external globals (no initializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +114,30 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
cacheDir = tmpdir
|
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.
|
// Check for a libc dependency.
|
||||||
// As a side effect, this also creates the headers for the given libc, if
|
// As a side effect, this also creates the headers for the given libc, if
|
||||||
// the libc needs them.
|
// the libc needs them.
|
||||||
@@ -158,7 +181,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||||
}
|
}
|
||||||
|
|
||||||
optLevel, sizeLevel, _ := config.OptLevels()
|
optLevel, speedLevel, sizeLevel := config.OptLevel()
|
||||||
compilerConfig := &compiler.Config{
|
compilerConfig := &compiler.Config{
|
||||||
Triple: config.Triple(),
|
Triple: config.Triple(),
|
||||||
CPU: config.CPU(),
|
CPU: config.CPU(),
|
||||||
@@ -169,11 +192,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
CodeModel: config.CodeModel(),
|
CodeModel: config.CodeModel(),
|
||||||
RelocationModel: config.RelocationModel(),
|
RelocationModel: config.RelocationModel(),
|
||||||
SizeLevel: sizeLevel,
|
SizeLevel: sizeLevel,
|
||||||
TinyGoVersion: goenv.Version,
|
TinyGoVersion: goenv.Version(),
|
||||||
|
|
||||||
Scheduler: config.Scheduler(),
|
Scheduler: config.Scheduler(),
|
||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
DefaultStackSize: config.StackSize(),
|
DefaultStackSize: config.StackSize(),
|
||||||
|
MaxStackAlloc: config.MaxStackAlloc(),
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
|
Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
|
||||||
}
|
}
|
||||||
@@ -188,7 +212,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
defer machine.Dispose()
|
defer machine.Dispose()
|
||||||
|
|
||||||
// Load entire program AST into memory.
|
// Load entire program AST into memory.
|
||||||
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, pkgName, types.Config{
|
||||||
Sizes: compiler.Sizes(machine),
|
Sizes: compiler.Sizes(machine),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -217,34 +241,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
var packageJobs []*compileJob
|
var packageJobs []*compileJob
|
||||||
packageActionIDJobs := make(map[string]*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
|
var embedFileObjects []*compileJob
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkg := pkg // necessary to avoid a race condition
|
pkg := pkg // necessary to avoid a race condition
|
||||||
|
|
||||||
var undefinedGlobals []string
|
var undefinedGlobals []string
|
||||||
for name := range config.Options.GlobalValues[pkg.Pkg.Path()] {
|
for name := range globalValues[pkg.Pkg.Path()] {
|
||||||
undefinedGlobals = append(undefinedGlobals, name)
|
undefinedGlobals = append(undefinedGlobals, name)
|
||||||
}
|
}
|
||||||
sort.Strings(undefinedGlobals)
|
sort.Strings(undefinedGlobals)
|
||||||
@@ -321,7 +323,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
EmbeddedFiles: make(map[string]string, len(allFiles)),
|
EmbeddedFiles: make(map[string]string, len(allFiles)),
|
||||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||||
OptLevel: optLevel,
|
OptLevel: optLevel,
|
||||||
SizeLevel: sizeLevel,
|
|
||||||
UndefinedGlobals: undefinedGlobals,
|
UndefinedGlobals: undefinedGlobals,
|
||||||
}
|
}
|
||||||
for filePath, hash := range pkg.FileHashes {
|
for filePath, hash := range pkg.FileHashes {
|
||||||
@@ -446,7 +447,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if pkgInit.IsNil() {
|
if pkgInit.IsNil() {
|
||||||
panic("init not found for " + pkg.Pkg.Path())
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
}
|
}
|
||||||
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.Options.InterpMaxDepth, config.Options.InterpMaxInstr, config.DumpSSA())
|
err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -532,13 +533,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
irbuilder := mod.Context().NewBuilder()
|
irbuilder := mod.Context().NewBuilder()
|
||||||
defer irbuilder.Dispose()
|
defer irbuilder.Dispose()
|
||||||
irbuilder.SetInsertPointAtEnd(block)
|
irbuilder.SetInsertPointAtEnd(block)
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
|
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
|
||||||
if pkgInit.IsNil() {
|
if pkgInit.IsNil() {
|
||||||
panic("init not found for " + pkg.Pkg.Path())
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
}
|
}
|
||||||
irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
|
irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(ptrType)}, "")
|
||||||
}
|
}
|
||||||
irbuilder.CreateRetVoid()
|
irbuilder.CreateRetVoid()
|
||||||
|
|
||||||
@@ -573,7 +574,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
// Run all optimization passes, which are much more effective now
|
// Run all optimization passes, which are much more effective now
|
||||||
// that the optimizer can see the whole program at once.
|
// that the optimizer can see the whole program at once.
|
||||||
err := optimizeProgram(mod, config)
|
err := optimizeProgram(mod, config, globalValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -662,7 +663,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile extra file " + path,
|
description: "compile extra file " + path,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands)
|
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(false), config.Options.PrintCommands)
|
||||||
job.result = result
|
job.result = result
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@@ -743,17 +744,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if config.GOOS() == "windows" {
|
if config.GOOS() == "windows" {
|
||||||
// Options for the MinGW wrapper for the lld COFF linker.
|
// Options for the MinGW wrapper for the lld COFF linker.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||||
} else if config.GOOS() == "darwin" {
|
} else if config.GOOS() == "darwin" {
|
||||||
// Options for the ld64-compatible lld linker.
|
// Options for the ld64-compatible lld linker.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"--lto-O"+strconv.Itoa(optLevel),
|
"--lto-O"+strconv.Itoa(speedLevel),
|
||||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||||
} else {
|
} else {
|
||||||
// Options for the ELF linker.
|
// Options for the ELF linker.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"--lto-O"+strconv.Itoa(optLevel),
|
"--lto-O"+strconv.Itoa(speedLevel),
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -764,7 +765,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if sizeLevel >= 2 {
|
if sizeLevel >= 2 {
|
||||||
// Workaround with roughly the same effect as
|
// Workaround with roughly the same effect as
|
||||||
// https://reviews.llvm.org/D119342.
|
// https://reviews.llvm.org/D119342.
|
||||||
// Can hopefully be removed in LLVM 15.
|
// Can hopefully be removed in LLVM 18.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"-mllvm", "--rotation-max-header-size=0")
|
"-mllvm", "--rotation-max-header-size=0")
|
||||||
}
|
}
|
||||||
@@ -807,21 +808,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
// Run wasm-opt for wasm binaries
|
// Run wasm-opt for wasm binaries
|
||||||
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
|
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
|
||||||
var opt string
|
optLevel, _, _ := config.OptLevel()
|
||||||
switch config.Options.Opt {
|
opt := "-" + optLevel
|
||||||
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
|
var args []string
|
||||||
|
|
||||||
@@ -1042,8 +1030,8 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
|||||||
// optimizeProgram runs a series of optimizations and transformations that are
|
// optimizeProgram runs a series of optimizations and transformations that are
|
||||||
// needed to convert a program to its final form. Some transformations are not
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
// optional and must be run as the compiler expects them to run.
|
// optional and must be run as the compiler expects them to run.
|
||||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues map[string]map[string]string) error {
|
||||||
err := interp.Run(mod, config.Options.InterpTimeout, config.Options.InterpMaxDepth, config.Options.InterpMaxInstr, config.DumpSSA())
|
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1061,15 +1049,14 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert values from -ldflags="-X ..." into the IR.
|
// Insert values from -ldflags="-X ..." into the IR.
|
||||||
err = setGlobalValues(mod, config.Options.GlobalValues)
|
err = setGlobalValues(mod, globalValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization levels here are roughly the same as Clang, but probably not
|
// Run most of the whole-program optimizations (including the whole
|
||||||
// exactly.
|
// O0/O1/O2/Os/Oz optimization pipeline).
|
||||||
optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
|
errs := transform.Optimize(mod, config)
|
||||||
errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold)
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return newMultiError(errs)
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,6 +35,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
"riscv-qemu",
|
"riscv-qemu",
|
||||||
"wasi",
|
"wasi",
|
||||||
"wasm",
|
"wasm",
|
||||||
|
"wasm-unknown",
|
||||||
}
|
}
|
||||||
if hasBuiltinTools {
|
if hasBuiltinTools {
|
||||||
// hasBuiltinTools is set when TinyGo is statically linked with LLVM,
|
// hasBuiltinTools is set when TinyGo is statically linked with LLVM,
|
||||||
@@ -74,7 +74,6 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
|
|
||||||
func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||||
testDir := t.TempDir()
|
testDir := t.TempDir()
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
|
||||||
|
|
||||||
ctx := llvm.NewContext()
|
ctx := llvm.NewContext()
|
||||||
defer ctx.Dispose()
|
defer ctx.Dispose()
|
||||||
@@ -84,9 +83,8 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
|||||||
t.Fatalf("could not load target: %s", err)
|
t.Fatalf("could not load target: %s", err)
|
||||||
}
|
}
|
||||||
config := compileopts.Config{
|
config := compileopts.Config{
|
||||||
Options: options,
|
Options: options,
|
||||||
Target: target,
|
Target: target,
|
||||||
ClangHeaders: clangHeaderPath,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a very simple C input file.
|
// Create a very simple C input file.
|
||||||
@@ -98,7 +96,7 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
|||||||
|
|
||||||
// Compile this file using Clang.
|
// Compile this file using Clang.
|
||||||
outpath := filepath.Join(testDir, "test.bc")
|
outpath := filepath.Join(testDir, "test.bc")
|
||||||
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags()...)
|
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags(false)...)
|
||||||
if config.GOOS() == "darwin" {
|
if config.GOOS() == "darwin" {
|
||||||
// Silence some warnings that happen when testing GOOS=darwin on
|
// Silence some warnings that happen when testing GOOS=darwin on
|
||||||
// something other than MacOS.
|
// something other than MacOS.
|
||||||
|
|||||||
+34
-10
@@ -77,6 +77,15 @@ func ReadBuildID() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return raw[4:], nil
|
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:
|
default:
|
||||||
// On other platforms (such as Windows) there isn't such a convenient
|
// 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
|
// build ID. Luckily, Go does have an equivalent of the build ID, which
|
||||||
@@ -88,16 +97,31 @@ func ReadBuildID() ([]byte, error) {
|
|||||||
// directly. Luckily the build ID is always at the start of the file.
|
// directly. Luckily the build ID is always at the start of the file.
|
||||||
// For details, see:
|
// For details, see:
|
||||||
// https://github.com/golang/go/blob/master/src/cmd/internal/buildid/buildid.go
|
// https://github.com/golang/go/blob/master/src/cmd/internal/buildid/buildid.go
|
||||||
fileStart := make([]byte, 4096)
|
id, err := readRawGoBuildID(f, 4096)
|
||||||
_, err := io.ReadFull(f, fileStart)
|
if len(id) != 0 || err != nil {
|
||||||
index := bytes.Index(fileStart, []byte("\xff Go build ID: \""))
|
return id, err
|
||||||
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 %s", executable)
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-6
@@ -1,5 +1,12 @@
|
|||||||
//go:build byollvm
|
//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 --------------------------------------===//
|
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
||||||
//
|
//
|
||||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
@@ -21,8 +28,8 @@
|
|||||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||||
#include "clang/Frontend/Utils.h"
|
#include "clang/Frontend/Utils.h"
|
||||||
#include "llvm/ADT/STLExtras.h"
|
#include "llvm/ADT/STLExtras.h"
|
||||||
|
#include "llvm/ADT/StringExtras.h"
|
||||||
#include "llvm/ADT/StringSwitch.h"
|
#include "llvm/ADT/StringSwitch.h"
|
||||||
#include "llvm/ADT/Triple.h"
|
|
||||||
#include "llvm/IR/DataLayout.h"
|
#include "llvm/IR/DataLayout.h"
|
||||||
#include "llvm/MC/MCAsmBackend.h"
|
#include "llvm/MC/MCAsmBackend.h"
|
||||||
#include "llvm/MC/MCAsmInfo.h"
|
#include "llvm/MC/MCAsmInfo.h"
|
||||||
@@ -46,7 +53,6 @@
|
|||||||
#include "llvm/Support/ErrorHandling.h"
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/FormattedStream.h"
|
#include "llvm/Support/FormattedStream.h"
|
||||||
#include "llvm/Support/Host.h"
|
|
||||||
#include "llvm/Support/MemoryBuffer.h"
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/Process.h"
|
#include "llvm/Support/Process.h"
|
||||||
@@ -55,6 +61,8 @@
|
|||||||
#include "llvm/Support/TargetSelect.h"
|
#include "llvm/Support/TargetSelect.h"
|
||||||
#include "llvm/Support/Timer.h"
|
#include "llvm/Support/Timer.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include "llvm/TargetParser/Host.h"
|
||||||
|
#include "llvm/TargetParser/Triple.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
@@ -151,8 +159,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
|
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
|
||||||
auto Split = StringRef(Arg).split('=');
|
auto Split = StringRef(Arg).split('=');
|
||||||
Opts.DebugPrefixMap.insert(
|
Opts.DebugPrefixMap.emplace_back(Split.first, Split.second);
|
||||||
{std::string(Split.first), std::string(Split.second)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frontend Options
|
// Frontend Options
|
||||||
@@ -225,6 +232,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
.Case("default", EmitDwarfUnwindType::Default);
|
.Case("default", EmitDwarfUnwindType::Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opts.EmitCompactUnwindNonCanonical =
|
||||||
|
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
|
||||||
|
|
||||||
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
@@ -260,8 +270,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
MemoryBuffer::getFileOrSTDIN(Opts.InputFile, /*IsText=*/true);
|
||||||
|
|
||||||
if (std::error_code EC = Buffer.getError()) {
|
if (std::error_code EC = Buffer.getError()) {
|
||||||
Error = EC.message();
|
return Diags.Report(diag::err_fe_error_reading)
|
||||||
return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
|
<< Opts.InputFile << EC.message();
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceMgr SrcMgr;
|
SourceMgr SrcMgr;
|
||||||
@@ -278,6 +288,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
MCTargetOptions MCOptions;
|
MCTargetOptions MCOptions;
|
||||||
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
||||||
|
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
|
||||||
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
||||||
|
|
||||||
std::unique_ptr<MCAsmInfo> MAI(
|
std::unique_ptr<MCAsmInfo> MAI(
|
||||||
|
|||||||
+9
-1
@@ -1,3 +1,6 @@
|
|||||||
|
// Source: https://github.com/llvm/llvm-project/blob/main/clang/tools/driver/cc1as_main.cpp
|
||||||
|
// See cc1as.cpp for details.
|
||||||
|
|
||||||
//===-- cc1as.h - Clang Assembler ----------------------------------------===//
|
//===-- cc1as.h - Clang Assembler ----------------------------------------===//
|
||||||
//
|
//
|
||||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
@@ -44,7 +47,7 @@ struct AssemblerInvocation {
|
|||||||
std::string DwarfDebugFlags;
|
std::string DwarfDebugFlags;
|
||||||
std::string DwarfDebugProducer;
|
std::string DwarfDebugProducer;
|
||||||
std::string DebugCompilationDir;
|
std::string DebugCompilationDir;
|
||||||
std::map<const std::string, const std::string> DebugPrefixMap;
|
llvm::SmallVector<std::pair<std::string, std::string>, 0> DebugPrefixMap;
|
||||||
llvm::DebugCompressionType CompressDebugSections =
|
llvm::DebugCompressionType CompressDebugSections =
|
||||||
llvm::DebugCompressionType::None;
|
llvm::DebugCompressionType::None;
|
||||||
std::string MainFileName;
|
std::string MainFileName;
|
||||||
@@ -89,6 +92,10 @@ struct AssemblerInvocation {
|
|||||||
/// Whether to emit DWARF unwind info.
|
/// Whether to emit DWARF unwind info.
|
||||||
EmitDwarfUnwindType EmitDwarfUnwind;
|
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||||
|
|
||||||
|
// Whether to emit compact-unwind for non-canonical entries.
|
||||||
|
// Note: maybe overriden by other constraints.
|
||||||
|
unsigned EmitCompactUnwindNonCanonical : 1;
|
||||||
|
|
||||||
/// The name of the relocation model to use.
|
/// The name of the relocation model to use.
|
||||||
std::string RelocationModel;
|
std::string RelocationModel;
|
||||||
|
|
||||||
@@ -128,6 +135,7 @@ public:
|
|||||||
DwarfVersion = 0;
|
DwarfVersion = 0;
|
||||||
EmbedBitcode = 0;
|
EmbedBitcode = 0;
|
||||||
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
||||||
|
EmitCompactUnwindNonCanonical = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
#include <clang/FrontendTool/Utils.h>
|
#include <clang/FrontendTool/Utils.h>
|
||||||
#include <llvm/ADT/IntrusiveRefCntPtr.h>
|
#include <llvm/ADT/IntrusiveRefCntPtr.h>
|
||||||
#include <llvm/Option/Option.h>
|
#include <llvm/Option/Option.h>
|
||||||
#include <llvm/Support/Host.h>
|
#include <llvm/TargetParser/Host.h>
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|||||||
+2
-5
@@ -27,19 +27,16 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if major != 1 || minor < 18 || minor > 21 {
|
if major != 1 || minor < 18 || minor > 22 {
|
||||||
// Note: when this gets updated, also update the Go compatibility matrix:
|
// 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
|
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
|
||||||
return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor)
|
return nil, fmt.Errorf("requires go version 1.18 through 1.22, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
|
||||||
|
|
||||||
return &compileopts.Config{
|
return &compileopts.Config{
|
||||||
Options: options,
|
Options: options,
|
||||||
Target: spec,
|
Target: spec,
|
||||||
GoMinorVersion: minor,
|
GoMinorVersion: minor,
|
||||||
ClangHeaders: clangHeaderPath,
|
|
||||||
TestConfig: options.TestConfig,
|
TestConfig: options.TestConfig,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
-105
@@ -1,105 +0,0 @@
|
|||||||
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 ""
|
|
||||||
}
|
|
||||||
+4
-6
@@ -143,6 +143,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
// reproducible. Otherwise the temporary directory is stored in the archive
|
// reproducible. Otherwise the temporary directory is stored in the archive
|
||||||
// itself, which varies each run.
|
// itself, which varies each run.
|
||||||
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
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()
|
cpu := config.CPU()
|
||||||
if cpu != "" {
|
if cpu != "" {
|
||||||
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
||||||
@@ -178,12 +182,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
if strings.HasPrefix(target, "riscv64-") {
|
if strings.HasPrefix(target, "riscv64-") {
|
||||||
args = append(args, "-march=rv64gc")
|
args = append(args, "-march=rv64gc")
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(target, "xtensa") {
|
|
||||||
// Hack to work around an issue in the Xtensa port:
|
|
||||||
// https://github.com/espressif/llvm-project/issues/52
|
|
||||||
// Hopefully this will be fixed soon (LLVM 14).
|
|
||||||
args = append(args, "-D__ELF__")
|
|
||||||
}
|
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
|
||||||
|
|||||||
+9
-20
@@ -5,7 +5,11 @@
|
|||||||
#include <lld/Common/Driver.h>
|
#include <lld/Common/Driver.h>
|
||||||
#include <llvm/Support/Parallel.h>
|
#include <llvm/Support/Parallel.h>
|
||||||
|
|
||||||
extern "C" {
|
LLD_HAS_DRIVER(coff)
|
||||||
|
LLD_HAS_DRIVER(elf)
|
||||||
|
LLD_HAS_DRIVER(mingw)
|
||||||
|
LLD_HAS_DRIVER(macho)
|
||||||
|
LLD_HAS_DRIVER(wasm)
|
||||||
|
|
||||||
static void configure() {
|
static void configure() {
|
||||||
#if _WIN64
|
#if _WIN64
|
||||||
@@ -16,28 +20,13 @@ static void configure() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tinygo_link_elf(int argc, char **argv) {
|
extern "C" {
|
||||||
configure();
|
|
||||||
std::vector<const char*> args(argv, argv + argc);
|
|
||||||
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool tinygo_link_macho(int argc, char **argv) {
|
bool tinygo_link(int argc, char **argv) {
|
||||||
configure();
|
configure();
|
||||||
std::vector<const char*> args(argv, argv + argc);
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false);
|
lld::Result r = lld::lldMain(args, llvm::outs(), llvm::errs(), LLD_ALL_DRIVERS);
|
||||||
}
|
return !r.retCode;
|
||||||
|
|
||||||
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"
|
} // external "C"
|
||||||
|
|||||||
+2
-7
@@ -6,12 +6,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"tinygo.org/x/go-llvm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Musl = Library{
|
var Musl = Library{
|
||||||
@@ -93,6 +91,7 @@ var Musl = Library{
|
|||||||
"-Wno-string-plus-int",
|
"-Wno-string-plus-int",
|
||||||
"-Wno-ignored-pragmas",
|
"-Wno-ignored-pragmas",
|
||||||
"-Wno-tautological-constant-out-of-range-compare",
|
"-Wno-tautological-constant-out-of-range-compare",
|
||||||
|
"-Wno-deprecated-non-prototype",
|
||||||
"-Qunused-arguments",
|
"-Qunused-arguments",
|
||||||
// Select include dirs. Don't include standard library includes
|
// Select include dirs. Don't include standard library includes
|
||||||
// (that would introduce host dependencies and other complications),
|
// (that would introduce host dependencies and other complications),
|
||||||
@@ -106,11 +105,6 @@ var Musl = Library{
|
|||||||
"-I" + muslDir + "/include",
|
"-I" + muslDir + "/include",
|
||||||
"-fno-stack-protector",
|
"-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
|
return cflags
|
||||||
},
|
},
|
||||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
||||||
@@ -125,6 +119,7 @@ var Musl = Library{
|
|||||||
"internal/syscall_ret.c",
|
"internal/syscall_ret.c",
|
||||||
"internal/vdso.c",
|
"internal/vdso.c",
|
||||||
"legacy/*.c",
|
"legacy/*.c",
|
||||||
|
"linux/*.c",
|
||||||
"malloc/*.c",
|
"malloc/*.c",
|
||||||
"malloc/mallocng/*.c",
|
"malloc/mallocng/*.c",
|
||||||
"mman/*.c",
|
"mman/*.c",
|
||||||
|
|||||||
+99
-79
@@ -3,6 +3,7 @@ package builder
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
@@ -41,91 +42,23 @@ var Picolibc = Library{
|
|||||||
},
|
},
|
||||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string) ([]string, error) {
|
||||||
return picolibcSources, nil
|
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
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var picolibcSources = []string{
|
var picolibcSources = []string{
|
||||||
"../../picolibc-stdio.c",
|
"../../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/bcmp.c",
|
||||||
"libc/string/bcopy.c",
|
"libc/string/bcopy.c",
|
||||||
"libc/string/bzero.c",
|
"libc/string/bzero.c",
|
||||||
@@ -229,6 +162,87 @@ var picolibcSources = []string{
|
|||||||
"libc/string/wmempcpy.c",
|
"libc/string/wmempcpy.c",
|
||||||
"libc/string/wmemset.c",
|
"libc/string/wmemset.c",
|
||||||
"libc/string/xpg_strerror_r.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_finite.c",
|
||||||
"libm/common/sf_copysign.c",
|
"libm/common/sf_copysign.c",
|
||||||
@@ -323,6 +337,12 @@ var picolibcSources = []string{
|
|||||||
"libm/common/math_err_may_uflow.c",
|
"libm/common/math_err_may_uflow.c",
|
||||||
"libm/common/math_err_check_uflow.c",
|
"libm/common/math_err_check_uflow.c",
|
||||||
"libm/common/math_err_check_oflow.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_inexact.c",
|
||||||
"libm/common/math_inexactf.c",
|
"libm/common/math_inexactf.c",
|
||||||
"libm/common/log.c",
|
"libm/common/log.c",
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ func TestBinarySize(t *testing.T) {
|
|||||||
// This is a small number of very diverse targets that we want to test.
|
// This is a small number of very diverse targets that we want to test.
|
||||||
tests := []sizeTest{
|
tests := []sizeTest{
|
||||||
// microcontrollers
|
// microcontrollers
|
||||||
{"hifive1b", "examples/echo", 4568, 280, 0, 2252},
|
{"hifive1b", "examples/echo", 4476, 280, 0, 2252},
|
||||||
{"microbit", "examples/serial", 2728, 388, 8, 2256},
|
{"microbit", "examples/serial", 2724, 388, 8, 2256},
|
||||||
{"wioterminal", "examples/pininterrupt", 5996, 1484, 116, 6816},
|
{"wioterminal", "examples/pininterrupt", 5996, 1484, 116, 6816},
|
||||||
|
|
||||||
// TODO: also check wasm. Right now this is difficult, because
|
// TODO: also check wasm. Right now this is difficult, because
|
||||||
|
|||||||
@@ -12,10 +12,7 @@ import (
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
bool tinygo_clang_driver(int argc, char **argv);
|
bool tinygo_clang_driver(int argc, char **argv);
|
||||||
bool tinygo_link_elf(int argc, char **argv);
|
bool tinygo_link(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"
|
import "C"
|
||||||
|
|
||||||
@@ -26,16 +23,7 @@ const hasBuiltinTools = true
|
|||||||
// This version actually runs the tools because TinyGo was compiled while
|
// This version actually runs the tools because TinyGo was compiled while
|
||||||
// linking statically with LLVM (with the byollvm build tag).
|
// linking statically with LLVM (with the byollvm build tag).
|
||||||
func RunTool(tool string, args ...string) error {
|
func RunTool(tool string, args ...string) error {
|
||||||
linker := "elf"
|
args = append([]string{tool}, args...)
|
||||||
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
|
var cflag *C.char
|
||||||
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag)))
|
||||||
@@ -51,19 +39,8 @@ func RunTool(tool string, args ...string) error {
|
|||||||
switch tool {
|
switch tool {
|
||||||
case "clang":
|
case "clang":
|
||||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||||
case "ld.lld":
|
case "ld.lld", "wasm-ld":
|
||||||
switch linker {
|
ok = C.tinygo_link(C.int(len(args)), (**C.char)(buf))
|
||||||
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:
|
default:
|
||||||
return errors.New("unknown tool: " + tool)
|
return errors.New("unknown tool: " + tool)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
@@ -12,11 +11,6 @@ import (
|
|||||||
func runCCompiler(flags ...string) error {
|
func runCCompiler(flags ...string) error {
|
||||||
if hasBuiltinTools {
|
if hasBuiltinTools {
|
||||||
// Compile this with the internal Clang compiler.
|
// 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 := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|||||||
+14
-7
@@ -25,9 +25,15 @@ import (
|
|||||||
"golang.org/x/tools/go/ast/astutil"
|
"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.
|
// cgoPackage holds all CGo-related information of a package.
|
||||||
type cgoPackage struct {
|
type cgoPackage struct {
|
||||||
generated *ast.File
|
generated *ast.File
|
||||||
|
packageName string
|
||||||
|
cgoFiles []*ast.File
|
||||||
generatedPos token.Pos
|
generatedPos token.Pos
|
||||||
errors []error
|
errors []error
|
||||||
currentDir string // current working directory
|
currentDir string // current working directory
|
||||||
@@ -88,6 +94,7 @@ var cgoAliases = map[string]string{
|
|||||||
"C.uintptr_t": "uintptr",
|
"C.uintptr_t": "uintptr",
|
||||||
"C.float": "float32",
|
"C.float": "float32",
|
||||||
"C.double": "float64",
|
"C.double": "float64",
|
||||||
|
"C._Bool": "bool",
|
||||||
}
|
}
|
||||||
|
|
||||||
// builtinAliases are handled specially because they only exist on the Go side
|
// builtinAliases are handled specially because they only exist on the Go side
|
||||||
@@ -164,8 +171,9 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
|||||||
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
||||||
// hashes of the accessed C header files. If there is one or more error, it
|
// hashes of the accessed C header files. If there is one or more error, it
|
||||||
// returns these in the []error slice but still modifies the AST.
|
// returns these in the []error slice but still modifies the AST.
|
||||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||||
p := &cgoPackage{
|
p := &cgoPackage{
|
||||||
|
packageName: files[0].Name.Name,
|
||||||
currentDir: dir,
|
currentDir: dir,
|
||||||
importPath: importPath,
|
importPath: importPath,
|
||||||
fset: fset,
|
fset: fset,
|
||||||
@@ -201,6 +209,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// This is always a bug in the cgo package.
|
// This is always a bug in the cgo package.
|
||||||
panic("unexpected error: " + err.Error())
|
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
|
// If the Comments field is not set to nil, the go/format package will get
|
||||||
// confused about where comments should go.
|
// confused about where comments should go.
|
||||||
p.generated.Comments = nil
|
p.generated.Comments = nil
|
||||||
@@ -291,9 +300,6 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// have better alternatives anyway.
|
// have better alternatives anyway.
|
||||||
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
|
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
|
||||||
cflagsForCGo = append(cflagsForCGo, p.cflags...)
|
cflagsForCGo = append(cflagsForCGo, p.cflags...)
|
||||||
if clangHeaders != "" {
|
|
||||||
cflagsForCGo = append(cflagsForCGo, "-isystem", clangHeaders)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve types such as C.int, C.longlong, etc from C.
|
// Retrieve types such as C.int, C.longlong, etc from C.
|
||||||
p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
|
p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
|
||||||
@@ -311,10 +317,11 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Process CGo imports for each file.
|
// Process CGo imports for each file.
|
||||||
for i, f := range files {
|
for i, f := range files {
|
||||||
cf := p.newCGoFile(f, i)
|
cf := p.newCGoFile(f, i)
|
||||||
// Float and double are aliased, meaning that C.float is the same thing
|
// These types are aliased with the corresponding types in C. For
|
||||||
// as float32 in Go.
|
// example, float in C is always float32 in Go.
|
||||||
cf.names["float"] = clangCursor{}
|
cf.names["float"] = clangCursor{}
|
||||||
cf.names["double"] = clangCursor{}
|
cf.names["double"] = clangCursor{}
|
||||||
|
cf.names["_Bool"] = clangCursor{}
|
||||||
// Now read all the names (identifies) that C defines in the header
|
// Now read all the names (identifies) that C defines in the header
|
||||||
// snippet.
|
// snippet.
|
||||||
cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
|
cf.readNames(p.cgoHeaders[i], cflagsForCGo, filepath.Base(fset.File(f.Pos()).Name()), func(names map[string]clangCursor) {
|
||||||
@@ -333,7 +340,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Print the newly generated in-memory AST, for debugging.
|
// Print the newly generated in-memory AST, for debugging.
|
||||||
//ast.Print(fset, p.generated)
|
//ast.Print(fset, p.generated)
|
||||||
|
|
||||||
return p.generated, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
return p.cgoFiles, p.cgoHeaders, p.cflags, p.ldflags, p.visitedFiles, p.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cgoPackage) newCGoFile(file *ast.File, index int) *cgoFile {
|
func (p *cgoPackage) newCGoFile(file *ast.File, index int) *cgoFile {
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
//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
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -55,7 +55,7 @@ func TestCGo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process the AST with CGo.
|
// Process the AST with CGo.
|
||||||
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "")
|
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
|
||||||
|
|
||||||
// Check the AST for type errors.
|
// Check the AST for type errors.
|
||||||
var typecheckErrors []error
|
var typecheckErrors []error
|
||||||
@@ -66,7 +66,7 @@ func TestCGo(t *testing.T) {
|
|||||||
Importer: simpleImporter{},
|
Importer: simpleImporter{},
|
||||||
Sizes: types.SizesFor("gccgo", "arm"),
|
Sizes: types.SizesFor("gccgo", "arm"),
|
||||||
}
|
}
|
||||||
_, err = config.Check("", fset, []*ast.File{f, cgoAST}, nil)
|
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
|
||||||
if err != nil && len(typecheckErrors) == 0 {
|
if err != nil && len(typecheckErrors) == 0 {
|
||||||
// Only report errors when no type errors are found (an
|
// Only report errors when no type errors are found (an
|
||||||
// unexpected condition).
|
// unexpected condition).
|
||||||
@@ -91,7 +91,7 @@ func TestCGo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
buf.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
}
|
}
|
||||||
err = format.Node(buf, fset, cgoAST)
|
err = format.Node(buf, fset, cgoFiles[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not write out CGo AST: %v", err)
|
t.Errorf("could not write out CGo AST: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -589,6 +589,13 @@ func (p *cgoPackage) getClangLocationPosition(location C.CXSourceLocation, tu C.
|
|||||||
f := p.fset.AddFile(filename, -1, int(size))
|
f := p.fset.AddFile(filename, -1, int(size))
|
||||||
f.SetLines(lines)
|
f.SetLines(lines)
|
||||||
p.tokenFiles[filename] = f
|
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]
|
positionFile := p.tokenFiles[filename]
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
//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"
|
|
||||||
@@ -8,8 +8,8 @@ package cgo
|
|||||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@15/include
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@15/include
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm15/include
|
#cgo freebsd CFLAGS: -I/usr/local/llvm15/include
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lclang
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lclang
|
||||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang -lffi
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang
|
||||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang -lffi
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang
|
||||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
//go:build !byollvm && !llvm14 && !llvm15
|
//go:build !byollvm && llvm16
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
// As of 2023-05-05, there is a packaging issue on Debian:
|
// As of 2023-05-05, there is a packaging issue with LLVM 16 on Debian:
|
||||||
// https://github.com/llvm/llvm-project/issues/62199
|
// https://github.com/llvm/llvm-project/issues/62199
|
||||||
// A workaround is to fix this locally, using something like this:
|
// 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 darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@16/include
|
||||||
#cgo freebsd CFLAGS: -I/usr/local/llvm16/include
|
#cgo freebsd CFLAGS: -I/usr/local/llvm16/include
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-16/lib -lclang
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-16/lib -lclang
|
||||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang -lffi
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang
|
||||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang -lffi
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang
|
||||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build !byollvm && !llvm15 && !llvm16
|
||||||
|
|
||||||
|
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"
|
||||||
+38
-19
@@ -19,7 +19,6 @@ type Config struct {
|
|||||||
Options *Options
|
Options *Options
|
||||||
Target *TargetSpec
|
Target *TargetSpec
|
||||||
GoMinorVersion int
|
GoMinorVersion int
|
||||||
ClangHeaders string // Clang built-in header include path
|
|
||||||
TestConfig TestConfig
|
TestConfig TestConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +74,12 @@ func (c *Config) GOARM() string {
|
|||||||
|
|
||||||
// BuildTags returns the complete list of build tags used during this build.
|
// BuildTags returns the complete list of build tags used during this build.
|
||||||
func (c *Config) BuildTags() []string {
|
func (c *Config) BuildTags() []string {
|
||||||
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
tags := append(c.Target.BuildTags, []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
|
||||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||||
}
|
}
|
||||||
@@ -83,12 +87,6 @@ func (c *Config) BuildTags() []string {
|
|||||||
return tags
|
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
|
// GC returns the garbage collection strategy in use on this platform. Valid
|
||||||
// values are "none", "leaking", "conservative" and "precise".
|
// values are "none", "leaking", "conservative" and "precise".
|
||||||
func (c *Config) GC() string {
|
func (c *Config) GC() string {
|
||||||
@@ -145,18 +143,18 @@ func (c *Config) Serial() string {
|
|||||||
|
|
||||||
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
|
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
|
||||||
// threshold as used in the LLVM optimization pipeline.
|
// threshold as used in the LLVM optimization pipeline.
|
||||||
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
func (c *Config) OptLevel() (level string, speedLevel, sizeLevel int) {
|
||||||
switch c.Options.Opt {
|
switch c.Options.Opt {
|
||||||
case "none", "0":
|
case "none", "0":
|
||||||
return 0, 0, 0 // -O0
|
return "O0", 0, 0
|
||||||
case "1":
|
case "1":
|
||||||
return 1, 0, 0 // -O1
|
return "O1", 1, 0
|
||||||
case "2":
|
case "2":
|
||||||
return 2, 0, 225 // -O2
|
return "O2", 2, 0
|
||||||
case "s":
|
case "s":
|
||||||
return 2, 1, 225 // -Os
|
return "Os", 2, 1
|
||||||
case "z":
|
case "z":
|
||||||
return 2, 2, 5 // -Oz, default
|
return "Oz", 2, 2 // default
|
||||||
default:
|
default:
|
||||||
// This is not shown to the user: valid choices are already checked as
|
// This is not shown to the user: valid choices are already checked as
|
||||||
// part of Options.Verify(). It is here as a sanity check.
|
// part of Options.Verify(). It is here as a sanity check.
|
||||||
@@ -190,6 +188,15 @@ func (c *Config) StackSize() uint64 {
|
|||||||
return c.Target.DefaultStackSize
|
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
|
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
|
||||||
// calculates and patches in the checksum for the 2nd stage bootloader.
|
// calculates and patches in the checksum for the 2nd stage bootloader.
|
||||||
func (c *Config) RP2040BootPatch() bool {
|
func (c *Config) RP2040BootPatch() bool {
|
||||||
@@ -259,24 +266,35 @@ func (c *Config) DefaultBinaryExtension() string {
|
|||||||
|
|
||||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||||
// preprocessing.
|
// preprocessing.
|
||||||
func (c *Config) CFlags() []string {
|
func (c *Config) CFlags(libclang bool) []string {
|
||||||
var cflags []string
|
var cflags []string
|
||||||
for _, flag := range c.Target.CFlags {
|
for _, flag := range c.Target.CFlags {
|
||||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||||
}
|
}
|
||||||
|
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 {
|
switch c.Target.Libc {
|
||||||
case "darwin-libSystem":
|
case "darwin-libSystem":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
cflags = append(cflags,
|
cflags = append(cflags,
|
||||||
"--sysroot="+filepath.Join(root, "lib/macos-minimal-sdk/src"),
|
"-nostdlibinc",
|
||||||
|
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
|
||||||
)
|
)
|
||||||
case "picolibc":
|
case "picolibc":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||||
path, _ := c.LibcPath("picolibc")
|
path, _ := c.LibcPath("picolibc")
|
||||||
cflags = append(cflags,
|
cflags = append(cflags,
|
||||||
"--sysroot="+path,
|
"-nostdlibinc",
|
||||||
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
|
"-isystem", filepath.Join(path, "include"),
|
||||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||||
)
|
)
|
||||||
@@ -297,7 +315,8 @@ func (c *Config) CFlags() []string {
|
|||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
path, _ := c.LibcPath("mingw-w64")
|
path, _ := c.LibcPath("mingw-w64")
|
||||||
cflags = append(cflags,
|
cflags = append(cflags,
|
||||||
"--sysroot="+path,
|
"-nostdlibinc",
|
||||||
|
"-isystem", filepath.Join(path, "include"),
|
||||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||||
"-D_UCRT",
|
"-D_UCRT",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||||
validSerialOptions = []string{"none", "uart", "usb"}
|
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||||
validPanicStrategyOptions = []string{"print", "trap"}
|
validPanicStrategyOptions = []string{"print", "trap"}
|
||||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||||
@@ -23,6 +23,7 @@ type Options struct {
|
|||||||
GOOS string // environment variable
|
GOOS string // environment variable
|
||||||
GOARCH string // environment variable
|
GOARCH string // environment variable
|
||||||
GOARM string // environment variable (only used with GOARCH=arm)
|
GOARM string // environment variable (only used with GOARCH=arm)
|
||||||
|
Directory string // working dir, leave it unset to use the current working dir
|
||||||
Target string
|
Target string
|
||||||
Opt string
|
Opt string
|
||||||
GC string
|
GC string
|
||||||
@@ -32,8 +33,6 @@ type Options struct {
|
|||||||
Serial string
|
Serial string
|
||||||
Work bool // -work flag to print temporary build directory
|
Work bool // -work flag to print temporary build directory
|
||||||
InterpTimeout time.Duration
|
InterpTimeout time.Duration
|
||||||
InterpMaxDepth int
|
|
||||||
InterpMaxInstr int
|
|
||||||
PrintIR bool
|
PrintIR bool
|
||||||
DumpSSA bool
|
DumpSSA bool
|
||||||
VerifyIR bool
|
VerifyIR bool
|
||||||
@@ -50,7 +49,6 @@ type Options struct {
|
|||||||
Programmer string
|
Programmer string
|
||||||
OpenOCDCommands []string
|
OpenOCDCommands []string
|
||||||
LLVMFeatures string
|
LLVMFeatures string
|
||||||
Directory string
|
|
||||||
PrintJSON bool
|
PrintJSON bool
|
||||||
Monitor bool
|
Monitor bool
|
||||||
BaudRate int
|
BaudRate int
|
||||||
|
|||||||
+58
-5
@@ -246,6 +246,43 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
return spec, nil
|
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) {
|
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||||
// No target spec available. Use the default one, useful on most systems
|
// No target spec available. Use the default one, useful on most systems
|
||||||
// with a regular OS.
|
// with a regular OS.
|
||||||
@@ -264,10 +301,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
switch goarch {
|
switch goarch {
|
||||||
case "386":
|
case "386":
|
||||||
spec.CPU = "pentium4"
|
spec.CPU = "pentium4"
|
||||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||||
case "amd64":
|
case "amd64":
|
||||||
spec.CPU = "x86-64"
|
spec.CPU = "x86-64"
|
||||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
spec.Features = "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||||
case "arm":
|
case "arm":
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||||
@@ -283,8 +320,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
if goos == "darwin" {
|
if goos == "darwin" {
|
||||||
spec.Features = "+neon"
|
spec.Features = "+neon"
|
||||||
} else { // windows, linux
|
} else if goos == "windows" {
|
||||||
spec.Features = "+neon,-fmv"
|
spec.Features = "+neon,-fmv"
|
||||||
|
} else { // linux
|
||||||
|
spec.Features = "+neon,-fmv,-outline-atomics"
|
||||||
}
|
}
|
||||||
case "wasm":
|
case "wasm":
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
@@ -312,6 +351,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.RTLib = "compiler-rt"
|
spec.RTLib = "compiler-rt"
|
||||||
spec.Libc = "musl"
|
spec.Libc = "musl"
|
||||||
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
||||||
|
if 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" {
|
} else if goos == "windows" {
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
spec.Libc = "mingw-w64"
|
spec.Libc = "mingw-w64"
|
||||||
@@ -344,12 +397,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.Linker = "wasm-ld"
|
spec.Linker = "wasm-ld"
|
||||||
spec.RTLib = "compiler-rt"
|
spec.RTLib = "compiler-rt"
|
||||||
spec.Libc = "wasi-libc"
|
spec.Libc = "wasi-libc"
|
||||||
spec.DefaultStackSize = 1024 * 32 // 32kB
|
spec.DefaultStackSize = 1024 * 64 // 64kB
|
||||||
spec.LDFlags = append(spec.LDFlags,
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
"--stack-first",
|
"--stack-first",
|
||||||
"--no-demangle",
|
"--no-demangle",
|
||||||
)
|
)
|
||||||
spec.Emulator = "wasmtime --mapdir=/tmp::{tmpDir} {}"
|
spec.Emulator = "wasmtime --dir={tmpDir}::/tmp {}"
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||||
"src/runtime/asm_tinygowasm.S",
|
"src/runtime/asm_tinygowasm.S",
|
||||||
"src/internal/task/task_asyncify_wasm.S",
|
"src/internal/task/task_asyncify_wasm.S",
|
||||||
|
|||||||
@@ -35,17 +35,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
|
|||||||
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
|
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
|
||||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||||
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||||
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)
|
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||||
if isPointer {
|
|
||||||
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
|
|
||||||
}
|
|
||||||
return oldVal
|
return oldVal
|
||||||
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
|
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
|
||||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||||
@@ -63,24 +53,6 @@ func (b *builder) createAtomicOp(name string) llvm.Value {
|
|||||||
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
|
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
|
||||||
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
ptr := b.getValue(b.fn.Params[0], getPos(b.fn))
|
||||||
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
val := b.getValue(b.fn.Params[1], getPos(b.fn))
|
||||||
if strings.HasPrefix(b.Triple, "avr") {
|
|
||||||
// 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 := b.CreateStore(val, ptr)
|
||||||
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
|
||||||
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name
|
|||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||||
}
|
}
|
||||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
args = append(args, llvm.Undef(b.dataPtrType)) // unused context parameter
|
||||||
if isInvoke {
|
if isInvoke {
|
||||||
return b.createInvoke(fnType, llvmFn, args, name)
|
return b.createInvoke(fnType, llvmFn, args, name)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-26
@@ -33,28 +33,27 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
// store value-to-send
|
// store value-to-send
|
||||||
valueType := b.getLLVMType(instr.X.Type())
|
valueType := b.getLLVMType(instr.X.Type())
|
||||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||||
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
var valueAlloca, valueAllocaSize llvm.Value
|
||||||
if isZeroSize {
|
if isZeroSize {
|
||||||
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
||||||
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
|
||||||
} else {
|
} else {
|
||||||
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
b.CreateStore(chanValue, valueAlloca)
|
b.CreateStore(chanValue, valueAlloca)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate blockedlist buffer.
|
||||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||||
|
|
||||||
// Do the send.
|
// Do the send.
|
||||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
|
||||||
|
|
||||||
// End the lifetime of the allocas.
|
// End the lifetime of the allocas.
|
||||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
|
||||||
if !isZeroSize {
|
if !isZeroSize {
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,28 +65,27 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
|
|
||||||
// Allocate memory to receive into.
|
// Allocate memory to receive into.
|
||||||
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||||
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
var valueAlloca, valueAllocaSize llvm.Value
|
||||||
if isZeroSize {
|
if isZeroSize {
|
||||||
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
valueAlloca = llvm.ConstNull(b.dataPtrType)
|
||||||
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
|
||||||
} else {
|
} else {
|
||||||
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate blockedlist buffer.
|
||||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
||||||
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
|
||||||
var received llvm.Value
|
var received llvm.Value
|
||||||
if isZeroSize {
|
if isZeroSize {
|
||||||
received = llvm.ConstNull(valueType)
|
received = llvm.ConstNull(valueType)
|
||||||
} else {
|
} else {
|
||||||
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||||
}
|
}
|
||||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
|
||||||
|
|
||||||
if unop.CommaOk {
|
if unop.CommaOk {
|
||||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
||||||
@@ -159,8 +157,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
sendValue := b.getValue(state.Send, state.Pos)
|
sendValue := b.getValue(state.Send, state.Pos)
|
||||||
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
|
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
|
||||||
b.CreateStore(sendValue, alloca)
|
b.CreateStore(sendValue, alloca)
|
||||||
ptr := b.CreateBitCast(alloca, b.i8ptrType, "")
|
selectState = b.CreateInsertValue(selectState, alloca, 1, "")
|
||||||
selectState = b.CreateInsertValue(selectState, ptr, 1, "")
|
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
@@ -168,10 +165,10 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a receive buffer, where the received value will be stored.
|
// Create a receive buffer, where the received value will be stored.
|
||||||
recvbuf := llvm.Undef(b.i8ptrType)
|
recvbuf := llvm.Undef(b.dataPtrType)
|
||||||
if recvbufSize != 0 {
|
if recvbufSize != 0 {
|
||||||
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
||||||
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
recvbufAlloca, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
||||||
recvbufAlloca.SetAlignment(recvbufAlign)
|
recvbufAlloca.SetAlignment(recvbufAlign)
|
||||||
recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{
|
recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
@@ -181,7 +178,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
|
|
||||||
// Create the states slice (allocated on the stack).
|
// Create the states slice (allocated on the stack).
|
||||||
statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates))
|
statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates))
|
||||||
statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
|
statesAlloca, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca")
|
||||||
for i, state := range selectStates {
|
for i, state := range selectStates {
|
||||||
// Set each slice element to the appropriate channel.
|
// Set each slice element to the appropriate channel.
|
||||||
gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
|
gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{
|
||||||
@@ -202,7 +199,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
// Stack-allocate operation structures.
|
// Stack-allocate operation structures.
|
||||||
// If these were simply created as a slice, they would heap-allocate.
|
// If these were simply created as a slice, they would heap-allocate.
|
||||||
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
|
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
|
||||||
chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
||||||
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
||||||
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
|
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
@@ -216,7 +213,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
}, "select.result")
|
}, "select.result")
|
||||||
|
|
||||||
// Terminate the lifetime of the operation structures.
|
// Terminate the lifetime of the operation structures.
|
||||||
b.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize)
|
b.emitLifetimeEnd(chBlockAlloca, chBlockSize)
|
||||||
} else {
|
} else {
|
||||||
results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
|
results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
|
||||||
recvbuf,
|
recvbuf,
|
||||||
@@ -225,7 +222,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Terminate the lifetime of the states alloca.
|
// Terminate the lifetime of the states alloca.
|
||||||
b.emitLifetimeEnd(statesI8, statesSize)
|
b.emitLifetimeEnd(statesAlloca, statesSize)
|
||||||
|
|
||||||
// The result value does not include all the possible received values,
|
// The result value does not include all the possible received values,
|
||||||
// because we can't load them in advance. Instead, the *ssa.Extract
|
// because we can't load them in advance. Instead, the *ssa.Extract
|
||||||
@@ -265,7 +262,6 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
|
|||||||
// it to the correct type, and dereference it.
|
// it to the correct type, and dereference it.
|
||||||
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
|
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
|
||||||
typ := b.getLLVMType(expr.Type())
|
typ := b.getLLVMType(expr.Type())
|
||||||
ptr := b.CreateBitCast(recvbuf, llvm.PointerType(typ, 0), "")
|
return b.CreateLoad(typ, recvbuf, "")
|
||||||
return b.CreateLoad(typ, ptr, "")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-60
@@ -53,6 +53,7 @@ type Config struct {
|
|||||||
Scheduler string
|
Scheduler string
|
||||||
AutomaticStackSize bool
|
AutomaticStackSize bool
|
||||||
DefaultStackSize uint64
|
DefaultStackSize uint64
|
||||||
|
MaxStackAlloc uint64
|
||||||
NeedsStackObjects bool
|
NeedsStackObjects bool
|
||||||
Debug bool // Whether to emit debug information in the LLVM module.
|
Debug bool // Whether to emit debug information in the LLVM module.
|
||||||
}
|
}
|
||||||
@@ -75,10 +76,9 @@ type compilerContext struct {
|
|||||||
machine llvm.TargetMachine
|
machine llvm.TargetMachine
|
||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
intType llvm.Type
|
intType llvm.Type
|
||||||
i8ptrType llvm.Type // for convenience
|
dataPtrType llvm.Type // pointer in address space 0
|
||||||
rawVoidFuncType llvm.Type // for convenience
|
funcPtrType llvm.Type // pointer in function address space (1 for AVR, 0 elsewhere)
|
||||||
funcPtrAddrSpace int
|
funcPtrAddrSpace int
|
||||||
hasTypedPointers bool // for LLVM 14 backwards compatibility
|
|
||||||
uintptrType llvm.Type
|
uintptrType llvm.Type
|
||||||
program *ssa.Program
|
program *ssa.Program
|
||||||
diagnostics []error
|
diagnostics []error
|
||||||
@@ -123,13 +123,12 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
|||||||
} else {
|
} else {
|
||||||
panic("unknown pointer size")
|
panic("unknown pointer size")
|
||||||
}
|
}
|
||||||
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
|
c.dataPtrType = llvm.PointerType(c.ctx.Int8Type(), 0)
|
||||||
|
|
||||||
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
|
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
|
||||||
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
|
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
|
||||||
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
||||||
c.hasTypedPointers = c.i8ptrType != llvm.PointerType(c.ctx.Int16Type(), 0) // with opaque pointers, all pointers are the same type (LLVM 15+)
|
c.funcPtrType = dummyFunc.Type()
|
||||||
c.rawVoidFuncType = dummyFunc.Type()
|
|
||||||
dummyFunc.EraseFromParentAsFunction()
|
dummyFunc.EraseFromParentAsFunction()
|
||||||
|
|
||||||
return c
|
return c
|
||||||
@@ -417,16 +416,14 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
|||||||
case types.Uintptr:
|
case types.Uintptr:
|
||||||
return c.uintptrType
|
return c.uintptrType
|
||||||
case types.UnsafePointer:
|
case types.UnsafePointer:
|
||||||
return c.i8ptrType
|
return c.dataPtrType
|
||||||
default:
|
default:
|
||||||
panic("unknown basic type: " + typ.String())
|
panic("unknown basic type: " + typ.String())
|
||||||
}
|
}
|
||||||
case *types.Chan:
|
case *types.Chan, *types.Map, *types.Pointer:
|
||||||
return llvm.PointerType(c.getLLVMRuntimeType("channel"), 0)
|
return c.dataPtrType // all pointers are the same
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
return c.getLLVMRuntimeType("_interface")
|
return c.getLLVMRuntimeType("_interface")
|
||||||
case *types.Map:
|
|
||||||
return llvm.PointerType(c.getLLVMRuntimeType("hashmap"), 0)
|
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
if st, ok := typ.Underlying().(*types.Struct); ok {
|
if st, ok := typ.Underlying().(*types.Struct); ok {
|
||||||
// Structs are a special case. While other named types are ignored
|
// Structs are a special case. While other named types are ignored
|
||||||
@@ -441,21 +438,11 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
|||||||
return llvmType
|
return llvmType
|
||||||
}
|
}
|
||||||
return c.getLLVMType(typ.Underlying())
|
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
|
case *types.Signature: // function value
|
||||||
return c.getFuncType(typ)
|
return c.getFuncType(typ)
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
ptrType := c.i8ptrType
|
|
||||||
if c.hasTypedPointers {
|
|
||||||
ptrType = llvm.PointerType(c.getLLVMType(typ.Elem()), 0)
|
|
||||||
}
|
|
||||||
members := []llvm.Type{
|
members := []llvm.Type{
|
||||||
ptrType,
|
c.dataPtrType,
|
||||||
c.uintptrType, // len
|
c.uintptrType, // len
|
||||||
c.uintptrType, // cap
|
c.uintptrType, // cap
|
||||||
}
|
}
|
||||||
@@ -545,8 +532,8 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
|||||||
Elements: []llvm.Metadata{
|
Elements: []llvm.Metadata{
|
||||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
Name: "ptr",
|
Name: "ptr",
|
||||||
SizeInBits: c.targetData.TypeAllocSize(c.i8ptrType) * 8,
|
SizeInBits: c.targetData.TypeAllocSize(c.dataPtrType) * 8,
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.i8ptrType)) * 8,
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.dataPtrType)) * 8,
|
||||||
OffsetInBits: 0,
|
OffsetInBits: 0,
|
||||||
Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])),
|
Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])),
|
||||||
}),
|
}),
|
||||||
@@ -1548,21 +1535,18 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
src := argValues[0]
|
src := argValues[0]
|
||||||
elems := argValues[1]
|
elems := argValues[1]
|
||||||
srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
|
srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
|
||||||
srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr")
|
|
||||||
srcLen := b.CreateExtractValue(src, 1, "append.srcLen")
|
srcLen := b.CreateExtractValue(src, 1, "append.srcLen")
|
||||||
srcCap := b.CreateExtractValue(src, 2, "append.srcCap")
|
srcCap := b.CreateExtractValue(src, 2, "append.srcCap")
|
||||||
elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
|
elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
|
||||||
elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr")
|
|
||||||
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
|
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
|
||||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||||
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||||
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new")
|
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
|
||||||
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
||||||
newBuf := b.CreateBitCast(newPtr, srcBuf.Type(), "append.newBuf")
|
|
||||||
newLen := b.CreateExtractValue(result, 1, "append.newLen")
|
newLen := b.CreateExtractValue(result, 1, "append.newLen")
|
||||||
newCap := b.CreateExtractValue(result, 2, "append.newCap")
|
newCap := b.CreateExtractValue(result, 2, "append.newCap")
|
||||||
newSlice := llvm.Undef(src.Type())
|
newSlice := llvm.Undef(src.Type())
|
||||||
newSlice = b.CreateInsertValue(newSlice, newBuf, 0, "")
|
newSlice = b.CreateInsertValue(newSlice, newPtr, 0, "")
|
||||||
newSlice = b.CreateInsertValue(newSlice, newLen, 1, "")
|
newSlice = b.CreateInsertValue(newSlice, newLen, 1, "")
|
||||||
newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
|
newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
|
||||||
return newSlice, nil
|
return newSlice, nil
|
||||||
@@ -1610,9 +1594,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
|
|
||||||
// The pointer to the data to be cleared.
|
// The pointer to the data to be cleared.
|
||||||
llvmBuf := b.CreateExtractValue(value, 0, "buf")
|
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.
|
// The length (in bytes) to be cleared.
|
||||||
llvmLen := b.CreateExtractValue(value, 1, "len")
|
llvmLen := b.CreateExtractValue(value, 1, "len")
|
||||||
@@ -1647,8 +1628,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
||||||
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
|
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
|
||||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
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)
|
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
|
||||||
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
||||||
case "delete":
|
case "delete":
|
||||||
@@ -1888,14 +1867,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
switch value := instr.Value.(type) {
|
switch value := instr.Value.(type) {
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
// Regular function call. No context is necessary.
|
// Regular function call. No context is necessary.
|
||||||
context = llvm.Undef(b.i8ptrType)
|
context = llvm.Undef(b.dataPtrType)
|
||||||
if info.variadic && len(fn.Params) == 0 {
|
if info.variadic && len(fn.Params) == 0 {
|
||||||
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
|
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
|
||||||
// Eventually we might be able to eliminate this special case
|
// Eventually we might be able to eliminate this special case
|
||||||
// entirely. For details, see:
|
// entirely. For details, see:
|
||||||
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
|
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
|
||||||
calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false)
|
calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false)
|
||||||
callee = llvm.ConstBitCast(callee, llvm.PointerType(calleeType, b.funcPtrAddrSpace))
|
|
||||||
}
|
}
|
||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
// A call on a func value, but the callee is trivial to find. For
|
// A call on a func value, but the callee is trivial to find. For
|
||||||
@@ -1923,13 +1901,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
params = append(params, typecode)
|
params = append(params, typecode)
|
||||||
callee = b.getInvokeFunction(instr)
|
callee = b.getInvokeFunction(instr)
|
||||||
calleeType = callee.GlobalValueType()
|
calleeType = callee.GlobalValueType()
|
||||||
context = llvm.Undef(b.i8ptrType)
|
context = llvm.Undef(b.dataPtrType)
|
||||||
} else {
|
} else {
|
||||||
// Function pointer.
|
// Function pointer.
|
||||||
value := b.getValue(instr.Value, getPos(instr))
|
value := b.getValue(instr.Value, getPos(instr))
|
||||||
// This is a func value, which cannot be called directly. We have to
|
// This is a func value, which cannot be called directly. We have to
|
||||||
// extract the function pointer and context first from the func value.
|
// extract the function pointer and context first from the func value.
|
||||||
calleeType, callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
callee, context = b.decodeFuncValue(value)
|
||||||
|
calleeType = b.getLLVMFunctionType(instr.Value.Type().Underlying().(*types.Signature))
|
||||||
b.createNilCheck(instr.Value, callee, "fpcall")
|
b.createNilCheck(instr.Value, callee, "fpcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1962,7 +1941,7 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
|
|||||||
return llvm.Undef(b.getLLVMType(expr.Type()))
|
return llvm.Undef(b.getLLVMType(expr.Type()))
|
||||||
}
|
}
|
||||||
_, fn := b.getFunction(expr)
|
_, fn := b.getFunction(expr)
|
||||||
return b.createFuncValue(fn, llvm.Undef(b.i8ptrType), expr.Signature)
|
return b.createFuncValue(fn, llvm.Undef(b.dataPtrType), expr.Signature)
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
value := b.getGlobal(expr)
|
value := b.getGlobal(expr)
|
||||||
if value.IsNil() {
|
if value.IsNil() {
|
||||||
@@ -2019,8 +1998,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
case *ssa.Alloc:
|
case *ssa.Alloc:
|
||||||
typ := b.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
|
typ := b.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
|
||||||
size := b.targetData.TypeAllocSize(typ)
|
size := b.targetData.TypeAllocSize(typ)
|
||||||
// Move all "large" allocations to the heap. This value is also transform.maxStackAlloc.
|
// Move all "large" allocations to the heap.
|
||||||
if expr.Heap || size > 256 {
|
if expr.Heap || size > b.MaxStackAlloc {
|
||||||
// Calculate ^uintptr(0)
|
// Calculate ^uintptr(0)
|
||||||
maxSize := llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)).ZExtValue()
|
maxSize := llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)).ZExtValue()
|
||||||
if size > maxSize {
|
if size > maxSize {
|
||||||
@@ -2030,7 +2009,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
layoutValue := b.createObjectLayout(typ, expr.Pos())
|
||||||
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
|
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
|
||||||
buf = b.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
} else {
|
} else {
|
||||||
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
|
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
|
||||||
@@ -2076,10 +2054,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
value = b.CreateInsertValue(value, field, i, "changetype.struct")
|
value = b.CreateInsertValue(value, field, i, "changetype.struct")
|
||||||
}
|
}
|
||||||
return value, nil
|
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:
|
default:
|
||||||
return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String())
|
return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String())
|
||||||
}
|
}
|
||||||
@@ -2156,12 +2130,12 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
// Can't load directly from array (as index is non-constant), so
|
// Can't load directly from array (as index is non-constant), so
|
||||||
// have to do it using an alloca+gep+load.
|
// have to do it using an alloca+gep+load.
|
||||||
arrayType := collection.Type()
|
arrayType := collection.Type()
|
||||||
alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
|
alloca, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
|
||||||
b.CreateStore(collection, alloca)
|
b.CreateStore(collection, alloca)
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
|
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
|
||||||
result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
|
result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
|
||||||
b.emitLifetimeEnd(allocaPtr, allocaSize)
|
b.emitLifetimeEnd(alloca, allocaSize)
|
||||||
return result, nil
|
return result, nil
|
||||||
default:
|
default:
|
||||||
panic("unknown *ssa.Index type")
|
panic("unknown *ssa.Index type")
|
||||||
@@ -2264,7 +2238,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
|
||||||
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
|
||||||
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
|
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
|
||||||
slicePtr = b.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array")
|
|
||||||
|
|
||||||
// Extend or truncate if necessary. This is safe as we've already done
|
// Extend or truncate if necessary. This is safe as we've already done
|
||||||
// the bounds check.
|
// the bounds check.
|
||||||
@@ -2310,7 +2283,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
default:
|
default:
|
||||||
panic("unknown type in range: " + typ.String())
|
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)
|
b.CreateStore(llvm.ConstNull(iteratorType), it)
|
||||||
return it, nil
|
return it, nil
|
||||||
case *ssa.Select:
|
case *ssa.Select:
|
||||||
@@ -2478,7 +2451,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
|
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
|
||||||
b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
|
b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
|
||||||
ptr := b.CreateExtractValue(slice, 0, "")
|
ptr := b.CreateExtractValue(slice, 0, "")
|
||||||
ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
|
|
||||||
return ptr, nil
|
return ptr, nil
|
||||||
case *ssa.TypeAssert:
|
case *ssa.TypeAssert:
|
||||||
return b.createTypeAssert(expr), nil
|
return b.createTypeAssert(expr), nil
|
||||||
@@ -2944,16 +2916,16 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
|
|||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero})
|
strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero})
|
||||||
} else {
|
} else {
|
||||||
strPtr = llvm.ConstNull(c.i8ptrType)
|
strPtr = llvm.ConstNull(c.dataPtrType)
|
||||||
}
|
}
|
||||||
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||||
return strObj
|
return strObj
|
||||||
} else if typ.Kind() == types.UnsafePointer {
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
if !expr.IsNil() {
|
if !expr.IsNil() {
|
||||||
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType)
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.dataPtrType)
|
||||||
}
|
}
|
||||||
return llvm.ConstNull(c.i8ptrType)
|
return llvm.ConstNull(c.dataPtrType)
|
||||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||||
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||||
return llvm.ConstInt(llvmType, n, false)
|
return llvm.ConstInt(llvmType, n, false)
|
||||||
@@ -2997,7 +2969,7 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
|
|||||||
// Create a generic nil interface with no dynamic type (typecode=0).
|
// Create a generic nil interface with no dynamic type (typecode=0).
|
||||||
fields := []llvm.Value{
|
fields := []llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
llvm.ConstPointerNull(c.i8ptrType),
|
llvm.ConstPointerNull(c.dataPtrType),
|
||||||
}
|
}
|
||||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
@@ -3014,8 +2986,7 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
|
|||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil slice constant")
|
panic("expected nil slice constant")
|
||||||
}
|
}
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
llvmPtr := llvm.ConstPointerNull(c.dataPtrType)
|
||||||
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
|
||||||
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
|
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
|
||||||
slice := c.ctx.ConstStruct([]llvm.Value{
|
slice := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvmPtr, // backing array
|
llvmPtr, // backing array
|
||||||
@@ -3056,7 +3027,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p
|
|||||||
|
|
||||||
// Conversion between pointers and unsafe.Pointer.
|
// Conversion between pointers and unsafe.Pointer.
|
||||||
if isPtrFrom && isPtrTo {
|
if isPtrFrom && isPtrTo {
|
||||||
return b.CreateBitCast(value, llvmTypeTo, ""), nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch typeTo := typeTo.Underlying().(type) {
|
switch typeTo := typeTo.Underlying().(type) {
|
||||||
@@ -3291,11 +3262,15 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
|||||||
// Instead of a load from the global, create a bitcast of the
|
// Instead of a load from the global, create a bitcast of the
|
||||||
// function pointer itself.
|
// function pointer itself.
|
||||||
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
|
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
|
||||||
_, fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
|
pkg := b.fn.Pkg
|
||||||
|
if pkg == nil {
|
||||||
|
pkg = b.fn.Origin().Pkg
|
||||||
|
}
|
||||||
|
_, fn := b.getFunction(pkg.Members[name].(*ssa.Function))
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
|
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
|
||||||
}
|
}
|
||||||
return b.CreateBitCast(fn, b.i8ptrType, ""), nil
|
return fn, nil
|
||||||
} else {
|
} else {
|
||||||
b.createNilCheck(unop.X, x, "deref")
|
b.createNilCheck(unop.X, x, "deref")
|
||||||
load := b.CreateLoad(valueType, x, "")
|
load := b.CreateLoad(valueType, x, "")
|
||||||
|
|||||||
@@ -91,14 +91,12 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Optimize IR a little.
|
// Optimize IR a little.
|
||||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
passOptions := llvm.NewPassBuilderOptions()
|
||||||
defer funcPasses.Dispose()
|
defer passOptions.Dispose()
|
||||||
funcPasses.AddInstructionCombiningPass()
|
err = mod.RunPasses("instcombine", llvm.TargetMachine{}, passOptions)
|
||||||
funcPasses.InitializeFunc()
|
if err != nil {
|
||||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
t.Error(err)
|
||||||
funcPasses.RunFunc(fn)
|
|
||||||
}
|
}
|
||||||
funcPasses.FinalizeFunc()
|
|
||||||
|
|
||||||
outFilePrefix := tc.file[:len(tc.file)-3]
|
outFilePrefix := tc.file[:len(tc.file)-3]
|
||||||
if tc.target != "" {
|
if tc.target != "" {
|
||||||
@@ -169,9 +167,9 @@ func filterIrrelevantIRLines(lines []string) []string {
|
|||||||
if strings.HasPrefix(line, "source_filename = ") {
|
if strings.HasPrefix(line, "source_filename = ") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
|
if llvmVersion < 15 && strings.HasPrefix(line, "target datalayout = ") {
|
||||||
// The datalayout string may vary betewen LLVM versions.
|
// The datalayout string may vary betewen LLVM versions.
|
||||||
// Right now test outputs are for LLVM 14 and higher.
|
// Right now test outputs are for LLVM 15 and higher.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out = append(out, line)
|
out = append(out, line)
|
||||||
@@ -245,7 +243,7 @@ func testCompilePackage(t *testing.T, options *compileopts.Options, file string)
|
|||||||
defer machine.Dispose()
|
defer machine.Dispose()
|
||||||
|
|
||||||
// Load entire program AST into memory.
|
// Load entire program AST into memory.
|
||||||
lprogram, err := loader.Load(config, "./testdata/"+file, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, "./testdata/"+file, types.Config{
|
||||||
Sizes: Sizes(machine),
|
Sizes: Sizes(machine),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+24
-33
@@ -60,9 +60,8 @@ func (b *builder) deferInitFunc() {
|
|||||||
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
||||||
|
|
||||||
// Create defer list pointer.
|
// Create defer list pointer.
|
||||||
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
|
||||||
b.deferPtr = b.CreateAlloca(deferType, "deferPtr")
|
b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
|
||||||
b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr)
|
|
||||||
|
|
||||||
if b.hasDeferFrame() {
|
if b.hasDeferFrame() {
|
||||||
// Set up the defer frame with the current stack pointer.
|
// Set up the defer frame with the current stack pointer.
|
||||||
@@ -249,8 +248,7 @@ func isInLoop(start *ssa.BasicBlock) bool {
|
|||||||
func (b *builder) createDefer(instr *ssa.Defer) {
|
func (b *builder) createDefer(instr *ssa.Defer) {
|
||||||
// The pointer to the previous defer struct, which we will replace to
|
// The pointer to the previous defer struct, which we will replace to
|
||||||
// make a linked list.
|
// make a linked list.
|
||||||
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
|
||||||
next := b.CreateLoad(deferType, b.deferPtr, "defer.next")
|
|
||||||
|
|
||||||
var values []llvm.Value
|
var values []llvm.Value
|
||||||
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
valueTypes := []llvm.Type{b.uintptrType, next.Type()}
|
||||||
@@ -271,7 +269,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||||
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
||||||
values = []llvm.Value{callback, next, typecode, receiverValue}
|
values = []llvm.Value{callback, next, typecode, receiverValue}
|
||||||
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
|
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
||||||
for _, arg := range instr.Call.Args {
|
for _, arg := range instr.Call.Args {
|
||||||
val := b.getValue(arg, getPos(instr))
|
val := b.getValue(arg, getPos(instr))
|
||||||
values = append(values, val)
|
values = append(values, val)
|
||||||
@@ -391,9 +389,8 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
// This may be hit a variable number of times, so use a heap allocation.
|
// This may be hit a variable number of times, so use a heap allocation.
|
||||||
size := b.targetData.TypeAllocSize(deferredCallType)
|
size := b.targetData.TypeAllocSize(deferredCallType)
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
nilPtr := llvm.ConstNull(b.i8ptrType)
|
nilPtr := llvm.ConstNull(b.dataPtrType)
|
||||||
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
|
||||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferredCallType, 0), "defer.alloc")
|
|
||||||
}
|
}
|
||||||
if b.NeedsStackObjects {
|
if b.NeedsStackObjects {
|
||||||
b.trackPointer(alloca)
|
b.trackPointer(alloca)
|
||||||
@@ -401,14 +398,12 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
|||||||
b.CreateStore(deferredCall, alloca)
|
b.CreateStore(deferredCall, alloca)
|
||||||
|
|
||||||
// Push it on top of the linked list by replacing deferPtr.
|
// Push it on top of the linked list by replacing deferPtr.
|
||||||
allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
b.CreateStore(alloca, b.deferPtr)
|
||||||
b.CreateStore(allocaCast, b.deferPtr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createRunDefers emits code to run all deferred functions.
|
// createRunDefers emits code to run all deferred functions.
|
||||||
func (b *builder) createRunDefers() {
|
func (b *builder) createRunDefers() {
|
||||||
deferType := b.getLLVMRuntimeType("_defer")
|
deferType := b.getLLVMRuntimeType("_defer")
|
||||||
deferPtrType := llvm.PointerType(deferType, 0)
|
|
||||||
|
|
||||||
// Add a loop like the following:
|
// Add a loop like the following:
|
||||||
// for stack != nil {
|
// for stack != nil {
|
||||||
@@ -435,7 +430,7 @@ func (b *builder) createRunDefers() {
|
|||||||
// Create loop head:
|
// Create loop head:
|
||||||
// for stack != nil {
|
// for stack != nil {
|
||||||
b.SetInsertPointAtEnd(loophead)
|
b.SetInsertPointAtEnd(loophead)
|
||||||
deferData := b.CreateLoad(deferPtrType, b.deferPtr, "")
|
deferData := b.CreateLoad(b.dataPtrType, b.deferPtr, "")
|
||||||
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
|
stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
|
||||||
b.CreateCondBr(stackIsNil, end, loop)
|
b.CreateCondBr(stackIsNil, end, loop)
|
||||||
|
|
||||||
@@ -448,7 +443,7 @@ func (b *builder) createRunDefers() {
|
|||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
|
llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
|
||||||
}, "stack.next.gep")
|
}, "stack.next.gep")
|
||||||
nextStack := b.CreateLoad(deferPtrType, nextStackGEP, "stack.next")
|
nextStack := b.CreateLoad(b.dataPtrType, nextStackGEP, "stack.next")
|
||||||
b.CreateStore(nextStack, b.deferPtr)
|
b.CreateStore(nextStack, b.deferPtr)
|
||||||
gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
|
gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
@@ -469,28 +464,26 @@ func (b *builder) createRunDefers() {
|
|||||||
// Call on an value or interface value.
|
// Call on an value or interface value.
|
||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||||
|
|
||||||
if !callback.IsInvoke() {
|
if !callback.IsInvoke() {
|
||||||
//Expect funcValue to be passed through the deferred call.
|
//Expect funcValue to be passed through the deferred call.
|
||||||
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
||||||
} else {
|
} else {
|
||||||
//Expect typecode
|
//Expect typecode
|
||||||
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType)
|
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, arg := range callback.Args {
|
for _, arg := range callback.Args {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
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).
|
// Extract the params from the struct (including receiver).
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
for i := 2; i < len(valueTypes); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -505,7 +498,8 @@ func (b *builder) createRunDefers() {
|
|||||||
|
|
||||||
//Get function pointer and context
|
//Get function pointer and context
|
||||||
var context llvm.Value
|
var context llvm.Value
|
||||||
fnType, fnPtr, context = b.decodeFuncValue(funcValue, callback.Signature())
|
fnPtr, context = b.decodeFuncValue(funcValue)
|
||||||
|
fnType = b.getLLVMFunctionType(callback.Signature())
|
||||||
|
|
||||||
//Pass context
|
//Pass context
|
||||||
forwardParams = append(forwardParams, context)
|
forwardParams = append(forwardParams, context)
|
||||||
@@ -519,7 +513,7 @@ func (b *builder) createRunDefers() {
|
|||||||
// Add the context parameter. An interface call cannot also be a
|
// Add the context parameter. An interface call cannot also be a
|
||||||
// closure but we have to supply the parameter anyway for platforms
|
// closure but we have to supply the parameter anyway for platforms
|
||||||
// with a strict calling convention.
|
// with a strict calling convention.
|
||||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||||
}
|
}
|
||||||
|
|
||||||
b.createCall(fnType, fnPtr, forwardParams, "")
|
b.createCall(fnType, fnPtr, forwardParams, "")
|
||||||
@@ -528,18 +522,17 @@ func (b *builder) createRunDefers() {
|
|||||||
// Direct call.
|
// Direct call.
|
||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||||
for _, param := range getParams(callback.Signature) {
|
for _, param := range getParams(callback.Signature) {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||||
}
|
}
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := range getParams(callback.Signature) {
|
for i := range getParams(callback.Signature) {
|
||||||
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -549,7 +542,7 @@ func (b *builder) createRunDefers() {
|
|||||||
if !b.getFunctionInfo(callback).exported {
|
if !b.getFunctionInfo(callback).exported {
|
||||||
// Add the context parameter. We know it is ignored by the receiving
|
// Add the context parameter. We know it is ignored by the receiving
|
||||||
// function, but we have to pass one anyway.
|
// function, but we have to pass one anyway.
|
||||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call real function.
|
// Call real function.
|
||||||
@@ -559,20 +552,19 @@ func (b *builder) createRunDefers() {
|
|||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
fn := callback.Fn.(*ssa.Function)
|
fn := callback.Fn.(*ssa.Function)
|
||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||||
params := fn.Signature.Params()
|
params := fn.Signature.Params()
|
||||||
for i := 0; i < params.Len(); i++ {
|
for i := 0; i < params.Len(); i++ {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||||
}
|
}
|
||||||
valueTypes = append(valueTypes, b.i8ptrType) // closure
|
valueTypes = append(valueTypes, b.dataPtrType) // closure
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
forwardParams := []llvm.Value{}
|
forwardParams := []llvm.Value{}
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 2; i < len(valueTypes); i++ {
|
for i := 2; i < len(valueTypes); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
|
||||||
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
@@ -584,7 +576,7 @@ func (b *builder) createRunDefers() {
|
|||||||
db := b.deferBuiltinFuncs[callback]
|
db := b.deferBuiltinFuncs[callback]
|
||||||
|
|
||||||
//Get parameter types
|
//Get parameter types
|
||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||||
|
|
||||||
//Get signature from call results
|
//Get signature from call results
|
||||||
params := callback.Type().Underlying().(*types.Signature).Params()
|
params := callback.Type().Underlying().(*types.Signature).Params()
|
||||||
@@ -593,13 +585,12 @@ func (b *builder) createRunDefers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall")
|
|
||||||
|
|
||||||
// Extract the params from the struct.
|
// Extract the params from the struct.
|
||||||
var argValues []llvm.Value
|
var argValues []llvm.Value
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
for i := 0; i < params.Len(); i++ {
|
for i := 0; i < params.Len(); i++ {
|
||||||
gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
|
||||||
argValues = append(argValues, forwardParam)
|
argValues = append(argValues, forwardParam)
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-39
@@ -13,35 +13,14 @@ import (
|
|||||||
// createFuncValue creates a function value from a raw function pointer with no
|
// createFuncValue creates a function value from a raw function pointer with no
|
||||||
// context.
|
// context.
|
||||||
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
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}
|
// Closure is: {context, function pointer}
|
||||||
funcValueScalar := llvm.ConstBitCast(funcPtr, c.rawVoidFuncType)
|
funcValueType := b.getFuncType(sig)
|
||||||
funcValueType := c.getFuncType(sig)
|
|
||||||
funcValue := llvm.Undef(funcValueType)
|
funcValue := llvm.Undef(funcValueType)
|
||||||
funcValue = builder.CreateInsertValue(funcValue, context, 0, "")
|
funcValue = b.CreateInsertValue(funcValue, context, 0, "")
|
||||||
funcValue = builder.CreateInsertValue(funcValue, funcValueScalar, 1, "")
|
funcValue = b.CreateInsertValue(funcValue, funcPtr, 1, "")
|
||||||
return funcValue
|
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
|
// extractFuncScalar returns some scalar that can be used in comparisons. It is
|
||||||
// a cheap operation.
|
// a cheap operation.
|
||||||
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
||||||
@@ -55,28 +34,20 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// decodeFuncValue extracts the context and the function pointer from this func
|
// decodeFuncValue extracts the context and the function pointer from this func
|
||||||
// value. This may be an expensive operation.
|
// value.
|
||||||
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcType llvm.Type, funcPtr, context llvm.Value) {
|
func (b *builder) decodeFuncValue(funcValue llvm.Value) (funcPtr, context llvm.Value) {
|
||||||
context = b.CreateExtractValue(funcValue, 0, "")
|
context = b.CreateExtractValue(funcValue, 0, "")
|
||||||
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFuncType returns the type of a func value given a signature.
|
// getFuncType returns the type of a func value given a signature.
|
||||||
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
|
return c.ctx.StructType([]llvm.Type{c.dataPtrType, c.funcPtrType}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRawFuncType returns a LLVM function type for a given signature.
|
// getLLVMFunctionType returns a LLVM function type for a given signature.
|
||||||
func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
||||||
// Get the return type.
|
// Get the return type.
|
||||||
var returnType llvm.Type
|
var returnType llvm.Type
|
||||||
switch typ.Results().Len() {
|
switch typ.Results().Len() {
|
||||||
@@ -104,7 +75,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
|||||||
if recv.StructName() == "runtime._interface" {
|
if recv.StructName() == "runtime._interface" {
|
||||||
// This is a call on an interface, not a concrete type.
|
// This is a call on an interface, not a concrete type.
|
||||||
// The receiver is not an interface, but a i8* type.
|
// The receiver is not an interface, but a i8* type.
|
||||||
recv = c.i8ptrType
|
recv = c.dataPtrType
|
||||||
}
|
}
|
||||||
for _, info := range c.expandFormalParamType(recv, "", nil) {
|
for _, info := range c.expandFormalParamType(recv, "", nil) {
|
||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
@@ -117,7 +88,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// All functions take these parameters at the end.
|
// All functions take these parameters at the end.
|
||||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
paramTypes = append(paramTypes, c.dataPtrType) // context
|
||||||
|
|
||||||
// Make a func type out of the signature.
|
// Make a func type out of the signature.
|
||||||
return llvm.FunctionType(returnType, paramTypes, false)
|
return llvm.FunctionType(returnType, paramTypes, false)
|
||||||
|
|||||||
@@ -81,9 +81,6 @@ func (b *builder) trackValue(value llvm.Value) {
|
|||||||
// trackPointer creates a call to runtime.trackPointer, bitcasting the poitner
|
// trackPointer creates a call to runtime.trackPointer, bitcasting the poitner
|
||||||
// first if needed. The input value must be of LLVM pointer type.
|
// first if needed. The input value must be of LLVM pointer type.
|
||||||
func (b *builder) trackPointer(value llvm.Value) {
|
func (b *builder) trackPointer(value llvm.Value) {
|
||||||
if value.Type() != b.i8ptrType {
|
|
||||||
value = b.CreateBitCast(value, b.i8ptrType, "")
|
|
||||||
}
|
|
||||||
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "")
|
b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-12
@@ -21,7 +21,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
|
|
||||||
var prefix string
|
var prefix string
|
||||||
var funcPtr llvm.Value
|
var funcPtr llvm.Value
|
||||||
var funcPtrType llvm.Type
|
var funcType llvm.Type
|
||||||
hasContext := false
|
hasContext := false
|
||||||
if callee := instr.Call.StaticCallee(); callee != nil {
|
if callee := instr.Call.StaticCallee(); callee != nil {
|
||||||
// Static callee is known. This makes it easier to start a new
|
// 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
|
params = append(params, context) // context parameter
|
||||||
hasContext = true
|
hasContext = true
|
||||||
}
|
}
|
||||||
funcPtrType, funcPtr = b.getFunction(callee)
|
funcType, funcPtr = b.getFunction(callee)
|
||||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||||
// We cheat. None of the builtins do any long or blocking operation, so
|
// We cheat. None of the builtins do any long or blocking operation, so
|
||||||
// we might as well run these builtins right away without the program
|
// we might as well run these builtins right away without the program
|
||||||
@@ -80,7 +80,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
itfTypeCode := b.CreateExtractValue(itf, 0, "")
|
itfTypeCode := b.CreateExtractValue(itf, 0, "")
|
||||||
itfValue := b.CreateExtractValue(itf, 1, "")
|
itfValue := b.CreateExtractValue(itf, 1, "")
|
||||||
funcPtr = b.getInvokeFunction(&instr.Call)
|
funcPtr = b.getInvokeFunction(&instr.Call)
|
||||||
funcPtrType = funcPtr.GlobalValueType()
|
funcType = funcPtr.GlobalValueType()
|
||||||
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
params = append([]llvm.Value{itfValue}, params...) // start with receiver
|
||||||
params = append(params, itfTypeCode) // end with typecode
|
params = append(params, itfTypeCode) // end with typecode
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +90,8 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
// * The function context, for closures.
|
// * The function context, for closures.
|
||||||
// * The function pointer (for tasks).
|
// * The function pointer (for tasks).
|
||||||
var context llvm.Value
|
var context llvm.Value
|
||||||
funcPtrType, funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)), instr.Call.Value.Type().Underlying().(*types.Signature))
|
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)))
|
||||||
|
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||||
params = append(params, context, funcPtr)
|
params = append(params, context, funcPtr)
|
||||||
hasContext = true
|
hasContext = true
|
||||||
prefix = b.fn.RelString(nil)
|
prefix = b.fn.RelString(nil)
|
||||||
@@ -98,14 +99,14 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
|
|
||||||
paramBundle := b.emitPointerPack(params)
|
paramBundle := b.emitPointerPack(params)
|
||||||
var stackSize llvm.Value
|
var stackSize llvm.Value
|
||||||
callee := b.createGoroutineStartWrapper(funcPtrType, funcPtr, prefix, hasContext, instr.Pos())
|
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, instr.Pos())
|
||||||
if b.AutomaticStackSize {
|
if b.AutomaticStackSize {
|
||||||
// The stack size is not known until after linking. Call a dummy
|
// The stack size is not known until after linking. Call a dummy
|
||||||
// function that will be replaced with a load from a special ELF
|
// function that will be replaced with a load from a special ELF
|
||||||
// section that contains the stack size (and is modified after
|
// section that contains the stack size (and is modified after
|
||||||
// linking).
|
// linking).
|
||||||
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
||||||
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize")
|
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.dataPtrType)}, "stacksize")
|
||||||
} else {
|
} else {
|
||||||
// The stack size is fixed at compile time. By emitting it here as a
|
// The stack size is fixed at compile time. By emitting it here as a
|
||||||
// constant, it can be optimized.
|
// constant, it can be optimized.
|
||||||
@@ -115,7 +116,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||||
}
|
}
|
||||||
fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
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.i8ptrType)}, "")
|
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// createGoroutineStartWrapper creates a wrapper for the task-based
|
// createGoroutineStartWrapper creates a wrapper for the task-based
|
||||||
@@ -165,7 +166,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
||||||
c.addStandardAttributes(wrapper)
|
c.addStandardAttributes(wrapper)
|
||||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
@@ -203,7 +204,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
}
|
}
|
||||||
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
params := b.emitPointerUnpack(wrapper.Param(0), paramTypes)
|
||||||
if !hasContext {
|
if !hasContext {
|
||||||
params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter
|
params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the call.
|
// Create the call.
|
||||||
@@ -211,7 +212,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
|
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||||
llvm.Undef(c.i8ptrType),
|
llvm.Undef(c.dataPtrType),
|
||||||
}, "")
|
}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
// merged into one.
|
// merged into one.
|
||||||
|
|
||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
||||||
c.addStandardAttributes(wrapper)
|
c.addStandardAttributes(wrapper)
|
||||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
@@ -279,7 +280,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
|
|
||||||
if c.Scheduler == "asyncify" {
|
if c.Scheduler == "asyncify" {
|
||||||
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
b.CreateCall(deadlockType, deadlock, []llvm.Value{
|
||||||
llvm.Undef(c.i8ptrType),
|
llvm.Undef(c.dataPtrType),
|
||||||
}, "")
|
}, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
case llvm.IntegerTypeKind:
|
case llvm.IntegerTypeKind:
|
||||||
constraints = append(constraints, "r")
|
constraints = append(constraints, "r")
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
// Memory references require a type in LLVM 14, probably as a
|
// Memory references require a type starting with LLVM 14,
|
||||||
// preparation for opaque pointers.
|
// probably as a preparation for opaque pointers.
|
||||||
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
|
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
|
||||||
return s
|
return s
|
||||||
default:
|
default:
|
||||||
|
|||||||
+24
-18
@@ -414,10 +414,10 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
}, typeFields...)
|
}, typeFields...)
|
||||||
if hasMethodSet {
|
if hasMethodSet {
|
||||||
typeFields = append([]llvm.Value{
|
typeFields = append([]llvm.Value{
|
||||||
llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType),
|
c.getTypeMethodSet(typ),
|
||||||
}, typeFields...)
|
}, typeFields...)
|
||||||
}
|
}
|
||||||
alignment := c.targetData.TypeAllocSize(c.i8ptrType)
|
alignment := c.targetData.TypeAllocSize(c.dataPtrType)
|
||||||
if alignment < 4 {
|
if alignment < 4 {
|
||||||
alignment = 4
|
alignment = 4
|
||||||
}
|
}
|
||||||
@@ -628,7 +628,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||||||
// Construct global value.
|
// Construct global value.
|
||||||
globalValue := c.ctx.ConstStruct([]llvm.Value{
|
globalValue := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
|
llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
|
||||||
llvm.ConstArray(c.i8ptrType, signatures),
|
llvm.ConstArray(c.dataPtrType, signatures),
|
||||||
c.ctx.ConstStruct(wrappers, false),
|
c.ctx.ConstStruct(wrappers, false),
|
||||||
}, false)
|
}, false)
|
||||||
global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
|
global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
|
||||||
@@ -684,19 +684,25 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
|
|
||||||
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
|
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
|
||||||
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 on interface type with methods.
|
||||||
|
// 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}, "")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
name, _ := getTypeCodeName(expr.AssertedType)
|
name, _ := getTypeCodeName(expr.AssertedType)
|
||||||
globalName := "reflect/types.typeid:" + name
|
globalName := "reflect/types.typeid:" + name
|
||||||
@@ -779,7 +785,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll
|
|||||||
fnName := s + ".$typeassert"
|
fnName := s + ".$typeassert"
|
||||||
llvmFn := c.mod.NamedFunction(fnName)
|
llvmFn := c.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, false)
|
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false)
|
||||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||||
c.addStandardDeclaredAttributes(llvmFn)
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
||||||
@@ -802,7 +808,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
|||||||
paramTuple = append(paramTuple, sig.Params().At(i))
|
paramTuple = append(paramTuple, sig.Params().At(i))
|
||||||
}
|
}
|
||||||
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
|
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
|
||||||
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
|
llvmFnType := c.getLLVMFunctionType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
|
||||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||||
c.addStandardDeclaredAttributes(llvmFn)
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
|
||||||
@@ -842,7 +848,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create wrapper function
|
// create wrapper function
|
||||||
paramTypes := append([]llvm.Type{c.i8ptrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
|
paramTypes := append([]llvm.Type{c.dataPtrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...)
|
||||||
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
|
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
||||||
c.addStandardAttributes(wrapper)
|
c.addStandardAttributes(wrapper)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
|||||||
// Fall back to a generic error.
|
// Fall back to a generic error.
|
||||||
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
|
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
|
||||||
}
|
}
|
||||||
_, funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil)
|
funcRawPtr, funcContext := b.decodeFuncValue(funcValue)
|
||||||
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
|
funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType)
|
||||||
|
|
||||||
// Create a new global of type runtime/interrupt.handle. Globals of this
|
// Create a new global of type runtime/interrupt.handle. Globals of this
|
||||||
|
|||||||
+3
-10
@@ -7,7 +7,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -48,12 +47,9 @@ func (b *builder) defineIntrinsicFunction() {
|
|||||||
func (b *builder) createMemoryCopyImpl() {
|
func (b *builder) createMemoryCopyImpl() {
|
||||||
b.createFunctionStart(true)
|
b.createFunctionStart(true)
|
||||||
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
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)
|
llvmFn := b.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false)
|
||||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
var params []llvm.Value
|
var params []llvm.Value
|
||||||
@@ -84,12 +80,9 @@ func (b *builder) createMemoryZeroImpl() {
|
|||||||
// Return the llvm.memset.p0.i8 function declaration.
|
// Return the llvm.memset.p0.i8 function declaration.
|
||||||
func (c *compilerContext) getMemsetFunc() llvm.Value {
|
func (c *compilerContext) getMemsetFunc() llvm.Value {
|
||||||
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
|
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)
|
llvmFn := c.mod.NamedFunction(fnName)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
|
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
|
||||||
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
|
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
return llvmFn
|
return llvmFn
|
||||||
@@ -111,7 +104,7 @@ func (b *builder) createKeepAliveImpl() {
|
|||||||
//
|
//
|
||||||
// It should be portable to basically everything as the "r" register type
|
// It should be portable to basically everything as the "r" register type
|
||||||
// exists basically everywhere.
|
// exists basically everywhere.
|
||||||
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false)
|
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
|
||||||
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
|
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
|
||||||
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
|
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
|
||||||
|
|
||||||
|
|||||||
+39
-44
@@ -20,7 +20,7 @@ import (
|
|||||||
//
|
//
|
||||||
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
||||||
// end the lifetime using emitLifetimeEnd after you're done with it.
|
// end the lifetime using emitLifetimeEnd after you're done with it.
|
||||||
func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, size llvm.Value) {
|
||||||
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
|
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,47 +63,45 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
// Allocate memory for the packed data.
|
// Allocate memory for the packed data.
|
||||||
size := b.targetData.TypeAllocSize(packedType)
|
size := b.targetData.TypeAllocSize(packedType)
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return llvm.ConstPointerNull(b.i8ptrType)
|
return llvm.ConstPointerNull(b.dataPtrType)
|
||||||
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
return b.CreateBitCast(values[0], b.i8ptrType, "pack.ptr")
|
return values[0]
|
||||||
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
|
} else if size <= b.targetData.TypeAllocSize(b.dataPtrType) {
|
||||||
// Packed data fits in a pointer, so store it directly inside the
|
// Packed data fits in a pointer, so store it directly inside the
|
||||||
// pointer.
|
// pointer.
|
||||||
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
||||||
// Try to keep this cast in SSA form.
|
// Try to keep this cast in SSA form.
|
||||||
return b.CreateIntToPtr(values[0], b.i8ptrType, "pack.int")
|
return b.CreateIntToPtr(values[0], b.dataPtrType, "pack.int")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Because packedType is a struct and we have to cast it to a *i8, store
|
// 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
|
// it in a *i8 alloca first and load the *i8 value from there. This is
|
||||||
// effectively a bitcast.
|
// effectively a bitcast.
|
||||||
packedAlloc, _, _ := b.createTemporaryAlloca(b.i8ptrType, "")
|
packedAlloc, _ := b.createTemporaryAlloca(b.dataPtrType, "")
|
||||||
|
|
||||||
if size < b.targetData.TypeAllocSize(b.i8ptrType) {
|
if size < b.targetData.TypeAllocSize(b.dataPtrType) {
|
||||||
// The alloca is bigger than the value that will be stored in it.
|
// The alloca is bigger than the value that will be stored in it.
|
||||||
// To avoid having some bits undefined, zero the alloca first.
|
// To avoid having some bits undefined, zero the alloca first.
|
||||||
// Hopefully this will get optimized away.
|
// Hopefully this will get optimized away.
|
||||||
b.CreateStore(llvm.ConstNull(b.i8ptrType), packedAlloc)
|
b.CreateStore(llvm.ConstNull(b.dataPtrType), packedAlloc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store all values in the alloca.
|
// Store all values in the alloca.
|
||||||
packedAllocCast := b.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
|
|
||||||
for i, value := range values {
|
for i, value := range values {
|
||||||
indices := []llvm.Value{
|
indices := []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||||
}
|
}
|
||||||
gep := b.CreateInBoundsGEP(packedType, packedAllocCast, indices, "")
|
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||||
b.CreateStore(value, gep)
|
b.CreateStore(value, gep)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load value (the *i8) from the alloca.
|
// Load value (the *i8) from the alloca.
|
||||||
result := b.CreateLoad(b.i8ptrType, packedAlloc, "")
|
result := b.CreateLoad(b.dataPtrType, packedAlloc, "")
|
||||||
|
|
||||||
// End the lifetime of the alloca, to help the optimizer.
|
// 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)
|
packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||||
b.emitLifetimeEnd(packedPtr, packedSize)
|
b.emitLifetimeEnd(packedAlloc, packedSize)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
@@ -124,21 +122,20 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
global.SetLinkage(llvm.InternalLinkage)
|
global.SetLinkage(llvm.InternalLinkage)
|
||||||
return llvm.ConstBitCast(global, b.i8ptrType)
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||||
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
|
||||||
alloc := b.mod.NamedFunction("runtime.alloc")
|
alloc := b.mod.NamedFunction("runtime.alloc")
|
||||||
packedHeapAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
|
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
|
||||||
sizeValue,
|
sizeValue,
|
||||||
llvm.ConstNull(b.i8ptrType),
|
llvm.ConstNull(b.dataPtrType),
|
||||||
llvm.Undef(b.i8ptrType), // unused context parameter
|
llvm.Undef(b.dataPtrType), // unused context parameter
|
||||||
}, "")
|
}, "")
|
||||||
if b.NeedsStackObjects {
|
if b.NeedsStackObjects {
|
||||||
b.trackPointer(packedHeapAlloc)
|
b.trackPointer(packedAlloc)
|
||||||
}
|
}
|
||||||
packedAlloc := b.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
|
||||||
|
|
||||||
// Store all values in the heap pointer.
|
// Store all values in the heap pointer.
|
||||||
for i, value := range values {
|
for i, value := range values {
|
||||||
@@ -151,7 +148,7 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the original heap allocation pointer, which already is an *i8.
|
// Return the original heap allocation pointer, which already is an *i8.
|
||||||
return packedHeapAlloc
|
return packedAlloc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,28 +157,27 @@ func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []ll
|
|||||||
packedType := b.ctx.StructType(valueTypes, false)
|
packedType := b.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
// Get a correctly-typed pointer to the packed data.
|
// Get a correctly-typed pointer to the packed data.
|
||||||
var packedAlloc, packedRawAlloc llvm.Value
|
var packedAlloc llvm.Value
|
||||||
|
needsLifetimeEnd := false
|
||||||
size := b.targetData.TypeAllocSize(packedType)
|
size := b.targetData.TypeAllocSize(packedType)
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
// No data to unpack.
|
// No data to unpack.
|
||||||
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
||||||
// A single pointer is always stored directly.
|
// A single pointer is always stored directly.
|
||||||
return []llvm.Value{b.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
return []llvm.Value{ptr}
|
||||||
} else if size <= b.targetData.TypeAllocSize(b.i8ptrType) {
|
} else if size <= b.targetData.TypeAllocSize(b.dataPtrType) {
|
||||||
// Packed data stored directly in pointer.
|
// Packed data stored directly in pointer.
|
||||||
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
||||||
// Keep this cast in SSA form.
|
// Keep this cast in SSA form.
|
||||||
return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
||||||
}
|
}
|
||||||
// Fallback: load it using an alloca.
|
// Fallback: load it using an alloca.
|
||||||
packedRawAlloc, _, _ = b.createTemporaryAlloca(llvm.PointerType(b.i8ptrType, 0), "unpack.raw.alloc")
|
packedAlloc, _ = b.createTemporaryAlloca(b.dataPtrType, "unpack.raw.alloc")
|
||||||
packedRawValue := b.CreateBitCast(ptr, llvm.PointerType(b.i8ptrType, 0), "unpack.raw.value")
|
b.CreateStore(ptr, packedAlloc)
|
||||||
b.CreateStore(packedRawValue, packedRawAlloc)
|
needsLifetimeEnd = true
|
||||||
packedAlloc = b.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
|
||||||
} else {
|
} else {
|
||||||
// Packed data stored on the heap. Bitcast the passed-in pointer to the
|
// Packed data stored on the heap.
|
||||||
// correct pointer type.
|
packedAlloc = ptr
|
||||||
packedAlloc = b.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
|
|
||||||
}
|
}
|
||||||
// Load each value from the packed data.
|
// Load each value from the packed data.
|
||||||
values := make([]llvm.Value, len(valueTypes))
|
values := make([]llvm.Value, len(valueTypes))
|
||||||
@@ -198,10 +194,9 @@ func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []ll
|
|||||||
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "")
|
||||||
values[i] = b.CreateLoad(valueType, gep, "")
|
values[i] = b.CreateLoad(valueType, gep, "")
|
||||||
}
|
}
|
||||||
if !packedRawAlloc.IsNil() {
|
if needsLifetimeEnd {
|
||||||
allocPtr := b.CreateBitCast(packedRawAlloc, b.i8ptrType, "")
|
|
||||||
allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false)
|
allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false)
|
||||||
b.emitLifetimeEnd(allocPtr, allocSize)
|
b.emitLifetimeEnd(packedAlloc, allocSize)
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
@@ -253,12 +248,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
|
// Do a few checks to see whether we need to generate any object layout
|
||||||
// information at all.
|
// information at all.
|
||||||
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
||||||
pointerSize := c.targetData.TypeAllocSize(c.i8ptrType)
|
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
||||||
pointerAlignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||||
if objectSizeBytes < pointerSize {
|
if objectSizeBytes < pointerSize {
|
||||||
// Too small to contain a pointer.
|
// Too small to contain a pointer.
|
||||||
layout := (uint64(1) << 1) | 1
|
layout := (uint64(1) << 1) | 1
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
}
|
}
|
||||||
bitmap := c.getPointerBitmap(t, pos)
|
bitmap := c.getPointerBitmap(t, pos)
|
||||||
if bitmap.BitLen() == 0 {
|
if bitmap.BitLen() == 0 {
|
||||||
@@ -266,13 +261,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
|
// TODO: this can be done in many other cases, e.g. when allocating an
|
||||||
// array (like [4][]byte, which repeats a slice 4 times).
|
// array (like [4][]byte, which repeats a slice 4 times).
|
||||||
layout := (uint64(1) << 1) | 1
|
layout := (uint64(1) << 1) | 1
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
}
|
}
|
||||||
if objectSizeBytes%uint64(pointerAlignment) != 0 {
|
if objectSizeBytes%uint64(pointerAlignment) != 0 {
|
||||||
// This shouldn't happen except for packed structs, which aren't
|
// This shouldn't happen except for packed structs, which aren't
|
||||||
// currently used.
|
// currently used.
|
||||||
c.addError(pos, "internal error: unexpected object size for object with pointer field")
|
c.addError(pos, "internal error: unexpected object size for object with pointer field")
|
||||||
return llvm.ConstNull(c.i8ptrType)
|
return llvm.ConstNull(c.dataPtrType)
|
||||||
}
|
}
|
||||||
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
||||||
|
|
||||||
@@ -297,7 +292,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
|
// The runtime knows that if the least significant bit of the pointer is
|
||||||
// set, the pointer contains the value itself.
|
// set, the pointer contains the value itself.
|
||||||
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
|
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType)
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
||||||
@@ -308,7 +303,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)
|
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
|
||||||
global := c.mod.NamedGlobal(globalName)
|
global := c.mod.NamedGlobal(globalName)
|
||||||
if !global.IsNil() {
|
if !global.IsNil() {
|
||||||
return llvm.ConstBitCast(global, c.i8ptrType)
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the global initializer.
|
// Create the global initializer.
|
||||||
@@ -359,13 +354,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
global.AddMetadata(0, diglobal)
|
global.AddMetadata(0, diglobal)
|
||||||
}
|
}
|
||||||
|
|
||||||
return llvm.ConstBitCast(global, c.i8ptrType)
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||||
// bigint at the word offset that contains a pointer. This scan is recursive.
|
// bigint at the word offset that contains a pointer. This scan is recursive.
|
||||||
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
|
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
|
||||||
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
||||||
switch typ.TypeKind() {
|
switch typ.TypeKind() {
|
||||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||||
return big.NewInt(0)
|
return big.NewInt(0)
|
||||||
@@ -378,7 +373,7 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In
|
|||||||
// of type uintptr, but before the LowerFuncValues pass it actually
|
// of type uintptr, but before the LowerFuncValues pass it actually
|
||||||
// contains a pointer (ptrtoint) to a global. This trips up the
|
// contains a pointer (ptrtoint) to a global. This trips up the
|
||||||
// interp package. Therefore, make the id field a pointer for now.
|
// interp package. Therefore, make the id field a pointer for now.
|
||||||
typ = c.ctx.StructType([]llvm.Type{c.i8ptrType, c.i8ptrType}, false)
|
typ = c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false)
|
||||||
}
|
}
|
||||||
for i, subtyp := range typ.StructElementTypes() {
|
for i, subtyp := range typ.StructElementTypes() {
|
||||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
subptrs := c.getPointerBitmap(subtyp, pos)
|
||||||
@@ -458,7 +453,7 @@ func (c *compilerContext) isThumb() bool {
|
|||||||
func (b *builder) readStackPointer() llvm.Value {
|
func (b *builder) readStackPointer() llvm.Value {
|
||||||
stacksave := b.mod.NamedFunction("llvm.stacksave")
|
stacksave := b.mod.NamedFunction("llvm.stacksave")
|
||||||
if stacksave.IsNil() {
|
if stacksave.IsNil() {
|
||||||
fnType := llvm.FunctionType(b.i8ptrType, nil, false)
|
fnType := llvm.FunctionType(b.dataPtrType, nil, false)
|
||||||
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
|
stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType)
|
||||||
}
|
}
|
||||||
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
||||||
|
|||||||
+13
-34
@@ -8,22 +8,9 @@
|
|||||||
package llvmutil
|
package llvmutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"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
|
// CreateEntryBlockAlloca creates a new alloca in the entry block, even though
|
||||||
// the IR builder is located elsewhere. It assumes that the insert point is
|
// the IR builder is located elsewhere. It assumes that the insert point is
|
||||||
// at the end of the current block.
|
// at the end of the current block.
|
||||||
@@ -46,16 +33,14 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
|
|||||||
//
|
//
|
||||||
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
||||||
// end the lifetime using emitLifetimeEnd after you're done with it.
|
// 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, bitcast, size llvm.Value) {
|
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, size llvm.Value) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
defer targetData.Dispose()
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
|
||||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||||
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
|
||||||
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||||
fnType, fn := getLifetimeStartFunc(mod)
|
fnType, fn := getLifetimeStartFunc(mod)
|
||||||
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,21 +49,19 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type,
|
|||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||||
defer targetData.Dispose()
|
defer targetData.Dispose()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
|
||||||
|
|
||||||
alloca := CreateEntryBlockAlloca(builder, t, name)
|
alloca := CreateEntryBlockAlloca(builder, t, name)
|
||||||
builder.SetInsertPointBefore(inst)
|
builder.SetInsertPointBefore(inst)
|
||||||
bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
|
||||||
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
|
||||||
fnType, fn := getLifetimeStartFunc(mod)
|
fnType, fn := getLifetimeStartFunc(mod)
|
||||||
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||||
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
if next := llvm.NextInstruction(inst); !next.IsNil() {
|
||||||
builder.SetInsertPointBefore(next)
|
builder.SetInsertPointBefore(next)
|
||||||
} else {
|
} else {
|
||||||
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
builder.SetInsertPointAtEnd(inst.InstructionParent())
|
||||||
}
|
}
|
||||||
fnType, fn = getLifetimeEndFunc(mod)
|
fnType, fn = getLifetimeEndFunc(mod)
|
||||||
builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "")
|
builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "")
|
||||||
return alloca
|
return alloca
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,13 +77,10 @@ func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value
|
|||||||
// first if it doesn't exist yet.
|
// first if it doesn't exist yet.
|
||||||
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||||
fnName := "llvm.lifetime.start.p0"
|
fnName := "llvm.lifetime.start.p0"
|
||||||
if Major() < 15 { // compatibility with LLVM 14
|
|
||||||
fnName = "llvm.lifetime.start.p0i8"
|
|
||||||
}
|
|
||||||
fn := mod.NamedFunction(fnName)
|
fn := mod.NamedFunction(fnName)
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
@@ -111,13 +91,10 @@ func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
|||||||
// first if it doesn't exist yet.
|
// first if it doesn't exist yet.
|
||||||
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
|
||||||
fnName := "llvm.lifetime.end.p0"
|
fnName := "llvm.lifetime.end.p0"
|
||||||
if Major() < 15 {
|
|
||||||
fnName = "llvm.lifetime.end.p0i8"
|
|
||||||
}
|
|
||||||
fn := mod.NamedFunction(fnName)
|
fn := mod.NamedFunction(fnName)
|
||||||
ctx := mod.Context()
|
ctx := mod.Context()
|
||||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false)
|
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false)
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
fn = llvm.AddFunction(mod, fnName, fnType)
|
fn = llvm.AddFunction(mod, fnName, fnType)
|
||||||
}
|
}
|
||||||
@@ -213,13 +190,15 @@ func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the new values.
|
// Add the new values.
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
usedValues = append(usedValues, llvm.ConstPointerCast(value, i8ptrType))
|
// Note: the bitcast is necessary to cast AVR function pointers to
|
||||||
|
// address space 0 pointer types.
|
||||||
|
usedValues = append(usedValues, llvm.ConstPointerCast(value, ptrType))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new array (with the old and new values).
|
// Create a new array (with the old and new values).
|
||||||
usedInitializer := llvm.ConstArray(i8ptrType, usedValues)
|
usedInitializer := llvm.ConstArray(ptrType, usedValues)
|
||||||
used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
|
used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
|
||||||
used.SetInitializer(usedInitializer)
|
used.SetInitializer(usedInitializer)
|
||||||
used.SetLinkage(llvm.AppendingLinkage)
|
used.SetLinkage(llvm.AppendingLinkage)
|
||||||
|
|||||||
+23
-30
@@ -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
|
// 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
|
// will be zeroed by the hashmap get implementation if the key is not
|
||||||
// present in the map.
|
// present in the map.
|
||||||
mapValueAlloca, mapValuePtr, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
mapValueAlloca, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
||||||
|
|
||||||
// We need the map size (with type uintptr) to pass to the hashmap*Get
|
// 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
|
// 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()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, mapValuePtr, mapValueSize}
|
params := []llvm.Value{m, key, mapValueAlloca, mapValueSize}
|
||||||
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
||||||
// growth.
|
// growth.
|
||||||
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, mapKeyAlloca)
|
b.CreateStore(key, mapKeyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
|
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
|
||||||
// Fetch the value from the hashmap.
|
// Fetch the value from the hashmap.
|
||||||
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
|
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
|
||||||
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||||
b.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||||
} else {
|
} else {
|
||||||
// Not trivially comparable using memcmp. Make it an interface instead.
|
// Not trivially comparable using memcmp. Make it an interface instead.
|
||||||
itfKey := key
|
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.
|
// Not already an interface, so convert it to an interface now.
|
||||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey, mapValuePtr, mapValueSize}
|
params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize}
|
||||||
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the resulting value from the hashmap. The value is set to the zero
|
// Load the resulting value from the hashmap. The value is set to the zero
|
||||||
// value if the key doesn't exist in the hashmap.
|
// value if the key doesn't exist in the hashmap.
|
||||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
||||||
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
|
b.emitLifetimeEnd(mapValueAlloca, mapValueAllocaSize)
|
||||||
|
|
||||||
if commaOk {
|
if commaOk {
|
||||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false))
|
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
|
// createMapUpdate updates a map key to a given value, by creating an
|
||||||
// appropriate runtime call.
|
// appropriate runtime call.
|
||||||
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||||
valueAlloca, valuePtr, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
|
valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
|
||||||
b.CreateStore(value, valueAlloca)
|
b.CreateStore(value, valueAlloca)
|
||||||
origKeyType := keyType
|
origKeyType := keyType
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, valuePtr}
|
params := []llvm.Value{m, key, valueAlloca}
|
||||||
b.createRuntimeCall("hashmapStringSet", params, "")
|
b.createRuntimeCall("hashmapStringSet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
||||||
params := []llvm.Value{m, keyPtr, valuePtr}
|
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
||||||
b.createRuntimeCall("hashmapBinarySet", params, "")
|
b.createRuntimeCall("hashmapBinarySet", params, "")
|
||||||
b.emitLifetimeEnd(keyPtr, keySize)
|
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||||
} else {
|
} else {
|
||||||
// Key is not trivially comparable, so compare it as an interface instead.
|
// Key is not trivially comparable, so compare it as an interface instead.
|
||||||
itfKey := key
|
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.
|
// Not already an interface, so convert it to an interface first.
|
||||||
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
itfKey = b.createMakeInterface(key, origKeyType, pos)
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey, valuePtr}
|
params := []llvm.Value{m, itfKey, valueAlloca}
|
||||||
b.createRuntimeCall("hashmapInterfaceSet", params, "")
|
b.createRuntimeCall("hashmapInterfaceSet", params, "")
|
||||||
}
|
}
|
||||||
b.emitLifetimeEnd(valuePtr, valueSize)
|
b.emitLifetimeEnd(valueAlloca, valueSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMapDelete deletes a key from a map by calling the appropriate runtime
|
// 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, "")
|
b.createRuntimeCall("hashmapStringDelete", params, "")
|
||||||
return nil
|
return nil
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
||||||
params := []llvm.Value{m, keyPtr}
|
params := []llvm.Value{m, keyAlloca}
|
||||||
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||||
b.emitLifetimeEnd(keyPtr, keySize)
|
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
// Key is not trivially comparable, so compare it as an interface
|
// 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.
|
// Extract the key and value from the map.
|
||||||
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key")
|
||||||
mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value")
|
||||||
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
|
ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next")
|
||||||
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
|
mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "")
|
||||||
mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "")
|
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.
|
// End the lifetimes of the allocas, because we're done with them.
|
||||||
b.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
|
||||||
b.emitLifetimeEnd(mapValuePtr, mapValueSize)
|
b.emitLifetimeEnd(mapValueAlloca, mapValueSize)
|
||||||
|
|
||||||
// Construct the *ssa.Next return value: {ok, mapKey, mapValue}
|
// Construct the *ssa.Next return value: {ok, mapKey, mapValue}
|
||||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false))
|
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false))
|
||||||
@@ -333,14 +333,7 @@ func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
|
|||||||
if fieldEndOffset != nextOffset {
|
if fieldEndOffset != nextOffset {
|
||||||
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
|
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
|
||||||
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
|
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
|
||||||
gepPtr := elemPtr
|
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "")
|
||||||
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}, "")
|
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-24
@@ -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
|
// 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.
|
// closures and bound methods, but should be optimized away when not used.
|
||||||
if !info.exported {
|
if !info.exported {
|
||||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", elemSize: 0})
|
paramInfos = append(paramInfos, paramInfo{llvmType: c.dataPtrType, name: "context", elemSize: 0})
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramTypes []llvm.Type
|
var paramTypes []llvm.Type
|
||||||
@@ -145,20 +145,18 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
for _, attrName := range []string{"noalias", "nonnull"} {
|
for _, attrName := range []string{"noalias", "nonnull"} {
|
||||||
llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0))
|
llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0))
|
||||||
}
|
}
|
||||||
if llvmutil.Major() >= 15 { // allockind etc are not available in LLVM 14
|
// Add attributes to signal to LLVM that this is an allocator function.
|
||||||
// Add attributes to signal to LLVM that this is an allocator
|
// This enables a number of optimizations.
|
||||||
// function. This enables a number of optimizations.
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed))
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed))
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc"))
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc"))
|
// Use a special value to indicate the first parameter:
|
||||||
// Use a special value to indicate the first parameter:
|
// > allocsize has two integer arguments, but because they're both 32 bits, we can
|
||||||
// > 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
|
||||||
// > pack them into one 64-bit value, at the cost of making said value
|
// > nonsensical.
|
||||||
// > nonsensical.
|
// >
|
||||||
// >
|
// > In order to do this, we need to reserve one value of the second (optional)
|
||||||
// > In order to do this, we need to reserve one value of the second (optional)
|
// > allocsize argument to signify "not present."
|
||||||
// > allocsize argument to signify "not present."
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff))
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff))
|
|
||||||
}
|
|
||||||
case "runtime.sliceAppend":
|
case "runtime.sliceAppend":
|
||||||
// Appending a slice will only read the to-be-appended slice, it won't
|
// Appending a slice will only read the to-be-appended slice, it won't
|
||||||
// be modified.
|
// be modified.
|
||||||
@@ -445,15 +443,10 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) {
|
|||||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0))
|
||||||
if strings.Split(c.Triple, "-")[0] == "x86_64" {
|
if strings.Split(c.Triple, "-")[0] == "x86_64" {
|
||||||
// Required by the ABI.
|
// Required by the ABI.
|
||||||
if llvmutil.Major() < 15 {
|
// The uwtable has two possible values: sync (1) or async (2). We use
|
||||||
// Needed for LLVM 14 support.
|
// sync because we currently don't use async unwind tables.
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0))
|
// For details, see: https://llvm.org/docs/LangRef.html#function-attributes
|
||||||
} else {
|
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1))
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -183,7 +183,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false)
|
llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false)
|
||||||
fn := b.getValue(call.Args[0], getPos(call))
|
fn := b.getValue(call.Args[0], getPos(call))
|
||||||
fnPtr := b.CreateIntToPtr(fn, llvm.PointerType(llvmType, 0), "")
|
fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "")
|
||||||
|
|
||||||
// Prepare some functions that will be called later.
|
// Prepare some functions that will be called later.
|
||||||
setLastError := b.mod.NamedFunction("SetLastError")
|
setLastError := b.mod.NamedFunction("SetLastError")
|
||||||
|
|||||||
+5
-5
@@ -21,7 +21,7 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, 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 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ entry:
|
|||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
||||||
store ptr %n, ptr %1, align 4
|
store ptr %n, ptr %1, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||||
%2 = load i32, ptr %n, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
call void @runtime.printint32(i32 %2, ptr undef) #9
|
call void @runtime.printint32(i32 %2, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
@@ -113,7 +113,7 @@ entry:
|
|||||||
store ptr %fn.context, ptr %1, align 4
|
store ptr %fn.context, ptr %1, align 4
|
||||||
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
||||||
store ptr %fn.funcptr, ptr %2, align 4
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 32768, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ entry:
|
|||||||
store i32 4, ptr %.repack1, align 4
|
store i32 4, ptr %.repack1, align 4
|
||||||
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
|
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
|
||||||
store ptr %itf.typecode, ptr %2, align 4
|
store ptr %itf.typecode, ptr %2, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 32768, 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 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
|||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
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 " html to make standalone HTML files"
|
||||||
@echo " dirhtml to make HTML files named index.html in directories"
|
@echo " dirhtml to make HTML files named index.html in directories"
|
||||||
@echo " singlehtml to make a single large HTML file"
|
@echo " singlehtml to make a single large HTML file"
|
||||||
Generated
+60
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# 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 --recursive # 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
|
||||||
|
llvmPackages_17.libcxx
|
||||||
|
# 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"\"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -15,10 +15,11 @@ require (
|
|||||||
github.com/mattn/go-tty v0.0.4
|
github.com/mattn/go-tty v0.0.4
|
||||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
|
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
|
||||||
go.bug.st/serial v1.6.0
|
go.bug.st/serial v1.6.0
|
||||||
golang.org/x/sys v0.11.0
|
golang.org/x/net v0.20.0
|
||||||
golang.org/x/tools v0.12.0
|
golang.org/x/sys v0.16.0
|
||||||
|
golang.org/x/tools v0.17.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc
|
tinygo.org/x/go-llvm v0.0.0-20240106122909-c2c543540318
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -30,4 +31,5 @@ require (
|
|||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9
|
|||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
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 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
|
||||||
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
|
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||||
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-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-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -56,14 +58,16 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||||
|
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
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-20240106122909-c2c543540318 h1:4KjZvPtcN1UwobevcGbdzeinx0L1i8HDdJu84bu7NI8=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
tinygo.org/x/go-llvm v0.0.0-20240106122909-c2c543540318/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||||
|
|||||||
+105
-5
@@ -14,6 +14,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Keys is a slice of all available environment variable keys.
|
// Keys is a slice of all available environment variable keys.
|
||||||
@@ -33,11 +35,20 @@ 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
|
// 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
|
// unset (by a -X ldflag), then sourceDir() will fallback to the original build
|
||||||
// directory.
|
// directory.
|
||||||
var TINYGOROOT string
|
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.
|
// Variables read from a `go env` command invocation.
|
||||||
var goEnvVars struct {
|
var goEnvVars struct {
|
||||||
GOPATH string
|
GOPATH string
|
||||||
@@ -131,11 +142,8 @@ func Get(name string) string {
|
|||||||
}
|
}
|
||||||
return filepath.Join(dir, "tinygo")
|
return filepath.Join(dir, "tinygo")
|
||||||
case "CGO_ENABLED":
|
case "CGO_ENABLED":
|
||||||
val := os.Getenv("CGO_ENABLED")
|
// Always enable CGo. It is required by a number of targets, including
|
||||||
if val == "1" || val == "0" {
|
// macOS and the rp2040.
|
||||||
return val
|
|
||||||
}
|
|
||||||
// Default to enabling CGo.
|
|
||||||
return "1"
|
return "1"
|
||||||
case "TINYGOROOT":
|
case "TINYGOROOT":
|
||||||
return sourceDir()
|
return sourceDir()
|
||||||
@@ -284,3 +292,95 @@ func isSourceDir(root string) bool {
|
|||||||
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
|
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
|
||||||
return err == nil
|
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 ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build byollvm
|
||||||
|
|
||||||
|
package goenv
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
hasBuiltinTools = true
|
||||||
|
}
|
||||||
+11
-1
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// Version of TinyGo.
|
// Version of TinyGo.
|
||||||
// Update this value before release of new version of software.
|
// Update this value before release of new version of software.
|
||||||
const Version = "0.30.0"
|
const version = "0.31.2"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// This variable is set at build time using -ldflags parameters.
|
// This variable is set at build time using -ldflags parameters.
|
||||||
@@ -17,6 +17,16 @@ var (
|
|||||||
GitSha1 string
|
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.
|
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||||
// If the goroot cannot be determined, (0, 0) is returned.
|
// If the goroot cannot be determined, (0, 0) is returned.
|
||||||
func GetGorootVersion() (major, minor int, err error) {
|
func GetGorootVersion() (major, minor int, err error) {
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Docker hub does a recursive clone, then checks the branch out,
|
# Docker hub does a recursive clone, then checks the branch out,
|
||||||
# so when a PR adds a submodule (or updates it), it fails.
|
# so when a PR adds a submodule (or updates it), it fails.
|
||||||
git submodule update --init
|
git submodule update --init --recursive
|
||||||
|
|||||||
+3
-4
@@ -19,7 +19,6 @@ var (
|
|||||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||||
errLoopUnrolled = errors.New("interp: loop unrolled")
|
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||||
errDepthExceeded = errors.New("interp: depth exceeded")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is one of the errors that can be returned from toLLVMValue when the
|
// This is one of the errors that can be returned from toLLVMValue when the
|
||||||
@@ -30,7 +29,7 @@ var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not
|
|||||||
func isRecoverableError(err error) bool {
|
func isRecoverableError(err error) bool {
|
||||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||||
err == errLoopUnrolled || err == errDepthExceeded
|
err == errLoopUnrolled
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorLine is one line in a traceback. The position may be missing.
|
// ErrorLine is one line in a traceback. The position may be missing.
|
||||||
@@ -61,10 +60,10 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
|
|||||||
pos := getPosition(inst.llvmInst)
|
pos := getPosition(inst.llvmInst)
|
||||||
return &Error{
|
return &Error{
|
||||||
ImportPath: r.pkgName,
|
ImportPath: r.pkgName,
|
||||||
Inst: inst.String(),
|
Inst: inst.llvmInst.String(),
|
||||||
Pos: pos,
|
Pos: pos,
|
||||||
Err: err,
|
Err: err,
|
||||||
Traceback: []ErrorLine{{pos, inst.String()}},
|
Traceback: []ErrorLine{{pos, inst.llvmInst.String()}},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-17
@@ -21,7 +21,7 @@ type runner struct {
|
|||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
builder llvm.Builder
|
builder llvm.Builder
|
||||||
pointerSize uint32 // cached pointer size from the TargetData
|
pointerSize uint32 // cached pointer size from the TargetData
|
||||||
i8ptrType llvm.Type // often used type so created in advance
|
dataPtrType llvm.Type // often used type so created in advance
|
||||||
uintptrType llvm.Type // equivalent to uintptr in Go
|
uintptrType llvm.Type // equivalent to uintptr in Go
|
||||||
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
|
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
|
||||||
debug bool // log debug messages
|
debug bool // log debug messages
|
||||||
@@ -31,12 +31,10 @@ type runner struct {
|
|||||||
globals map[llvm.Value]int // map from global to index in objects slice
|
globals map[llvm.Value]int // map from global to index in objects slice
|
||||||
start time.Time
|
start time.Time
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
maxDepth int
|
|
||||||
maxInstr int
|
|
||||||
callsExecuted uint64
|
callsExecuted uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRunner(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr int, debug bool) *runner {
|
func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
|
||||||
r := runner{
|
r := runner{
|
||||||
mod: mod,
|
mod: mod,
|
||||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||||
@@ -46,13 +44,11 @@ func newRunner(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr in
|
|||||||
globals: make(map[llvm.Value]int),
|
globals: make(map[llvm.Value]int),
|
||||||
start: time.Now(),
|
start: time.Now(),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
maxDepth: maxDepth,
|
|
||||||
maxInstr: maxInstr,
|
|
||||||
}
|
}
|
||||||
r.pointerSize = uint32(r.targetData.PointerSize())
|
r.pointerSize = uint32(r.targetData.PointerSize())
|
||||||
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
r.dataPtrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
r.uintptrType = mod.Context().IntType(r.targetData.PointerSize() * 8)
|
r.uintptrType = mod.Context().IntType(r.targetData.PointerSize() * 8)
|
||||||
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
|
r.maxAlign = r.targetData.PrefTypeAlignment(r.dataPtrType) // assume pointers are maximally aligned (this is not always the case)
|
||||||
return &r
|
return &r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +60,8 @@ func (r *runner) dispose() {
|
|||||||
|
|
||||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||||
// Set debug to true if it should print output while running.
|
// Set debug to true if it should print output while running.
|
||||||
func Run(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr int, debug bool) error {
|
func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
||||||
r := newRunner(mod, timeout, maxDepth, maxInstr, debug)
|
r := newRunner(mod, timeout, debug)
|
||||||
defer r.dispose()
|
defer r.dispose()
|
||||||
|
|
||||||
initAll := mod.NamedFunction("runtime.initAll")
|
initAll := mod.NamedFunction("runtime.initAll")
|
||||||
@@ -118,7 +114,7 @@ func Run(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr int, deb
|
|||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
||||||
}
|
}
|
||||||
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, 0, " ")
|
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||||
call.EraseFromParentAsInstruction()
|
call.EraseFromParentAsInstruction()
|
||||||
if callErr != nil {
|
if callErr != nil {
|
||||||
if isRecoverableError(callErr.Err) {
|
if isRecoverableError(callErr.Err) {
|
||||||
@@ -130,7 +126,7 @@ func Run(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr int, deb
|
|||||||
mem.revert()
|
mem.revert()
|
||||||
// Create a call to the package initializer (which was
|
// Create a call to the package initializer (which was
|
||||||
// previously deleted).
|
// previously deleted).
|
||||||
i8undef := llvm.Undef(r.i8ptrType)
|
i8undef := llvm.Undef(r.dataPtrType)
|
||||||
r.builder.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{i8undef}, "")
|
r.builder.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{i8undef}, "")
|
||||||
// Make sure that any globals touched by the package
|
// Make sure that any globals touched by the package
|
||||||
// initializer, won't be accessed by later package initializers.
|
// initializer, won't be accessed by later package initializers.
|
||||||
@@ -178,8 +174,7 @@ func Run(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr int, deb
|
|||||||
newGlobal.SetLinkage(obj.llvmGlobal.Linkage())
|
newGlobal.SetLinkage(obj.llvmGlobal.Linkage())
|
||||||
newGlobal.SetAlignment(obj.llvmGlobal.Alignment())
|
newGlobal.SetAlignment(obj.llvmGlobal.Alignment())
|
||||||
// TODO: copy debug info, unnamed_addr, ...
|
// TODO: copy debug info, unnamed_addr, ...
|
||||||
bitcast := llvm.ConstBitCast(newGlobal, obj.llvmGlobal.Type())
|
obj.llvmGlobal.ReplaceAllUsesWith(newGlobal)
|
||||||
obj.llvmGlobal.ReplaceAllUsesWith(bitcast)
|
|
||||||
name := obj.llvmGlobal.Name()
|
name := obj.llvmGlobal.Name()
|
||||||
obj.llvmGlobal.EraseFromParentAsGlobal()
|
obj.llvmGlobal.EraseFromParentAsGlobal()
|
||||||
newGlobal.SetName(name)
|
newGlobal.SetName(name)
|
||||||
@@ -205,10 +200,10 @@ func Run(mod llvm.Module, timeout time.Duration, maxDepth int, maxInstr int, deb
|
|||||||
|
|
||||||
// RunFunc evaluates a single package initializer at compile time.
|
// RunFunc evaluates a single package initializer at compile time.
|
||||||
// Set debug to true if it should print output while running.
|
// Set debug to true if it should print output while running.
|
||||||
func RunFunc(fn llvm.Value, timeout time.Duration, maxDepth int, maxInstr int, debug bool) error {
|
func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
|
||||||
// Create and initialize *runner object.
|
// Create and initialize *runner object.
|
||||||
mod := fn.GlobalParent()
|
mod := fn.GlobalParent()
|
||||||
r := newRunner(mod, timeout, maxDepth, maxInstr, debug)
|
r := newRunner(mod, timeout, debug)
|
||||||
defer r.dispose()
|
defer r.dispose()
|
||||||
initName := fn.Name()
|
initName := fn.Name()
|
||||||
if !strings.HasSuffix(initName, ".init") {
|
if !strings.HasSuffix(initName, ".init") {
|
||||||
@@ -239,7 +234,7 @@ func RunFunc(fn llvm.Value, timeout time.Duration, maxDepth int, maxInstr int, d
|
|||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, "interp:", fn.Name())
|
fmt.Fprintln(os.Stderr, "interp:", fn.Name())
|
||||||
}
|
}
|
||||||
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, 0, " ")
|
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||||
if callErr != nil {
|
if callErr != nil {
|
||||||
if isRecoverableError(callErr.Err) {
|
if isRecoverableError(callErr.Err) {
|
||||||
// Could not finish, but could recover from it.
|
// Could not finish, but could recover from it.
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func runTest(t *testing.T, pathPrefix string) {
|
|||||||
defer mod.Dispose()
|
defer mod.Dispose()
|
||||||
|
|
||||||
// Perform the transform.
|
// Perform the transform.
|
||||||
err = Run(mod, 10*time.Minute, 10, 0, false)
|
err = Run(mod, 10*time.Minute, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, match := err.(*Error); match {
|
if err, match := err.(*Error); match {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
@@ -77,12 +77,9 @@ func runTest(t *testing.T, pathPrefix string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run some cleanup passes to get easy-to-read outputs.
|
// Run some cleanup passes to get easy-to-read outputs.
|
||||||
pm := llvm.NewPassManager()
|
to := llvm.NewPassBuilderOptions()
|
||||||
defer pm.Dispose()
|
defer to.Dispose()
|
||||||
pm.AddGlobalOptimizerPass()
|
mod.RunPasses("globalopt,dse,adce", llvm.TargetMachine{}, to)
|
||||||
pm.AddDeadStoreEliminationPass()
|
|
||||||
pm.AddAggressiveDCEPass()
|
|
||||||
pm.Run(mod)
|
|
||||||
|
|
||||||
// Read the expected output IR.
|
// Read the expected output IR.
|
||||||
out, err := os.ReadFile(pathPrefix + ".out.ll")
|
out, err := os.ReadFile(pathPrefix + ".out.ll")
|
||||||
|
|||||||
+30
-37
@@ -12,7 +12,7 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth int, indent string) (value, memoryView, *Error) {
|
func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent string) (value, memoryView, *Error) {
|
||||||
mem := memoryView{r: r, parent: parentMem}
|
mem := memoryView{r: r, parent: parentMem}
|
||||||
locals := make([]value, len(fn.locals))
|
locals := make([]value, len(fn.locals))
|
||||||
r.callsExecuted++
|
r.callsExecuted++
|
||||||
@@ -33,20 +33,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||||
var operands []value
|
var operands []value
|
||||||
startRTInsts := len(mem.instructions)
|
startRTInsts := len(mem.instructions)
|
||||||
instCount := 0
|
|
||||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||||
instCount++
|
|
||||||
if r.maxInstr > 0 && instCount > r.maxInstr {
|
|
||||||
fmt.Printf("interp: excess instructions evaluated in %v\n", fn.name)
|
|
||||||
return nil, mem, r.errorAt(fn.blocks[0].instructions[0], errDepthExceeded)
|
|
||||||
}
|
|
||||||
|
|
||||||
if instIndex == 0 {
|
if instIndex == 0 {
|
||||||
if r.maxDepth > 0 && depth >= r.maxDepth {
|
|
||||||
fmt.Printf("interp: depth exceeded in %v\n", fn.name)
|
|
||||||
return nil, mem, r.errorAt(fn.blocks[0].instructions[0], errDepthExceeded)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the start of a new basic block.
|
// This is the start of a new basic block.
|
||||||
if len(mem.instructions) != startRTInsts {
|
if len(mem.instructions) != startRTInsts {
|
||||||
if _, ok := runtimeBlocks[lastBB]; ok {
|
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||||
@@ -251,7 +239,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
// already be emitted in initAll.
|
// already be emitted in initAll.
|
||||||
continue
|
continue
|
||||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "runtime.hashmapInterfaceHash" ||
|
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "runtime.hashmapInterfaceHash" ||
|
||||||
callFn.name == "os.runtime_args" || callFn.name == "internal/task.start" || callFn.name == "internal/task.Current":
|
callFn.name == "os.runtime_args" || callFn.name == "internal/task.start" || callFn.name == "internal/task.Current" ||
|
||||||
|
callFn.name == "time.startTimer" || callFn.name == "time.stopTimer" || callFn.name == "time.resetTimer":
|
||||||
// These functions should be run at runtime. Specifically:
|
// These functions should be run at runtime. Specifically:
|
||||||
// * Print and panic functions are best emitted directly without
|
// * Print and panic functions are best emitted directly without
|
||||||
// interpreting them, otherwise we get a ton of putchar (etc.)
|
// interpreting them, otherwise we get a ton of putchar (etc.)
|
||||||
@@ -264,6 +253,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
// at runtime.
|
// at runtime.
|
||||||
// * internal/task.start, internal/task.Current: start and read shcheduler state,
|
// * internal/task.start, internal/task.Current: start and read shcheduler state,
|
||||||
// which is modified elsewhere.
|
// which is modified elsewhere.
|
||||||
|
// * Timer functions access runtime internal state which may
|
||||||
|
// not be initialized.
|
||||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, err
|
return nil, mem, err
|
||||||
@@ -436,7 +427,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
methodSetPtr, err := mem.load(typecodePtr.addOffset(-int64(r.pointerSize)), r.pointerSize).asPointer(r)
|
typecodePtrOffset, err := typecodePtr.addOffset(-int64(r.pointerSize))
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err) // unlikely
|
||||||
|
}
|
||||||
|
methodSetPtr, err := mem.load(typecodePtrOffset, r.pointerSize).asPointer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
@@ -482,7 +477,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
methodSetPtr, err := mem.load(typecodePtr.addOffset(-int64(r.pointerSize)), r.pointerSize).asPointer(r)
|
typecodePtrOffset, err := typecodePtr.addOffset(-int64(r.pointerSize))
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
methodSetPtr, err := mem.load(typecodePtrOffset, r.pointerSize).asPointer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
@@ -535,7 +534,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, indent+"call:", callFn.name+"("+strings.Join(argStrings, ", ")+")")
|
fmt.Fprintln(os.Stderr, indent+"call:", callFn.name+"("+strings.Join(argStrings, ", ")+")")
|
||||||
}
|
}
|
||||||
retval, callMem, callErr := r.run(callFn, operands[1:], &mem, depth+1, indent+" ")
|
retval, callMem, callErr := r.run(callFn, operands[1:], &mem, indent+" ")
|
||||||
if callErr != nil {
|
if callErr != nil {
|
||||||
if isRecoverableError(callErr.Err) {
|
if isRecoverableError(callErr.Err) {
|
||||||
// This error can be recovered by doing the call at
|
// This error can be recovered by doing the call at
|
||||||
@@ -555,7 +554,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
// how this function got called.
|
// how this function got called.
|
||||||
callErr.Traceback = append(callErr.Traceback, ErrorLine{
|
callErr.Traceback = append(callErr.Traceback, ErrorLine{
|
||||||
Pos: getPosition(inst.llvmInst),
|
Pos: getPosition(inst.llvmInst),
|
||||||
Inst: inst.String(),
|
Inst: inst.llvmInst.String(),
|
||||||
})
|
})
|
||||||
return nil, mem, callErr
|
return nil, mem, callErr
|
||||||
}
|
}
|
||||||
@@ -645,11 +644,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
case llvm.GetElementPtr:
|
case llvm.GetElementPtr:
|
||||||
// GetElementPtr does pointer arithmetic, changing the offset of the
|
// GetElementPtr does pointer arithmetic, changing the offset of the
|
||||||
// pointer into the underlying object.
|
// pointer into the underlying object.
|
||||||
var offset uint64
|
var offset int64
|
||||||
for i := 1; i < len(operands); i += 2 {
|
for i := 1; i < len(operands); i += 2 {
|
||||||
index := operands[i].Uint()
|
index := operands[i].Int()
|
||||||
elementSize := operands[i+1].Uint()
|
elementSize := operands[i+1].Int()
|
||||||
if int64(elementSize) < 0 {
|
if elementSize < 0 {
|
||||||
// This is a struct field.
|
// This is a struct field.
|
||||||
offset += index
|
offset += index
|
||||||
} else {
|
} else {
|
||||||
@@ -663,11 +662,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
// GEP on fixed pointer value (for example, memory-mapped I/O).
|
// GEP on fixed pointer value (for example, memory-mapped I/O).
|
||||||
ptrValue := operands[0].Uint() + offset
|
ptrValue := operands[0].Uint() + uint64(offset)
|
||||||
locals[inst.localIndex] = makeLiteralInt(ptrValue, int(operands[0].len(r)*8))
|
locals[inst.localIndex] = makeLiteralInt(ptrValue, int(operands[0].len(r)*8))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ptr = ptr.addOffset(int64(offset))
|
ptr, err = ptr.addOffset(int64(offset))
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
locals[inst.localIndex] = ptr
|
locals[inst.localIndex] = ptr
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr)
|
fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr)
|
||||||
@@ -765,7 +767,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth
|
|||||||
if inst.opcode == llvm.Add {
|
if inst.opcode == llvm.Add {
|
||||||
// This likely means this is part of a
|
// This likely means this is part of a
|
||||||
// unsafe.Pointer(uintptr(ptr) + offset) pattern.
|
// unsafe.Pointer(uintptr(ptr) + offset) pattern.
|
||||||
lhsPtr = lhsPtr.addOffset(int64(rhs.Uint()))
|
lhsPtr, err = lhsPtr.addOffset(int64(rhs.Uint()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, r.errorAt(inst, err)
|
||||||
|
}
|
||||||
locals[inst.localIndex] = lhsPtr
|
locals[inst.localIndex] = lhsPtr
|
||||||
} else if inst.opcode == llvm.Xor && rhs.Uint() == 0 {
|
} else if inst.opcode == llvm.Xor && rhs.Uint() == 0 {
|
||||||
// Special workaround for strings.noescape, see
|
// Special workaround for strings.noescape, see
|
||||||
@@ -1058,15 +1063,3 @@ func intPredicateString(predicate llvm.IntPredicate) string {
|
|||||||
return "cmp?"
|
return "cmp?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip some pointer casts. This is probably unnecessary once support for
|
|
||||||
// LLVM 14 (non-opaque pointers) is dropped.
|
|
||||||
func stripPointerCasts(value llvm.Value) llvm.Value {
|
|
||||||
if !value.IsAConstantExpr().IsNil() {
|
|
||||||
switch value.Opcode() {
|
|
||||||
case llvm.GetElementPtr, llvm.BitCast:
|
|
||||||
return stripPointerCasts(value.Operand(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|||||||
+7
-18
@@ -517,12 +517,12 @@ func (v pointerValue) offset() uint32 {
|
|||||||
// addOffset essentially does a GEP operation (pointer arithmetic): it adds the
|
// addOffset essentially does a GEP operation (pointer arithmetic): it adds the
|
||||||
// offset to the pointer. It also checks that the offset doesn't overflow the
|
// offset to the pointer. It also checks that the offset doesn't overflow the
|
||||||
// maximum offset size (which is 4GB).
|
// maximum offset size (which is 4GB).
|
||||||
func (v pointerValue) addOffset(offset int64) pointerValue {
|
func (v pointerValue) addOffset(offset int64) (pointerValue, error) {
|
||||||
result := pointerValue{v.pointer + uint64(offset)}
|
result := pointerValue{v.pointer + uint64(offset)}
|
||||||
if checks && v.index() != result.index() {
|
if checks && v.index() != result.index() {
|
||||||
panic("interp: offset out of range")
|
return result, fmt.Errorf("interp: offset %d out of range for object %v", offset, v)
|
||||||
}
|
}
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v pointerValue) len(r *runner) uint32 {
|
func (v pointerValue) len(r *runner) uint32 {
|
||||||
@@ -658,20 +658,11 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
if v.offset() != 0 {
|
if v.offset() != 0 {
|
||||||
// If there is an offset, make sure to use a GEP to index into the
|
// If there is an offset, make sure to use a GEP to index into the
|
||||||
// pointer.
|
// pointer.
|
||||||
// Cast to an i8* first (if needed) for easy indexing.
|
|
||||||
if llvmValue.Type() != mem.r.i8ptrType {
|
|
||||||
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
|
|
||||||
}
|
|
||||||
llvmValue = llvm.ConstInBoundsGEP(mem.r.mod.Context().Int8Type(), llvmValue, []llvm.Value{
|
llvmValue = llvm.ConstInBoundsGEP(mem.r.mod.Context().Int8Type(), llvmValue, []llvm.Value{
|
||||||
llvm.ConstInt(mem.r.mod.Context().Int32Type(), uint64(v.offset()), false),
|
llvm.ConstInt(mem.r.mod.Context().Int32Type(), uint64(v.offset()), false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a particular LLVM pointer type is requested, cast to it.
|
|
||||||
if !llvmType.IsNil() && llvmType != llvmValue.Type() {
|
|
||||||
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
|
|
||||||
}
|
|
||||||
|
|
||||||
return llvmValue, nil
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,7 +863,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.i8ptrType) {
|
if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.dataPtrType) {
|
||||||
// Probably trying to serialize a pointer to a byte array,
|
// Probably trying to serialize a pointer to a byte array,
|
||||||
// perhaps as a result of rawLLVMValue() in a previous interp
|
// perhaps as a result of rawLLVMValue() in a previous interp
|
||||||
// run.
|
// run.
|
||||||
@@ -954,8 +945,6 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
|
|||||||
// Because go-llvm doesn't have addrspacecast at the moment,
|
// Because go-llvm doesn't have addrspacecast at the moment,
|
||||||
// do it indirectly with a ptrtoint/inttoptr pair.
|
// do it indirectly with a ptrtoint/inttoptr pair.
|
||||||
llvmValue = llvm.ConstIntToPtr(llvm.ConstPtrToInt(llvmValue, mem.r.uintptrType), llvmType)
|
llvmValue = llvm.ConstIntToPtr(llvm.ConstPtrToInt(llvmValue, mem.r.uintptrType), llvmType)
|
||||||
} else {
|
|
||||||
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return llvmValue, nil
|
return llvmValue, nil
|
||||||
@@ -1256,7 +1245,7 @@ func (r *runner) getValue(llvmValue llvm.Value) value {
|
|||||||
// For details on this format, see src/runtime/gc_precise.go.
|
// For details on this format, see src/runtime/gc_precise.go.
|
||||||
func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
|
func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
|
||||||
pointerSize := layoutValue.len(r)
|
pointerSize := layoutValue.len(r)
|
||||||
if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.i8ptrType) {
|
if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.dataPtrType) {
|
||||||
panic("inconsistent pointer size")
|
panic("inconsistent pointer size")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1331,12 +1320,12 @@ func (r *runner) getLLVMTypeFromLayout(layoutValue value) llvm.Type {
|
|||||||
|
|
||||||
// Create the LLVM type.
|
// Create the LLVM type.
|
||||||
pointerSize := layoutValue.len(r)
|
pointerSize := layoutValue.len(r)
|
||||||
pointerAlignment := r.targetData.PrefTypeAlignment(r.i8ptrType)
|
pointerAlignment := r.targetData.PrefTypeAlignment(r.dataPtrType)
|
||||||
var fields []llvm.Type
|
var fields []llvm.Type
|
||||||
for i := 0; i < int(objectSizeWords); {
|
for i := 0; i < int(objectSizeWords); {
|
||||||
if bitmap.Bit(i) != 0 {
|
if bitmap.Bit(i) != 0 {
|
||||||
// Pointer field.
|
// Pointer field.
|
||||||
fields = append(fields, r.i8ptrType)
|
fields = append(fields, r.dataPtrType)
|
||||||
i += int(pointerSize / uint32(pointerAlignment))
|
i += int(pointerSize / uint32(pointerAlignment))
|
||||||
} else {
|
} else {
|
||||||
// Byte/word field.
|
// Byte/word field.
|
||||||
|
|||||||
Vendored
+7
@@ -10,6 +10,8 @@ target triple = "x86_64--linux"
|
|||||||
@main.exposedValue1 = global i16 0
|
@main.exposedValue1 = global i16 0
|
||||||
@main.exposedValue2 = global i16 0
|
@main.exposedValue2 = global i16 0
|
||||||
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
|
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
|
||||||
|
@main.gepArray = global [8 x i8] zeroinitializer
|
||||||
|
@main.negativeGEP = global ptr null
|
||||||
|
|
||||||
declare void @runtime.printint64(i64) unnamed_addr
|
declare void @runtime.printint64(i64) unnamed_addr
|
||||||
|
|
||||||
@@ -88,6 +90,11 @@ entry:
|
|||||||
%agg2 = insertvalue {i8, i32, {float, {i64, i16}}} %agg, i64 5, 2, 1, 0
|
%agg2 = insertvalue {i8, i32, {float, {i64, i16}}} %agg, i64 5, 2, 1, 0
|
||||||
store {i8, i32, {float, {i64, i16}}} %agg2, ptr @main.insertedValue
|
store {i8, i32, {float, {i64, i16}}} %agg2, ptr @main.insertedValue
|
||||||
|
|
||||||
|
; negative GEP instruction
|
||||||
|
%ngep1 = getelementptr [8 x i8], ptr @main.negativeGEP, i32 0, i32 5
|
||||||
|
%ngep2 = getelementptr [8 x i8], ptr %ngep1, i32 0, i32 -3
|
||||||
|
store ptr %ngep2, ptr @main.negativeGEP
|
||||||
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
@@ -9,6 +9,8 @@ target triple = "x86_64--linux"
|
|||||||
@main.exposedValue1 = global i16 0
|
@main.exposedValue1 = global i16 0
|
||||||
@main.exposedValue2 = local_unnamed_addr global i16 0
|
@main.exposedValue2 = local_unnamed_addr global i16 0
|
||||||
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
|
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
|
||||||
|
@main.gepArray = local_unnamed_addr global [8 x i8] zeroinitializer
|
||||||
|
@main.negativeGEP = global ptr getelementptr inbounds (i8, ptr @main.negativeGEP, i64 2)
|
||||||
|
|
||||||
declare void @runtime.printint64(i64) unnamed_addr
|
declare void @runtime.printint64(i64) unnamed_addr
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule lib/binaryen updated: 96f7acf09a...11dba9b1c2
+1
-1
Submodule lib/cmsis-svd updated: df75ff974c...40327a4d2d
+1
-1
Submodule lib/wasi-libc updated: 30094b6ed0...1dfe5c302d
+5
-3
@@ -18,7 +18,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
@@ -157,7 +156,7 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
|
|||||||
|
|
||||||
// Add files from TinyGo.
|
// Add files from TinyGo.
|
||||||
tinygoDir := filepath.Join(tinygoSrc, dir)
|
tinygoDir := filepath.Join(tinygoSrc, dir)
|
||||||
tinygoEntries, err := ioutil.ReadDir(tinygoDir)
|
tinygoEntries, err := os.ReadDir(tinygoDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -177,7 +176,7 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
|
|||||||
// Add all directories from $GOROOT that are not part of the TinyGo
|
// Add all directories from $GOROOT that are not part of the TinyGo
|
||||||
// overrides.
|
// overrides.
|
||||||
goDir := filepath.Join(goSrc, dir)
|
goDir := filepath.Join(goSrc, dir)
|
||||||
goEntries, err := ioutil.ReadDir(goDir)
|
goEntries, err := os.ReadDir(goDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -233,6 +232,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
|||||||
"": true,
|
"": true,
|
||||||
"crypto/": true,
|
"crypto/": true,
|
||||||
"crypto/rand/": false,
|
"crypto/rand/": false,
|
||||||
|
"crypto/tls/": false,
|
||||||
"device/": false,
|
"device/": false,
|
||||||
"examples/": false,
|
"examples/": false,
|
||||||
"internal/": true,
|
"internal/": true,
|
||||||
@@ -242,7 +242,9 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
|||||||
"internal/task/": false,
|
"internal/task/": false,
|
||||||
"machine/": false,
|
"machine/": false,
|
||||||
"net/": true,
|
"net/": true,
|
||||||
|
"net/http/": false,
|
||||||
"os/": true,
|
"os/": true,
|
||||||
|
"os/user/": false,
|
||||||
"reflect/": false,
|
"reflect/": false,
|
||||||
"runtime/": false,
|
"runtime/": false,
|
||||||
"sync/": true,
|
"sync/": true,
|
||||||
|
|||||||
+1
-5
@@ -22,12 +22,8 @@ func List(config *compileopts.Config, extraArgs, pkgs []string) (*exec.Cmd, erro
|
|||||||
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
|
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
|
||||||
}
|
}
|
||||||
args = append(args, pkgs...)
|
args = append(args, pkgs...)
|
||||||
cgoEnabled := "0"
|
|
||||||
if config.CgoEnabled() {
|
|
||||||
cgoEnabled = "1"
|
|
||||||
}
|
|
||||||
cmd := exec.Command(filepath.Join(goenv.Get("GOROOT"), "bin", "go"), args...)
|
cmd := exec.Command(filepath.Join(goenv.Get("GOROOT"), "bin", "go"), args...)
|
||||||
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
|
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED=1")
|
||||||
if config.Options.Directory != "" {
|
if config.Options.Directory != "" {
|
||||||
cmd.Dir = config.Options.Directory
|
cmd.Dir = config.Options.Directory
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-16
@@ -27,13 +27,14 @@ import (
|
|||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var initFileVersions = func(info *types.Info) {}
|
||||||
|
|
||||||
// Program holds all packages and some metadata about the program as a whole.
|
// Program holds all packages and some metadata about the program as a whole.
|
||||||
type Program struct {
|
type Program struct {
|
||||||
config *compileopts.Config
|
config *compileopts.Config
|
||||||
clangHeaders string
|
typeChecker types.Config
|
||||||
typeChecker types.Config
|
goroot string // synthetic GOROOT
|
||||||
goroot string // synthetic GOROOT
|
workingDir string
|
||||||
workingDir string
|
|
||||||
|
|
||||||
Packages map[string]*Package
|
Packages map[string]*Package
|
||||||
sorted []*Package
|
sorted []*Package
|
||||||
@@ -103,7 +104,7 @@ type EmbedFile struct {
|
|||||||
// Load loads the given package with all dependencies (including the runtime
|
// Load loads the given package with all dependencies (including the runtime
|
||||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||||
// processing, if necessary).
|
// processing, if necessary).
|
||||||
func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config) (*Program, error) {
|
||||||
goroot, err := GetCachedGoroot(config)
|
goroot, err := GetCachedGoroot(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -118,13 +119,12 @@ func Load(config *compileopts.Config, inputPkg string, clangHeaders string, type
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
p := &Program{
|
p := &Program{
|
||||||
config: config,
|
config: config,
|
||||||
clangHeaders: clangHeaders,
|
typeChecker: typeChecker,
|
||||||
typeChecker: typeChecker,
|
goroot: goroot,
|
||||||
goroot: goroot,
|
workingDir: wd,
|
||||||
workingDir: wd,
|
Packages: make(map[string]*Package),
|
||||||
Packages: make(map[string]*Package),
|
fset: token.NewFileSet(),
|
||||||
fset: token.NewFileSet(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List the dependencies of this package, in raw JSON format.
|
// List the dependencies of this package, in raw JSON format.
|
||||||
@@ -380,6 +380,24 @@ func (p *Package) Check() error {
|
|||||||
typeErrors = append(typeErrors, err)
|
typeErrors = append(typeErrors, err)
|
||||||
}
|
}
|
||||||
checker.Importer = p
|
checker.Importer = p
|
||||||
|
if p.Module.GoVersion != "" {
|
||||||
|
// Setting the Go version for a module makes sure the type checker
|
||||||
|
// errors out on language features not supported in that particular
|
||||||
|
// version.
|
||||||
|
checker.GoVersion = "go" + p.Module.GoVersion
|
||||||
|
} else {
|
||||||
|
// Version is not known, so use the currently installed Go version.
|
||||||
|
// This is needed for `tinygo run` for example.
|
||||||
|
// Normally we'd use goenv.GorootVersionString(), but for compatibility
|
||||||
|
// with Go 1.20 and below we need a version in the form of "go1.12" (no
|
||||||
|
// patch version).
|
||||||
|
major, minor, err := goenv.GetGorootVersion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
checker.GoVersion = fmt.Sprintf("go%d.%d", major, minor)
|
||||||
|
}
|
||||||
|
initFileVersions(&p.info)
|
||||||
|
|
||||||
// Do typechecking of the package.
|
// Do typechecking of the package.
|
||||||
packageName := p.ImportPath
|
packageName := p.ImportPath
|
||||||
@@ -438,9 +456,9 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
// to call cgo.Process in that case as it will only cause issues.
|
// to call cgo.Process in that case as it will only cause issues.
|
||||||
if len(p.CgoFiles) != 0 && len(files) != 0 {
|
if len(p.CgoFiles) != 0 && len(files) != 0 {
|
||||||
var initialCFlags []string
|
var initialCFlags []string
|
||||||
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
|
initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...)
|
||||||
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||||
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.clangHeaders)
|
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags)
|
||||||
p.CFlags = append(initialCFlags, cflags...)
|
p.CFlags = append(initialCFlags, cflags...)
|
||||||
p.CGoHeaders = headerCode
|
p.CGoHeaders = headerCode
|
||||||
for path, hash := range accessedFiles {
|
for path, hash := range accessedFiles {
|
||||||
@@ -449,7 +467,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
}
|
}
|
||||||
files = append(files, generated)
|
files = append(files, generated...)
|
||||||
p.program.LDFlags = append(p.program.LDFlags, ldflags...)
|
p.program.LDFlags = append(p.program.LDFlags, ldflags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
//go:build go1.22
|
||||||
|
|
||||||
|
// types.Info.FileVersions was added in Go 1.22, so we can only initialize it
|
||||||
|
// when built with Go 1.22.
|
||||||
|
|
||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initFileVersions = func(info *types.Info) {
|
||||||
|
info.FileVersions = make(map[*ast.File]string)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -533,7 +532,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||||
}
|
}
|
||||||
if options.Monitor {
|
if options.Monitor {
|
||||||
return Monitor(result.Executable, "", options)
|
return Monitor(result.Executable, "", config)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -798,7 +797,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
|
|||||||
needsEnvInVars = true
|
needsEnvInVars = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var args, env []string
|
var args, emuArgs, env []string
|
||||||
var extraCmdEnv []string
|
var extraCmdEnv []string
|
||||||
if needsEnvInVars {
|
if needsEnvInVars {
|
||||||
runtimeGlobals := make(map[string]string)
|
runtimeGlobals := make(map[string]string)
|
||||||
@@ -821,13 +820,14 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
|
|||||||
} else if config.EmulatorName() == "wasmtime" {
|
} else if config.EmulatorName() == "wasmtime" {
|
||||||
// Wasmtime needs some special flags to pass environment variables
|
// Wasmtime needs some special flags to pass environment variables
|
||||||
// and allow reading from the current directory.
|
// and allow reading from the current directory.
|
||||||
args = append(args, "--dir=.")
|
emuArgs = append(emuArgs, "--dir=.")
|
||||||
for _, v := range environmentVars {
|
for _, v := range environmentVars {
|
||||||
args = append(args, "--env", v)
|
emuArgs = append(emuArgs, "--env", v)
|
||||||
}
|
}
|
||||||
if len(cmdArgs) != 0 {
|
if len(cmdArgs) != 0 {
|
||||||
// mark end of wasmtime arguments and start of program ones: --
|
// Use of '--' argument no longer necessary as of Wasmtime v14:
|
||||||
args = append(args, "--")
|
// https://github.com/bytecodealliance/wasmtime/pull/6946
|
||||||
|
// args = append(args, "--")
|
||||||
args = append(args, cmdArgs...)
|
args = append(args, cmdArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,7 +877,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
name = emulator[0]
|
name = emulator[0]
|
||||||
emuArgs := append([]string(nil), emulator[1:]...)
|
emuArgs = append(emuArgs, emulator[1:]...)
|
||||||
args = append(emuArgs, args...)
|
args = append(emuArgs, args...)
|
||||||
}
|
}
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
@@ -1212,15 +1212,10 @@ func getBMPPorts() (gdbPort, uartPort string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func usage(command string) {
|
func usage(command string) {
|
||||||
version := goenv.Version
|
|
||||||
if strings.HasSuffix(version, "-dev") && goenv.GitSha1 != "" {
|
|
||||||
version += "-" + goenv.GitSha1
|
|
||||||
}
|
|
||||||
|
|
||||||
switch command {
|
switch command {
|
||||||
default:
|
default:
|
||||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||||
fmt.Fprintln(os.Stderr, "version:", version)
|
fmt.Fprintln(os.Stderr, "version:", goenv.Version())
|
||||||
fmt.Fprintf(os.Stderr, "usage: %s <command> [arguments]\n", os.Args[0])
|
fmt.Fprintf(os.Stderr, "usage: %s <command> [arguments]\n", os.Args[0])
|
||||||
fmt.Fprintln(os.Stderr, "\ncommands:")
|
fmt.Fprintln(os.Stderr, "\ncommands:")
|
||||||
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
|
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
|
||||||
@@ -1230,6 +1225,7 @@ func usage(command string) {
|
|||||||
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
|
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
|
||||||
fmt.Fprintln(os.Stderr, " lldb: run/flash and immediately enter LLDB")
|
fmt.Fprintln(os.Stderr, " lldb: run/flash and immediately enter LLDB")
|
||||||
fmt.Fprintln(os.Stderr, " monitor: open communication port")
|
fmt.Fprintln(os.Stderr, " monitor: open communication port")
|
||||||
|
fmt.Fprintln(os.Stderr, " ports: list available serial ports")
|
||||||
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
|
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
|
||||||
fmt.Fprintln(os.Stderr, " list: run go list using the TinyGo root")
|
fmt.Fprintln(os.Stderr, " list: run go list using the TinyGo root")
|
||||||
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
|
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
|
||||||
@@ -1418,8 +1414,6 @@ func main() {
|
|||||||
serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
|
serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
|
||||||
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
||||||
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
||||||
interpMaxDepth := flag.Int("interp-maxdepth", 0, "interp optimization max depth (default 0=disabled)")
|
|
||||||
interpMaxInstr := flag.Int("interp-maxinstr", 100_000, "limit interp optimization max instructions (0=disabled)")
|
|
||||||
var tags buildutil.TagsFlag
|
var tags buildutil.TagsFlag
|
||||||
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
|
flag.Var(&tags, "tags", "a space-separated list of extra build tags")
|
||||||
target := flag.String("target", "", "chip/board name or JSON target specification file")
|
target := flag.String("target", "", "chip/board name or JSON target specification file")
|
||||||
@@ -1530,8 +1524,6 @@ func main() {
|
|||||||
Serial: *serial,
|
Serial: *serial,
|
||||||
Work: *work,
|
Work: *work,
|
||||||
InterpTimeout: *interpTimeout,
|
InterpTimeout: *interpTimeout,
|
||||||
InterpMaxDepth: *interpMaxDepth,
|
|
||||||
InterpMaxInstr: *interpMaxInstr,
|
|
||||||
PrintIR: *printIR,
|
PrintIR: *printIR,
|
||||||
DumpSSA: *dumpSSA,
|
DumpSSA: *dumpSSA,
|
||||||
VerifyIR: *verifyIR,
|
VerifyIR: *verifyIR,
|
||||||
@@ -1741,35 +1733,33 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
case "monitor":
|
case "monitor":
|
||||||
err := Monitor("", *port, options)
|
config, err := builder.NewConfig(options)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
|
err = Monitor("", *port, config)
|
||||||
|
handleCompilerError(err)
|
||||||
|
case "ports":
|
||||||
|
serialPortInfo, err := ListSerialPorts()
|
||||||
|
handleCompilerError(err)
|
||||||
|
if len(serialPortInfo) == 0 {
|
||||||
|
fmt.Println("No serial ports found.")
|
||||||
|
}
|
||||||
|
fmt.Printf("%-20s %-9s %s\n", "Port", "ID", "Boards")
|
||||||
|
for _, s := range serialPortInfo {
|
||||||
|
fmt.Printf("%-20s %4s:%4s %s\n", s.Name, s.VID, s.PID, s.Target)
|
||||||
|
}
|
||||||
case "targets":
|
case "targets":
|
||||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
specs, err := compileopts.GetTargetSpecs()
|
||||||
entries, err := ioutil.ReadDir(dir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "could not list targets:", err)
|
fmt.Fprintln(os.Stderr, "could not list targets:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
names := []string{}
|
||||||
if !entry.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") {
|
for key := range specs {
|
||||||
// Only inspect JSON files.
|
names = append(names, key)
|
||||||
continue
|
}
|
||||||
}
|
sort.Strings(names)
|
||||||
path := filepath.Join(dir, entry.Name())
|
for _, name := range names {
|
||||||
spec, err := compileopts.LoadTarget(&compileopts.Options{Target: path})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "could not list target:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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]
|
|
||||||
fmt.Println(name)
|
fmt.Println(name)
|
||||||
}
|
}
|
||||||
case "info":
|
case "info":
|
||||||
@@ -1878,11 +1868,7 @@ func main() {
|
|||||||
if s, err := goenv.GorootVersionString(); err == nil {
|
if s, err := goenv.GorootVersionString(); err == nil {
|
||||||
goversion = s
|
goversion = s
|
||||||
}
|
}
|
||||||
version := goenv.Version
|
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", goenv.Version(), runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
||||||
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
|
|
||||||
version += "-" + goenv.GitSha1
|
|
||||||
}
|
|
||||||
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
|
||||||
case "env":
|
case "env":
|
||||||
if flag.NArg() == 0 {
|
if flag.NArg() == 0 {
|
||||||
// Show all environment variables.
|
// Show all environment variables.
|
||||||
|
|||||||
+24
-19
@@ -69,6 +69,7 @@ func TestBuild(t *testing.T) {
|
|||||||
"json.go",
|
"json.go",
|
||||||
"map.go",
|
"map.go",
|
||||||
"math.go",
|
"math.go",
|
||||||
|
"oldgo/",
|
||||||
"print.go",
|
"print.go",
|
||||||
"reflect.go",
|
"reflect.go",
|
||||||
"slice.go",
|
"slice.go",
|
||||||
@@ -90,6 +91,9 @@ func TestBuild(t *testing.T) {
|
|||||||
if minor >= 21 {
|
if minor >= 21 {
|
||||||
tests = append(tests, "go1.21.go")
|
tests = append(tests, "go1.21.go")
|
||||||
}
|
}
|
||||||
|
if minor >= 22 {
|
||||||
|
tests = append(tests, "go1.22/")
|
||||||
|
}
|
||||||
|
|
||||||
if *testTarget != "" {
|
if *testTarget != "" {
|
||||||
// This makes it possible to run one specific test (instead of all),
|
// This makes it possible to run one specific test (instead of all),
|
||||||
@@ -290,16 +294,15 @@ func emuCheck(t *testing.T, options compileopts.Options) {
|
|||||||
func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
|
func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
|
||||||
return compileopts.Options{
|
return compileopts.Options{
|
||||||
// GOOS/GOARCH are only used if target == ""
|
// GOOS/GOARCH are only used if target == ""
|
||||||
GOOS: goenv.Get("GOOS"),
|
GOOS: goenv.Get("GOOS"),
|
||||||
GOARCH: goenv.Get("GOARCH"),
|
GOARCH: goenv.Get("GOARCH"),
|
||||||
GOARM: goenv.Get("GOARM"),
|
GOARM: goenv.Get("GOARM"),
|
||||||
Target: target,
|
Target: target,
|
||||||
Semaphore: sema,
|
Semaphore: sema,
|
||||||
InterpTimeout: 180 * time.Second,
|
InterpTimeout: 180 * time.Second,
|
||||||
InterpMaxDepth: 10,
|
Debug: true,
|
||||||
Debug: true,
|
VerifyIR: true,
|
||||||
VerifyIR: true,
|
Opt: "z",
|
||||||
Opt: "z",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,14 +312,13 @@ func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
|
|||||||
func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options {
|
func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options {
|
||||||
parts := strings.Split(osarch, "/")
|
parts := strings.Split(osarch, "/")
|
||||||
options := compileopts.Options{
|
options := compileopts.Options{
|
||||||
GOOS: parts[0],
|
GOOS: parts[0],
|
||||||
GOARCH: parts[1],
|
GOARCH: parts[1],
|
||||||
Semaphore: sema,
|
Semaphore: sema,
|
||||||
InterpTimeout: 180 * time.Second,
|
InterpTimeout: 180 * time.Second,
|
||||||
InterpMaxDepth: 10,
|
Debug: true,
|
||||||
Debug: true,
|
VerifyIR: true,
|
||||||
VerifyIR: true,
|
Opt: "z",
|
||||||
Opt: "z",
|
|
||||||
}
|
}
|
||||||
if options.GOARCH == "arm" {
|
if options.GOARCH == "arm" {
|
||||||
options.GOARM = parts[2]
|
options.GOARM = parts[2]
|
||||||
@@ -335,8 +337,11 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
path := TESTDATA + "/" + name
|
path := TESTDATA + "/" + name
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
txtpath := path[:len(path)-3] + ".txt"
|
txtpath := path[:len(path)-3] + ".txt"
|
||||||
|
pkgName := "./" + path
|
||||||
if path[len(path)-1] == '/' {
|
if path[len(path)-1] == '/' {
|
||||||
txtpath = path + "out.txt"
|
txtpath = path + "out.txt"
|
||||||
|
options.Directory = path
|
||||||
|
pkgName = "."
|
||||||
}
|
}
|
||||||
expected, err := os.ReadFile(txtpath)
|
expected, err := os.ReadFile(txtpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -350,7 +355,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
|
|
||||||
// Build the test binary.
|
// Build the test binary.
|
||||||
stdout := &bytes.Buffer{}
|
stdout := &bytes.Buffer{}
|
||||||
_, err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+199
-32
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
@@ -9,57 +10,167 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattn/go-tty"
|
"github.com/mattn/go-tty"
|
||||||
"github.com/tinygo-org/tinygo/builder"
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
|
||||||
"go.bug.st/serial"
|
"go.bug.st/serial"
|
||||||
|
"go.bug.st/serial/enumerator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Monitor connects to the given port and reads/writes the serial port.
|
// Monitor connects to the given port and reads/writes the serial port.
|
||||||
func Monitor(executable, port string, options *compileopts.Options) error {
|
func Monitor(executable, port string, config *compileopts.Config) error {
|
||||||
config, err := builder.NewConfig(options)
|
const timeout = time.Second * 3
|
||||||
if err != nil {
|
var exit func() // function to be called before exiting
|
||||||
return err
|
var serialConn io.ReadWriter
|
||||||
}
|
|
||||||
|
|
||||||
wait := 300
|
if config.Options.Serial == "rtt" {
|
||||||
for i := 0; i <= wait; i++ {
|
// Use the RTT interface, which is documented (in part) here:
|
||||||
port, err = getDefaultPort(port, config.Target.SerialPort)
|
// https://wiki.segger.com/RTT
|
||||||
|
|
||||||
|
// Try to find the "machine.rttSerialInstance" symbol, which is the RTT
|
||||||
|
// control block.
|
||||||
|
file, err := elf.Open(executable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if i < wait {
|
return fmt.Errorf("could not open ELF file to determine RTT control block: %w", err)
|
||||||
time.Sleep(10 * time.Millisecond)
|
}
|
||||||
continue
|
defer file.Close()
|
||||||
|
symbols, err := file.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read ELF symbol table to determine RTT control block: %w", err)
|
||||||
|
}
|
||||||
|
var address uint64
|
||||||
|
for _, symbol := range symbols {
|
||||||
|
if symbol.Name == "machine.rttSerialInstance" {
|
||||||
|
address = symbol.Value
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if address == 0 {
|
||||||
|
return fmt.Errorf("could not find RTT control block in ELF file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start an openocd process in the background.
|
||||||
|
args, err := config.OpenOCDConfiguration()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
break
|
args = append(args,
|
||||||
}
|
"-c", fmt.Sprintf("rtt setup 0x%x 16 \"SEGGER RTT\"", address),
|
||||||
|
"-c", "init",
|
||||||
br := options.BaudRate
|
"-c", "rtt server start 0 0")
|
||||||
if br <= 0 {
|
cmd := executeCommand(config.Options, "openocd", args...)
|
||||||
br = 115200
|
stderr, err := cmd.StderrPipe()
|
||||||
}
|
|
||||||
|
|
||||||
wait = 300
|
|
||||||
var p serial.Port
|
|
||||||
for i := 0; i <= wait; i++ {
|
|
||||||
p, err = serial.Open(port, &serial.Mode{BaudRate: br})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if i < wait {
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
break
|
cmd.Stdout = os.Stdout
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
exit = func() {
|
||||||
|
// Make sure the openocd process is terminated at exit.
|
||||||
|
// This does not happen through the defer above when exiting through
|
||||||
|
// os.Exit.
|
||||||
|
cmd.Process.Kill()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the stderr, which logs various important messages we need.
|
||||||
|
r := bufio.NewReader(stderr)
|
||||||
|
var telnet net.Conn
|
||||||
|
var timeoutAt time.Time
|
||||||
|
for {
|
||||||
|
// Read the next line from the openocd process.
|
||||||
|
lineBytes, err := r.ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line := string(lineBytes)
|
||||||
|
|
||||||
|
if line == "Info : rtt: No control block found\n" {
|
||||||
|
// Message that is sent back when OpenOCD can't find the control
|
||||||
|
// block after a 'rtt start' message.
|
||||||
|
if time.Now().After(timeoutAt) {
|
||||||
|
return fmt.Errorf("RTT timeout (could not locate RTT control block at 0x%08x)", address)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
telnet.Write([]byte("rtt start\r\n"))
|
||||||
|
} else if strings.HasPrefix(line, "Info : Listening on port") {
|
||||||
|
// We need two different ports for controlling OpenOCD
|
||||||
|
// (typically port 4444) and the RTT channel 0 socket (arbitrary
|
||||||
|
// port).
|
||||||
|
var port int
|
||||||
|
var protocol string
|
||||||
|
fmt.Sscanf(line, "Info : Listening on port %d for %s connections\n", &port, &protocol)
|
||||||
|
if protocol == "telnet" && telnet == nil {
|
||||||
|
// Connect to the "telnet" command line interface.
|
||||||
|
telnet, err = net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Tell OpenOCD to start scanning for the RTT control block.
|
||||||
|
telnet.Write([]byte("rtt start\r\n"))
|
||||||
|
// Also make sure we will time out if the control block just
|
||||||
|
// can't be found.
|
||||||
|
timeoutAt = time.Now().Add(timeout)
|
||||||
|
} else if protocol == "rtt" {
|
||||||
|
// Connect to the RTT channel, for both stdin and stdout.
|
||||||
|
conn, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serialConn = conn
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(line, "Info : rtt: Control block found at") {
|
||||||
|
// Connection established!
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // -serial=uart or -serial=usb
|
||||||
|
var err error
|
||||||
|
wait := 300
|
||||||
|
for i := 0; i <= wait; i++ {
|
||||||
|
port, err = getDefaultPort(port, config.Target.SerialPort)
|
||||||
|
if err != nil {
|
||||||
|
if i < wait {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
br := config.Options.BaudRate
|
||||||
|
if br <= 0 {
|
||||||
|
br = 115200
|
||||||
|
}
|
||||||
|
|
||||||
|
wait = 300
|
||||||
|
var p serial.Port
|
||||||
|
for i := 0; i <= wait; i++ {
|
||||||
|
p, err = serial.Open(port, &serial.Mode{BaudRate: br})
|
||||||
|
if err != nil {
|
||||||
|
if i < wait {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serialConn = p
|
||||||
|
break
|
||||||
|
}
|
||||||
|
defer p.Close()
|
||||||
}
|
}
|
||||||
defer p.Close()
|
|
||||||
|
|
||||||
tty, err := tty.Open()
|
tty, err := tty.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -74,6 +185,9 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
|||||||
go func() {
|
go func() {
|
||||||
<-sig
|
<-sig
|
||||||
tty.Close()
|
tty.Close()
|
||||||
|
if exit != nil {
|
||||||
|
exit()
|
||||||
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -85,7 +199,7 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
|||||||
buf := make([]byte, 100*1024)
|
buf := make([]byte, 100*1024)
|
||||||
var line []byte
|
var line []byte
|
||||||
for {
|
for {
|
||||||
n, err := p.Read(buf)
|
n, err := serialConn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCh <- fmt.Errorf("read error: %w", err)
|
errCh <- fmt.Errorf("read error: %w", err)
|
||||||
return
|
return
|
||||||
@@ -121,13 +235,66 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
|||||||
if r == 0 {
|
if r == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p.Write([]byte(string(r)))
|
serialConn.Write([]byte(string(r)))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return <-errCh
|
return <-errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SerialPortInfo is a structure that holds information about the port and its
|
||||||
|
// associated TargetSpec.
|
||||||
|
type SerialPortInfo struct {
|
||||||
|
Name string
|
||||||
|
IsUSB bool
|
||||||
|
VID string
|
||||||
|
PID string
|
||||||
|
Target string
|
||||||
|
Spec *compileopts.TargetSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListSerialPort returns serial port information and any detected TinyGo
|
||||||
|
// target.
|
||||||
|
func ListSerialPorts() ([]SerialPortInfo, error) {
|
||||||
|
maps, err := compileopts.GetTargetSpecs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
portsList, err := enumerator.GetDetailedPortsList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serialPortInfo := []SerialPortInfo{}
|
||||||
|
for _, p := range portsList {
|
||||||
|
info := SerialPortInfo{
|
||||||
|
Name: p.Name,
|
||||||
|
IsUSB: p.IsUSB,
|
||||||
|
VID: p.VID,
|
||||||
|
PID: p.PID,
|
||||||
|
}
|
||||||
|
vid := strings.ToLower(p.VID)
|
||||||
|
pid := strings.ToLower(p.PID)
|
||||||
|
for k, v := range maps {
|
||||||
|
usbInterfaces := v.SerialPort
|
||||||
|
for _, s := range usbInterfaces {
|
||||||
|
parts := strings.Split(s, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if vid == strings.ToLower(parts[0]) && pid == strings.ToLower(parts[1]) {
|
||||||
|
info.Target = k
|
||||||
|
info.Spec = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serialPortInfo = append(serialPortInfo, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return serialPortInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
var addressMatch = regexp.MustCompile(`^panic: runtime error at 0x([0-9a-f]+): `)
|
var addressMatch = regexp.MustCompile(`^panic: runtime error at 0x([0-9a-f]+): `)
|
||||||
|
|
||||||
// Extract the address from the "panic: runtime error at" message.
|
// Extract the address from the "panic: runtime error at" message.
|
||||||
|
|||||||
+5
-6
@@ -22,12 +22,11 @@ func TestTraceback(t *testing.T) {
|
|||||||
// Build a small binary that only panics.
|
// Build a small binary that only panics.
|
||||||
tmpdir := t.TempDir()
|
tmpdir := t.TempDir()
|
||||||
config, err := builder.NewConfig(&compileopts.Options{
|
config, err := builder.NewConfig(&compileopts.Options{
|
||||||
GOOS: runtime.GOOS,
|
GOOS: runtime.GOOS,
|
||||||
GOARCH: runtime.GOARCH,
|
GOARCH: runtime.GOARCH,
|
||||||
Opt: "z",
|
Opt: "z",
|
||||||
InterpTimeout: time.Minute,
|
InterpTimeout: time.Minute,
|
||||||
InterpMaxDepth: 10,
|
Debug: true,
|
||||||
Debug: true,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
//go:build nrf || stm32 || (sam && atsamd51) || (sam && atsame5x)
|
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3
|
||||||
|
|
||||||
|
// If you update the above build constraint, you'll probably also need to update
|
||||||
|
// src/runtime/rand_hwrng.go.
|
||||||
|
|
||||||
package rand
|
package rand
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,447 @@
|
|||||||
|
// TINYGO: The following is copied and modified from Go 1.19.3 official implementation.
|
||||||
|
|
||||||
|
// Copyright 2009 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 tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CurveID is the type of a TLS identifier for an elliptic curve. See
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8.
|
||||||
|
//
|
||||||
|
// In TLS 1.3, this type is called NamedGroup, but at this time this library
|
||||||
|
// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7.
|
||||||
|
type CurveID uint16
|
||||||
|
|
||||||
|
// ConnectionState records basic TLS details about the connection.
|
||||||
|
type ConnectionState struct {
|
||||||
|
// TINYGO: empty; TLS connection offloaded to device
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientAuthType declares the policy the server will follow for
|
||||||
|
// TLS Client Authentication.
|
||||||
|
type ClientAuthType int
|
||||||
|
|
||||||
|
// ClientSessionCache is a cache of ClientSessionState objects that can be used
|
||||||
|
// by a client to resume a TLS session with a given server. ClientSessionCache
|
||||||
|
// implementations should expect to be called concurrently from different
|
||||||
|
// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not
|
||||||
|
// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which
|
||||||
|
// are supported via this interface.
|
||||||
|
type ClientSessionCache interface {
|
||||||
|
// Get searches for a ClientSessionState associated with the given key.
|
||||||
|
// On return, ok is true if one was found.
|
||||||
|
Get(sessionKey string) (session *ClientSessionState, ok bool)
|
||||||
|
|
||||||
|
// Put adds the ClientSessionState to the cache with the given key. It might
|
||||||
|
// get called multiple times in a connection if a TLS 1.3 server provides
|
||||||
|
// more than one session ticket. If called with a nil *ClientSessionState,
|
||||||
|
// it should remove the cache entry.
|
||||||
|
Put(sessionKey string, cs *ClientSessionState)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go
|
||||||
|
|
||||||
|
// SignatureScheme identifies a signature algorithm supported by TLS. See
|
||||||
|
// RFC 8446, Section 4.2.3.
|
||||||
|
type SignatureScheme uint16
|
||||||
|
|
||||||
|
// ClientHelloInfo contains information from a ClientHello message in order to
|
||||||
|
// guide application logic in the GetCertificate and GetConfigForClient callbacks.
|
||||||
|
type ClientHelloInfo struct {
|
||||||
|
// CipherSuites lists the CipherSuites supported by the client (e.g.
|
||||||
|
// TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
|
||||||
|
CipherSuites []uint16
|
||||||
|
|
||||||
|
// ServerName indicates the name of the server requested by the client
|
||||||
|
// in order to support virtual hosting. ServerName is only set if the
|
||||||
|
// client is using SNI (see RFC 4366, Section 3.1).
|
||||||
|
ServerName string
|
||||||
|
|
||||||
|
// SupportedCurves lists the elliptic curves supported by the client.
|
||||||
|
// SupportedCurves is set only if the Supported Elliptic Curves
|
||||||
|
// Extension is being used (see RFC 4492, Section 5.1.1).
|
||||||
|
SupportedCurves []CurveID
|
||||||
|
|
||||||
|
// SupportedPoints lists the point formats supported by the client.
|
||||||
|
// SupportedPoints is set only if the Supported Point Formats Extension
|
||||||
|
// is being used (see RFC 4492, Section 5.1.2).
|
||||||
|
SupportedPoints []uint8
|
||||||
|
|
||||||
|
// SignatureSchemes lists the signature and hash schemes that the client
|
||||||
|
// is willing to verify. SignatureSchemes is set only if the Signature
|
||||||
|
// Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1).
|
||||||
|
SignatureSchemes []SignatureScheme
|
||||||
|
|
||||||
|
// SupportedProtos lists the application protocols supported by the client.
|
||||||
|
// SupportedProtos is set only if the Application-Layer Protocol
|
||||||
|
// Negotiation Extension is being used (see RFC 7301, Section 3.1).
|
||||||
|
//
|
||||||
|
// Servers can select a protocol by setting Config.NextProtos in a
|
||||||
|
// GetConfigForClient return value.
|
||||||
|
SupportedProtos []string
|
||||||
|
|
||||||
|
// SupportedVersions lists the TLS versions supported by the client.
|
||||||
|
// For TLS versions less than 1.3, this is extrapolated from the max
|
||||||
|
// version advertised by the client, so values other than the greatest
|
||||||
|
// might be rejected if used.
|
||||||
|
SupportedVersions []uint16
|
||||||
|
|
||||||
|
// Conn is the underlying net.Conn for the connection. Do not read
|
||||||
|
// from, or write to, this connection; that will cause the TLS
|
||||||
|
// connection to fail.
|
||||||
|
Conn net.Conn
|
||||||
|
|
||||||
|
// config is embedded by the GetCertificate or GetConfigForClient caller,
|
||||||
|
// for use with SupportsCertificate.
|
||||||
|
config *Config
|
||||||
|
|
||||||
|
// ctx is the context of the handshake that is in progress.
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateRequestInfo contains information from a server's
|
||||||
|
// CertificateRequest message, which is used to demand a certificate and proof
|
||||||
|
// of control from a client.
|
||||||
|
type CertificateRequestInfo struct {
|
||||||
|
// AcceptableCAs contains zero or more, DER-encoded, X.501
|
||||||
|
// Distinguished Names. These are the names of root or intermediate CAs
|
||||||
|
// that the server wishes the returned certificate to be signed by. An
|
||||||
|
// empty slice indicates that the server has no preference.
|
||||||
|
AcceptableCAs [][]byte
|
||||||
|
|
||||||
|
// SignatureSchemes lists the signature schemes that the server is
|
||||||
|
// willing to verify.
|
||||||
|
SignatureSchemes []SignatureScheme
|
||||||
|
|
||||||
|
// Version is the TLS version that was negotiated for this connection.
|
||||||
|
Version uint16
|
||||||
|
|
||||||
|
// ctx is the context of the handshake that is in progress.
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenegotiationSupport enumerates the different levels of support for TLS
|
||||||
|
// renegotiation. TLS renegotiation is the act of performing subsequent
|
||||||
|
// handshakes on a connection after the first. This significantly complicates
|
||||||
|
// the state machine and has been the source of numerous, subtle security
|
||||||
|
// issues. Initiating a renegotiation is not supported, but support for
|
||||||
|
// accepting renegotiation requests may be enabled.
|
||||||
|
//
|
||||||
|
// Even when enabled, the server may not change its identity between handshakes
|
||||||
|
// (i.e. the leaf certificate must be the same). Additionally, concurrent
|
||||||
|
// handshake and application data flow is not permitted so renegotiation can
|
||||||
|
// only be used with protocols that synchronise with the renegotiation, such as
|
||||||
|
// HTTPS.
|
||||||
|
//
|
||||||
|
// Renegotiation is not defined in TLS 1.3.
|
||||||
|
type RenegotiationSupport int
|
||||||
|
|
||||||
|
// A Config structure is used to configure a TLS client or server.
|
||||||
|
// After one has been passed to a TLS function it must not be
|
||||||
|
// modified. A Config may be reused; the tls package will also not
|
||||||
|
// modify it.
|
||||||
|
type Config struct {
|
||||||
|
// Rand provides the source of entropy for nonces and RSA blinding.
|
||||||
|
// If Rand is nil, TLS uses the cryptographic random reader in package
|
||||||
|
// crypto/rand.
|
||||||
|
// The Reader must be safe for use by multiple goroutines.
|
||||||
|
Rand io.Reader
|
||||||
|
|
||||||
|
// Time returns the current time as the number of seconds since the epoch.
|
||||||
|
// If Time is nil, TLS uses time.Now.
|
||||||
|
Time func() time.Time
|
||||||
|
|
||||||
|
// Certificates contains one or more certificate chains to present to the
|
||||||
|
// other side of the connection. The first certificate compatible with the
|
||||||
|
// peer's requirements is selected automatically.
|
||||||
|
//
|
||||||
|
// Server configurations must set one of Certificates, GetCertificate or
|
||||||
|
// GetConfigForClient. Clients doing client-authentication may set either
|
||||||
|
// Certificates or GetClientCertificate.
|
||||||
|
//
|
||||||
|
// Note: if there are multiple Certificates, and they don't have the
|
||||||
|
// optional field Leaf set, certificate selection will incur a significant
|
||||||
|
// per-handshake performance cost.
|
||||||
|
Certificates []Certificate
|
||||||
|
|
||||||
|
// NameToCertificate maps from a certificate name to an element of
|
||||||
|
// Certificates. Note that a certificate name can be of the form
|
||||||
|
// '*.example.com' and so doesn't have to be a domain name as such.
|
||||||
|
//
|
||||||
|
// Deprecated: NameToCertificate only allows associating a single
|
||||||
|
// certificate with a given name. Leave this field nil to let the library
|
||||||
|
// select the first compatible chain from Certificates.
|
||||||
|
NameToCertificate map[string]*Certificate
|
||||||
|
|
||||||
|
// GetCertificate returns a Certificate based on the given
|
||||||
|
// ClientHelloInfo. It will only be called if the client supplies SNI
|
||||||
|
// information or if Certificates is empty.
|
||||||
|
//
|
||||||
|
// If GetCertificate is nil or returns nil, then the certificate is
|
||||||
|
// retrieved from NameToCertificate. If NameToCertificate is nil, the
|
||||||
|
// best element of Certificates will be used.
|
||||||
|
//
|
||||||
|
// Once a Certificate is returned it should not be modified.
|
||||||
|
GetCertificate func(*ClientHelloInfo) (*Certificate, error)
|
||||||
|
|
||||||
|
// GetClientCertificate, if not nil, is called when a server requests a
|
||||||
|
// certificate from a client. If set, the contents of Certificates will
|
||||||
|
// be ignored.
|
||||||
|
//
|
||||||
|
// If GetClientCertificate returns an error, the handshake will be
|
||||||
|
// aborted and that error will be returned. Otherwise
|
||||||
|
// GetClientCertificate must return a non-nil Certificate. If
|
||||||
|
// Certificate.Certificate is empty then no certificate will be sent to
|
||||||
|
// the server. If this is unacceptable to the server then it may abort
|
||||||
|
// the handshake.
|
||||||
|
//
|
||||||
|
// GetClientCertificate may be called multiple times for the same
|
||||||
|
// connection if renegotiation occurs or if TLS 1.3 is in use.
|
||||||
|
//
|
||||||
|
// Once a Certificate is returned it should not be modified.
|
||||||
|
GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)
|
||||||
|
|
||||||
|
// GetConfigForClient, if not nil, is called after a ClientHello is
|
||||||
|
// received from a client. It may return a non-nil Config in order to
|
||||||
|
// change the Config that will be used to handle this connection. If
|
||||||
|
// the returned Config is nil, the original Config will be used. The
|
||||||
|
// Config returned by this callback may not be subsequently modified.
|
||||||
|
//
|
||||||
|
// If GetConfigForClient is nil, the Config passed to Server() will be
|
||||||
|
// used for all connections.
|
||||||
|
//
|
||||||
|
// If SessionTicketKey was explicitly set on the returned Config, or if
|
||||||
|
// SetSessionTicketKeys was called on the returned Config, those keys will
|
||||||
|
// be used. Otherwise, the original Config keys will be used (and possibly
|
||||||
|
// rotated if they are automatically managed).
|
||||||
|
GetConfigForClient func(*ClientHelloInfo) (*Config, error)
|
||||||
|
|
||||||
|
// VerifyPeerCertificate, if not nil, is called after normal
|
||||||
|
// certificate verification by either a TLS client or server. It
|
||||||
|
// receives the raw ASN.1 certificates provided by the peer and also
|
||||||
|
// any verified chains that normal processing found. If it returns a
|
||||||
|
// non-nil error, the handshake is aborted and that error results.
|
||||||
|
//
|
||||||
|
// If normal verification fails then the handshake will abort before
|
||||||
|
// considering this callback. If normal verification is disabled (on the
|
||||||
|
// client when InsecureSkipVerify is set, or on a server when ClientAuth is
|
||||||
|
// RequestClientCert or RequireAnyClientCert), then this callback will be
|
||||||
|
// considered but the verifiedChains argument will always be nil. When
|
||||||
|
// ClientAuth is NoClientCert, this callback is not called on the server.
|
||||||
|
// rawCerts may be empty on the server if ClientAuth is RequestClientCert or
|
||||||
|
// VerifyClientCertIfGiven.
|
||||||
|
//
|
||||||
|
// This callback is not invoked on resumed connections, as certificates are
|
||||||
|
// not re-verified on resumption.
|
||||||
|
//
|
||||||
|
// verifiedChains and its contents should not be modified.
|
||||||
|
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
|
||||||
|
|
||||||
|
// VerifyConnection, if not nil, is called after normal certificate
|
||||||
|
// verification and after VerifyPeerCertificate by either a TLS client
|
||||||
|
// or server. If it returns a non-nil error, the handshake is aborted
|
||||||
|
// and that error results.
|
||||||
|
//
|
||||||
|
// If normal verification fails then the handshake will abort before
|
||||||
|
// considering this callback. This callback will run for all connections,
|
||||||
|
// including resumptions, regardless of InsecureSkipVerify or ClientAuth
|
||||||
|
// settings.
|
||||||
|
VerifyConnection func(ConnectionState) error
|
||||||
|
|
||||||
|
// RootCAs defines the set of root certificate authorities
|
||||||
|
// that clients use when verifying server certificates.
|
||||||
|
// If RootCAs is nil, TLS uses the host's root CA set.
|
||||||
|
RootCAs *x509.CertPool
|
||||||
|
|
||||||
|
// NextProtos is a list of supported application level protocols, in
|
||||||
|
// order of preference. If both peers support ALPN, the selected
|
||||||
|
// protocol will be one from this list, and the connection will fail
|
||||||
|
// if there is no mutually supported protocol. If NextProtos is empty
|
||||||
|
// or the peer doesn't support ALPN, the connection will succeed and
|
||||||
|
// ConnectionState.NegotiatedProtocol will be empty.
|
||||||
|
NextProtos []string
|
||||||
|
|
||||||
|
// ServerName is used to verify the hostname on the returned
|
||||||
|
// certificates unless InsecureSkipVerify is given. It is also included
|
||||||
|
// in the client's handshake to support virtual hosting unless it is
|
||||||
|
// an IP address.
|
||||||
|
ServerName string
|
||||||
|
|
||||||
|
// ClientAuth determines the server's policy for
|
||||||
|
// TLS Client Authentication. The default is NoClientCert.
|
||||||
|
ClientAuth ClientAuthType
|
||||||
|
|
||||||
|
// ClientCAs defines the set of root certificate authorities
|
||||||
|
// that servers use if required to verify a client certificate
|
||||||
|
// by the policy in ClientAuth.
|
||||||
|
ClientCAs *x509.CertPool
|
||||||
|
|
||||||
|
// InsecureSkipVerify controls whether a client verifies the server's
|
||||||
|
// certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
|
||||||
|
// accepts any certificate presented by the server and any host name in that
|
||||||
|
// certificate. In this mode, TLS is susceptible to machine-in-the-middle
|
||||||
|
// attacks unless custom verification is used. This should be used only for
|
||||||
|
// testing or in combination with VerifyConnection or VerifyPeerCertificate.
|
||||||
|
InsecureSkipVerify bool
|
||||||
|
|
||||||
|
// CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of
|
||||||
|
// the list is ignored. Note that TLS 1.3 ciphersuites are not configurable.
|
||||||
|
//
|
||||||
|
// If CipherSuites is nil, a safe default list is used. The default cipher
|
||||||
|
// suites might change over time.
|
||||||
|
CipherSuites []uint16
|
||||||
|
|
||||||
|
// PreferServerCipherSuites is a legacy field and has no effect.
|
||||||
|
//
|
||||||
|
// It used to control whether the server would follow the client's or the
|
||||||
|
// server's preference. Servers now select the best mutually supported
|
||||||
|
// cipher suite based on logic that takes into account inferred client
|
||||||
|
// hardware, server hardware, and security.
|
||||||
|
//
|
||||||
|
// Deprecated: PreferServerCipherSuites is ignored.
|
||||||
|
PreferServerCipherSuites bool
|
||||||
|
|
||||||
|
// SessionTicketsDisabled may be set to true to disable session ticket and
|
||||||
|
// PSK (resumption) support. Note that on clients, session ticket support is
|
||||||
|
// also disabled if ClientSessionCache is nil.
|
||||||
|
SessionTicketsDisabled bool
|
||||||
|
|
||||||
|
// SessionTicketKey is used by TLS servers to provide session resumption.
|
||||||
|
// See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled
|
||||||
|
// with random data before the first server handshake.
|
||||||
|
//
|
||||||
|
// Deprecated: if this field is left at zero, session ticket keys will be
|
||||||
|
// automatically rotated every day and dropped after seven days. For
|
||||||
|
// customizing the rotation schedule or synchronizing servers that are
|
||||||
|
// terminating connections for the same host, use SetSessionTicketKeys.
|
||||||
|
SessionTicketKey [32]byte
|
||||||
|
|
||||||
|
// ClientSessionCache is a cache of ClientSessionState entries for TLS
|
||||||
|
// session resumption. It is only used by clients.
|
||||||
|
ClientSessionCache ClientSessionCache
|
||||||
|
|
||||||
|
// UnwrapSession is called on the server to turn a ticket/identity
|
||||||
|
// previously produced by [WrapSession] into a usable session.
|
||||||
|
//
|
||||||
|
// UnwrapSession will usually either decrypt a session state in the ticket
|
||||||
|
// (for example with [Config.EncryptTicket]), or use the ticket as a handle
|
||||||
|
// to recover a previously stored state. It must use [ParseSessionState] to
|
||||||
|
// deserialize the session state.
|
||||||
|
//
|
||||||
|
// If UnwrapSession returns an error, the connection is terminated. If it
|
||||||
|
// returns (nil, nil), the session is ignored. crypto/tls may still choose
|
||||||
|
// not to resume the returned session.
|
||||||
|
UnwrapSession func(identity []byte, cs ConnectionState) (*SessionState, error)
|
||||||
|
|
||||||
|
// WrapSession is called on the server to produce a session ticket/identity.
|
||||||
|
//
|
||||||
|
// WrapSession must serialize the session state with [SessionState.Bytes].
|
||||||
|
// It may then encrypt the serialized state (for example with
|
||||||
|
// [Config.DecryptTicket]) and use it as the ticket, or store the state and
|
||||||
|
// return a handle for it.
|
||||||
|
//
|
||||||
|
// If WrapSession returns an error, the connection is terminated.
|
||||||
|
//
|
||||||
|
// Warning: the return value will be exposed on the wire and to clients in
|
||||||
|
// plaintext. The application is in charge of encrypting and authenticating
|
||||||
|
// it (and rotating keys) or returning high-entropy identifiers. Failing to
|
||||||
|
// do so correctly can compromise current, previous, and future connections
|
||||||
|
// depending on the protocol version.
|
||||||
|
WrapSession func(ConnectionState, *SessionState) ([]byte, error)
|
||||||
|
|
||||||
|
// MinVersion contains the minimum TLS version that is acceptable.
|
||||||
|
//
|
||||||
|
// By default, TLS 1.2 is currently used as the minimum when acting as a
|
||||||
|
// client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum
|
||||||
|
// supported by this package, both as a client and as a server.
|
||||||
|
//
|
||||||
|
// The client-side default can temporarily be reverted to TLS 1.0 by
|
||||||
|
// including the value "x509sha1=1" in the GODEBUG environment variable.
|
||||||
|
// Note that this option will be removed in Go 1.19 (but it will still be
|
||||||
|
// possible to set this field to VersionTLS10 explicitly).
|
||||||
|
MinVersion uint16
|
||||||
|
|
||||||
|
// MaxVersion contains the maximum TLS version that is acceptable.
|
||||||
|
//
|
||||||
|
// By default, the maximum version supported by this package is used,
|
||||||
|
// which is currently TLS 1.3.
|
||||||
|
MaxVersion uint16
|
||||||
|
|
||||||
|
// CurvePreferences contains the elliptic curves that will be used in
|
||||||
|
// an ECDHE handshake, in preference order. If empty, the default will
|
||||||
|
// be used. The client will use the first preference as the type for
|
||||||
|
// its key share in TLS 1.3. This may change in the future.
|
||||||
|
CurvePreferences []CurveID
|
||||||
|
|
||||||
|
// DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
|
||||||
|
// When true, the largest possible TLS record size is always used. When
|
||||||
|
// false, the size of TLS records may be adjusted in an attempt to
|
||||||
|
// improve latency.
|
||||||
|
DynamicRecordSizingDisabled bool
|
||||||
|
|
||||||
|
// Renegotiation controls what types of renegotiation are supported.
|
||||||
|
// The default, none, is correct for the vast majority of applications.
|
||||||
|
Renegotiation RenegotiationSupport
|
||||||
|
|
||||||
|
// KeyLogWriter optionally specifies a destination for TLS master secrets
|
||||||
|
// in NSS key log format that can be used to allow external programs
|
||||||
|
// such as Wireshark to decrypt TLS connections.
|
||||||
|
// See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format.
|
||||||
|
// Use of KeyLogWriter compromises security and should only be
|
||||||
|
// used for debugging.
|
||||||
|
KeyLogWriter io.Writer
|
||||||
|
|
||||||
|
// mutex protects sessionTicketKeys and autoSessionTicketKeys.
|
||||||
|
mutex sync.RWMutex
|
||||||
|
// sessionTicketKeys contains zero or more ticket keys. If set, it means
|
||||||
|
// the keys were set with SessionTicketKey or SetSessionTicketKeys. The
|
||||||
|
// first key is used for new tickets and any subsequent keys can be used to
|
||||||
|
// decrypt old tickets. The slice contents are not protected by the mutex
|
||||||
|
// and are immutable.
|
||||||
|
sessionTicketKeys []ticketKey
|
||||||
|
// autoSessionTicketKeys is like sessionTicketKeys but is owned by the
|
||||||
|
// auto-rotation logic. See Config.ticketKeys.
|
||||||
|
autoSessionTicketKeys []ticketKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// ticketKey is the internal representation of a session ticket key.
|
||||||
|
type ticketKey struct {
|
||||||
|
aesKey [16]byte
|
||||||
|
hmacKey [16]byte
|
||||||
|
// created is the time at which this ticket key was created. See Config.ticketKeys.
|
||||||
|
created time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Certificate is a chain of one or more certificates, leaf first.
|
||||||
|
type Certificate struct {
|
||||||
|
Certificate [][]byte
|
||||||
|
// PrivateKey contains the private key corresponding to the public key in
|
||||||
|
// Leaf. This must implement crypto.Signer with an RSA, ECDSA or Ed25519 PublicKey.
|
||||||
|
// For a server up to TLS 1.2, it can also implement crypto.Decrypter with
|
||||||
|
// an RSA PublicKey.
|
||||||
|
PrivateKey crypto.PrivateKey
|
||||||
|
// SupportedSignatureAlgorithms is an optional list restricting what
|
||||||
|
// signature algorithms the PrivateKey can be used for.
|
||||||
|
SupportedSignatureAlgorithms []SignatureScheme
|
||||||
|
// OCSPStaple contains an optional OCSP response which will be served
|
||||||
|
// to clients that request it.
|
||||||
|
OCSPStaple []byte
|
||||||
|
// SignedCertificateTimestamps contains an optional list of Signed
|
||||||
|
// Certificate Timestamps which will be served to clients that request it.
|
||||||
|
SignedCertificateTimestamps [][]byte
|
||||||
|
// Leaf is the parsed form of the leaf certificate, which may be initialized
|
||||||
|
// using x509.ParseCertificate to reduce per-handshake processing. If nil,
|
||||||
|
// the leaf certificate will be parsed as needed.
|
||||||
|
Leaf *x509.Certificate
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2012 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 tls
|
||||||
|
|
||||||
|
// A SessionState is a resumable session.
|
||||||
|
type SessionState struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientSessionState contains the state needed by a client to
|
||||||
|
// resume a previous TLS session.
|
||||||
|
type ClientSessionState struct {
|
||||||
|
ticket []byte
|
||||||
|
session *SessionState
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// TINYGO: The following is copied and modified from Go 1.21.4 official implementation.
|
||||||
|
|
||||||
|
// Copyright 2009 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 tls partially implements TLS 1.2, as specified in RFC 5246,
|
||||||
|
// and TLS 1.3, as specified in RFC 8446.
|
||||||
|
package tls
|
||||||
|
|
||||||
|
// BUG(agl): The crypto/tls package only implements some countermeasures
|
||||||
|
// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
|
||||||
|
// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
|
||||||
|
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client returns a new TLS client side connection
|
||||||
|
// using conn as the underlying transport.
|
||||||
|
// The config cannot be nil: users must set either ServerName or
|
||||||
|
// InsecureSkipVerify in the config.
|
||||||
|
func Client(conn net.Conn, config *Config) *net.TLSConn {
|
||||||
|
panic("tls.Client() not implemented")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A listener implements a network listener (net.Listener) for TLS connections.
|
||||||
|
type listener struct {
|
||||||
|
net.Listener
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListener creates a Listener which accepts connections from an inner
|
||||||
|
// Listener and wraps each connection with Server.
|
||||||
|
// The configuration config must be non-nil and must include
|
||||||
|
// at least one certificate or else set GetCertificate.
|
||||||
|
func NewListener(inner net.Listener, config *Config) net.Listener {
|
||||||
|
l := new(listener)
|
||||||
|
l.Listener = inner
|
||||||
|
l.config = config
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialWithDialer connects to the given network address using dialer.Dial and
|
||||||
|
// then initiates a TLS handshake, returning the resulting TLS connection. Any
|
||||||
|
// timeout or deadline given in the dialer apply to connection and TLS
|
||||||
|
// handshake as a whole.
|
||||||
|
//
|
||||||
|
// DialWithDialer interprets a nil configuration as equivalent to the zero
|
||||||
|
// configuration; see the documentation of Config for the defaults.
|
||||||
|
//
|
||||||
|
// DialWithDialer uses context.Background internally; to specify the context,
|
||||||
|
// use Dialer.DialContext with NetDialer set to the desired dialer.
|
||||||
|
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*net.TLSConn, error) {
|
||||||
|
switch network {
|
||||||
|
case "tcp", "tcp4":
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Network %s not supported", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.DialTLS(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial connects to the given network address using net.Dial
|
||||||
|
// and then initiates a TLS handshake, returning the resulting
|
||||||
|
// TLS connection.
|
||||||
|
// Dial interprets a nil configuration as equivalent to
|
||||||
|
// the zero configuration; see the documentation of Config
|
||||||
|
// for the defaults.
|
||||||
|
func Dial(network, addr string, config *Config) (*net.TLSConn, error) {
|
||||||
|
return DialWithDialer(new(net.Dialer), network, addr, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialer dials TLS connections given a configuration and a Dialer for the
|
||||||
|
// underlying connection.
|
||||||
|
type Dialer struct {
|
||||||
|
// NetDialer is the optional dialer to use for the TLS connections'
|
||||||
|
// underlying TCP connections.
|
||||||
|
// A nil NetDialer is equivalent to the net.Dialer zero value.
|
||||||
|
NetDialer *net.Dialer
|
||||||
|
|
||||||
|
// Config is the TLS configuration to use for new connections.
|
||||||
|
// A nil configuration is equivalent to the zero
|
||||||
|
// configuration; see the documentation of Config for the
|
||||||
|
// defaults.
|
||||||
|
Config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialContext connects to the given network address and initiates a TLS
|
||||||
|
// handshake, returning the resulting TLS connection.
|
||||||
|
//
|
||||||
|
// The provided Context must be non-nil. If the context expires before
|
||||||
|
// the connection is complete, an error is returned. Once successfully
|
||||||
|
// connected, any expiration of the context will not affect the
|
||||||
|
// connection.
|
||||||
|
//
|
||||||
|
// The returned Conn, if any, will always be of type *Conn.
|
||||||
|
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
return nil, errors.New("tls:DialContext not implmented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadX509KeyPair reads and parses a public/private key pair from a pair
|
||||||
|
// of files. The files must contain PEM encoded data. The certificate file
|
||||||
|
// may contain intermediate certificates following the leaf certificate to
|
||||||
|
// form a certificate chain. On successful return, Certificate.Leaf will
|
||||||
|
// be nil because the parsed form of the certificate is not retained.
|
||||||
|
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
|
||||||
|
return Certificate{}, errors.New("tls:LoadX509KeyPair not implemented")
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
// For efficiency, it's best to get the device ID once and cache it
|
||||||
|
// (e.g. on RP2040 XIP flash and interrupts disabled for period of
|
||||||
|
// retrieving the hardware ID from ROM chip)
|
||||||
|
id := machine.DeviceID()
|
||||||
|
|
||||||
|
for {
|
||||||
|
println("Device ID:", hex.EncodeToString(id))
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// this is intended to be used as wasm32-unknown-unknown module.
|
||||||
|
// to compile it, run:
|
||||||
|
// tinygo build -size short -o hello-unknown.wasm -target wasm-unknown -gc=leaking -no-debug ./src/examples/hello-wasm-unknown/
|
||||||
|
package main
|
||||||
|
|
||||||
|
var x int32
|
||||||
|
|
||||||
|
//go:wasmimport hosted echo_i32
|
||||||
|
func echo(x int32)
|
||||||
|
|
||||||
|
//go:export update
|
||||||
|
func update() {
|
||||||
|
x++
|
||||||
|
echo(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// to override the USB Manufacturer or Product names:
|
// to override the USB Manufacturer or Product names:
|
||||||
//
|
//
|
||||||
// tinygo flash -target circuitplay-express -ldflags="-X main.usbManufacturer='TinyGopher Labs' -X main.usbProduct='GopherKeyboard'" examples/hid-keyboard
|
// tinygo flash -target circuitplay-express -ldflags="-X main.usbManufacturer='TinyGopher Labs' -X main.usbProduct='GopherKeyboard' -X main.usbSerial='XXXXX'" examples/hid-keyboard
|
||||||
//
|
//
|
||||||
// you can also override the VID/PID. however, only set this if you know what you are doing,
|
// you can also override the VID/PID. however, only set this if you know what you are doing,
|
||||||
// since changing it can make it difficult to reflash some devices.
|
// since changing it can make it difficult to reflash some devices.
|
||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var usbVID, usbPID string
|
var usbVID, usbPID string
|
||||||
var usbManufacturer, usbProduct string
|
var usbManufacturer, usbProduct, usbSerial string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
button := machine.BUTTON
|
button := machine.BUTTON
|
||||||
@@ -49,4 +49,8 @@ func init() {
|
|||||||
if usbProduct != "" {
|
if usbProduct != "" {
|
||||||
usb.Product = usbProduct
|
usb.Product = usbProduct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if usbSerial != "" {
|
||||||
|
usb.Serial = usbSerial
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ func main() {
|
|||||||
|
|
||||||
// Schedule and enable recurring interrupt.
|
// Schedule and enable recurring interrupt.
|
||||||
// The callback function is executed in the context of an interrupt handler,
|
// The callback function is executed in the context of an interrupt handler,
|
||||||
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
|
// so regular restrictions for this sort of code apply: no blocking, no memory allocation, etc.
|
||||||
|
// Please check the online documentation for the details about interrupts:
|
||||||
|
// https://tinygo.org/docs/concepts/compiler-internals/interrupts/
|
||||||
delay := time.Minute + 12*time.Second
|
delay := time.Minute + 12*time.Second
|
||||||
machine.RTC.SetInterrupt(uint32(delay.Seconds()), true, func() { println("Peekaboo!") })
|
machine.RTC.SetInterrupt(uint32(delay.Seconds()), true, func() { println("Peekaboo!") })
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
package bytealg
|
package bytealg
|
||||||
|
|
||||||
|
// Some code in this file has been copied from the Go source code, and has
|
||||||
|
// copyright of their original authors:
|
||||||
|
//
|
||||||
|
// Copyright 2020 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.
|
||||||
|
//
|
||||||
|
// This is indicated specifically in the file.
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Index can search any valid length of string.
|
// Index can search any valid length of string.
|
||||||
|
|
||||||
@@ -125,10 +134,6 @@ func IndexString(str, sub string) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copyright 2020 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.
|
|
||||||
|
|
||||||
// The following code has been copied from the Go 1.15 release tree.
|
// The following code has been copied from the Go 1.15 release tree.
|
||||||
|
|
||||||
// PrimeRK is the prime base used in Rabin-Karp algorithm.
|
// PrimeRK is the prime base used in Rabin-Karp algorithm.
|
||||||
@@ -136,6 +141,8 @@ const PrimeRK = 16777619
|
|||||||
|
|
||||||
// HashStrBytes returns the hash and the appropriate multiplicative
|
// HashStrBytes returns the hash and the appropriate multiplicative
|
||||||
// factor for use in Rabin-Karp algorithm.
|
// factor for use in Rabin-Karp algorithm.
|
||||||
|
//
|
||||||
|
// This function was removed in Go 1.22.
|
||||||
func HashStrBytes(sep []byte) (uint32, uint32) {
|
func HashStrBytes(sep []byte) (uint32, uint32) {
|
||||||
hash := uint32(0)
|
hash := uint32(0)
|
||||||
for i := 0; i < len(sep); i++ {
|
for i := 0; i < len(sep); i++ {
|
||||||
@@ -153,7 +160,9 @@ func HashStrBytes(sep []byte) (uint32, uint32) {
|
|||||||
|
|
||||||
// HashStr returns the hash and the appropriate multiplicative
|
// HashStr returns the hash and the appropriate multiplicative
|
||||||
// factor for use in Rabin-Karp algorithm.
|
// factor for use in Rabin-Karp algorithm.
|
||||||
func HashStr(sep string) (uint32, uint32) {
|
//
|
||||||
|
// This function was removed in Go 1.22.
|
||||||
|
func HashStr[T string | []byte](sep T) (uint32, uint32) {
|
||||||
hash := uint32(0)
|
hash := uint32(0)
|
||||||
for i := 0; i < len(sep); i++ {
|
for i := 0; i < len(sep); i++ {
|
||||||
hash = hash*PrimeRK + uint32(sep[i])
|
hash = hash*PrimeRK + uint32(sep[i])
|
||||||
@@ -170,6 +179,8 @@ func HashStr(sep string) (uint32, uint32) {
|
|||||||
|
|
||||||
// HashStrRevBytes returns the hash of the reverse of sep and the
|
// HashStrRevBytes returns the hash of the reverse of sep and the
|
||||||
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
||||||
|
//
|
||||||
|
// This function was removed in Go 1.22.
|
||||||
func HashStrRevBytes(sep []byte) (uint32, uint32) {
|
func HashStrRevBytes(sep []byte) (uint32, uint32) {
|
||||||
hash := uint32(0)
|
hash := uint32(0)
|
||||||
for i := len(sep) - 1; i >= 0; i-- {
|
for i := len(sep) - 1; i >= 0; i-- {
|
||||||
@@ -187,7 +198,9 @@ func HashStrRevBytes(sep []byte) (uint32, uint32) {
|
|||||||
|
|
||||||
// HashStrRev returns the hash of the reverse of sep and the
|
// HashStrRev returns the hash of the reverse of sep and the
|
||||||
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
||||||
func HashStrRev(sep string) (uint32, uint32) {
|
//
|
||||||
|
// Copied from the Go 1.22rc1 source tree.
|
||||||
|
func HashStrRev[T string | []byte](sep T) (uint32, uint32) {
|
||||||
hash := uint32(0)
|
hash := uint32(0)
|
||||||
for i := len(sep) - 1; i >= 0; i-- {
|
for i := len(sep) - 1; i >= 0; i-- {
|
||||||
hash = hash*PrimeRK + uint32(sep[i])
|
hash = hash*PrimeRK + uint32(sep[i])
|
||||||
@@ -204,6 +217,8 @@ func HashStrRev(sep string) (uint32, uint32) {
|
|||||||
|
|
||||||
// IndexRabinKarpBytes uses the Rabin-Karp search algorithm to return the index of the
|
// IndexRabinKarpBytes uses the Rabin-Karp search algorithm to return the index of the
|
||||||
// first occurence of substr in s, or -1 if not present.
|
// first occurence of substr in s, or -1 if not present.
|
||||||
|
//
|
||||||
|
// This function was removed in Go 1.22.
|
||||||
func IndexRabinKarpBytes(s, sep []byte) int {
|
func IndexRabinKarpBytes(s, sep []byte) int {
|
||||||
// Rabin-Karp search
|
// Rabin-Karp search
|
||||||
hashsep, pow := HashStrBytes(sep)
|
hashsep, pow := HashStrBytes(sep)
|
||||||
@@ -228,16 +243,18 @@ func IndexRabinKarpBytes(s, sep []byte) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IndexRabinKarp uses the Rabin-Karp search algorithm to return the index of the
|
// IndexRabinKarp uses the Rabin-Karp search algorithm to return the index of the
|
||||||
// first occurence of substr in s, or -1 if not present.
|
// first occurrence of sep in s, or -1 if not present.
|
||||||
func IndexRabinKarp(s, substr string) int {
|
//
|
||||||
|
// Copied from the Go 1.22rc1 source tree.
|
||||||
|
func IndexRabinKarp[T string | []byte](s, sep T) int {
|
||||||
// Rabin-Karp search
|
// Rabin-Karp search
|
||||||
hashss, pow := HashStr(substr)
|
hashss, pow := HashStr(sep)
|
||||||
n := len(substr)
|
n := len(sep)
|
||||||
var h uint32
|
var h uint32
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
h = h*PrimeRK + uint32(s[i])
|
h = h*PrimeRK + uint32(s[i])
|
||||||
}
|
}
|
||||||
if h == hashss && s[:n] == substr {
|
if h == hashss && string(s[:n]) == string(sep) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
for i := n; i < len(s); {
|
for i := n; i < len(s); {
|
||||||
@@ -245,7 +262,7 @@ func IndexRabinKarp(s, substr string) int {
|
|||||||
h += uint32(s[i])
|
h += uint32(s[i])
|
||||||
h -= pow * uint32(s[i-n])
|
h -= pow * uint32(s[i-n])
|
||||||
i++
|
i++
|
||||||
if h == hashss && s[i-n:i] == substr {
|
if h == hashss && string(s[i-n:i]) == string(sep) {
|
||||||
return i - n
|
return i - n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,3 +278,50 @@ func MakeNoZero(n int) []byte {
|
|||||||
// malloc function implemented in the runtime).
|
// malloc function implemented in the runtime).
|
||||||
return make([]byte, n)
|
return make([]byte, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copied from the Go 1.22rc1 source tree.
|
||||||
|
func LastIndexByte(s []byte, c byte) int {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if s[i] == c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from the Go 1.22rc1 source tree.
|
||||||
|
func LastIndexByteString(s string, c byte) int {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if s[i] == c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastIndexRabinKarp uses the Rabin-Karp search algorithm to return the last index of the
|
||||||
|
// occurrence of sep in s, or -1 if not present.
|
||||||
|
//
|
||||||
|
// Copied from the Go 1.22rc1 source tree.
|
||||||
|
func LastIndexRabinKarp[T string | []byte](s, sep T) int {
|
||||||
|
// Rabin-Karp search from the end of the string
|
||||||
|
hashss, pow := HashStrRev(sep)
|
||||||
|
n := len(sep)
|
||||||
|
last := len(s) - n
|
||||||
|
var h uint32
|
||||||
|
for i := len(s) - 1; i >= last; i-- {
|
||||||
|
h = h*PrimeRK + uint32(s[i])
|
||||||
|
}
|
||||||
|
if h == hashss && string(s[last:]) == string(sep) {
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
for i := last - 1; i >= 0; i-- {
|
||||||
|
h *= PrimeRK
|
||||||
|
h += uint32(s[i])
|
||||||
|
h -= pow * uint32(s[i+n])
|
||||||
|
if h == hashss && string(s[i:i+n]) == string(sep) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build tinygo
|
||||||
|
|
||||||
// Only generate .debug_frame, don't generate .eh_frame.
|
// Only generate .debug_frame, don't generate .eh_frame.
|
||||||
.cfi_sections .debug_frame
|
.cfi_sections .debug_frame
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build tinygo
|
||||||
|
|
||||||
.section .bss.tinygo_systemStack
|
.section .bss.tinygo_systemStack
|
||||||
.global tinygo_systemStack
|
.global tinygo_systemStack
|
||||||
.type tinygo_systemStack, %object
|
.type tinygo_systemStack, %object
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build tinygo
|
||||||
|
|
||||||
// Only generate .debug_frame, don't generate .eh_frame.
|
// Only generate .debug_frame, don't generate .eh_frame.
|
||||||
.cfi_sections .debug_frame
|
.cfi_sections .debug_frame
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user