mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c660750636 | |||
| 2320b18953 | |||
| 3b1913ac57 | |||
| 1da1abe314 | |||
| c9721197d5 | |||
| 738747bd05 | |||
| 0bb4a8abeb | |||
| 6ef8fcd537 | |||
| a896f7f218 | |||
| 081d2e58c6 | |||
| ed2a98c7d0 | |||
| 731532cd2b | |||
| adadbadec3 | |||
| 58fafaeb5c | |||
| 13a8eae0d4 | |||
| 42da7654ec | |||
| 8698a7e496 | |||
| 1d7543e2bf | |||
| ff32fbbb4f | |||
| 3ad2530ee7 | |||
| c4de195f04 | |||
| 75aca0f5ee | |||
| bb9a9beed5 | |||
| 4042c1d618 | |||
| bf73516259 | |||
| 43d282174f | |||
| 0042bf62a5 | |||
| f11731ff35 | |||
| e9d8a9bc0b | |||
| 9d6eb1ff06 | |||
| 4643401a1d | |||
| dc449882ad | |||
| a158d3194f | |||
| 806498f099 | |||
| e3bc6da9e4 | |||
| 3e2471d934 | |||
| a545f17d2e | |||
| f4375d0452 | |||
| 756cdf44ed | |||
| 9037bf8bf0 | |||
| bfe9ee378f | |||
| 37a4fa205c | |||
| 59cc7d4c42 | |||
| ab64e215dd | |||
| 253dbe335a | |||
| 0ef86e1534 | |||
| 72270f9052 | |||
| 67ec722a74 | |||
| 62294feb56 | |||
| f1e25a18d2 | |||
| a2f886a67a | |||
| f791c821ff | |||
| a93f0ed12a | |||
| c25dd0a972 | |||
| 215dd3f0be | |||
| c51f5cea0b | |||
| 395ee2d338 | |||
| 069e4f0d98 | |||
| d1eb642d45 | |||
| 5f2e72f371 | |||
| 5b581d83b3 | |||
| d845f1e1b2 | |||
| 00d46bd25d | |||
| 01d2ef327d | |||
| 4da1f6bcbe | |||
| 14ddba8519 | |||
| a7b205c26c | |||
| c83f712c17 | |||
| fffad84a63 | |||
| e075e0591d | |||
| 46d2696363 | |||
| e98dfd6c46 | |||
| 9126b95737 | |||
| 3871b831af | |||
| 7755f2385c | |||
| 04601a29e8 | |||
| 9aadea930f | |||
| 6efa94035e | |||
| db8d80755f | |||
| dc91c96305 | |||
| 08b3a4576d | |||
| acba0748f1 | |||
| ef72c5bb4e | |||
| 93cc03b15a | |||
| fdc4bbbfad | |||
| dd4e9e86e7 | |||
| ad32d26511 | |||
| 4619896c19 | |||
| cec237917f | |||
| 4d2a6d2bbe | |||
| 6d5f4c4be2 | |||
| 0cb5d336f4 | |||
| 91ee4d0030 | |||
| 785eb93b62 | |||
| e041a8ed88 | |||
| d01d85930d | |||
| ac821d8295 |
@@ -10,12 +10,12 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-14-v3
|
- llvm-source-16-v3
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-14-v3
|
key: llvm-source-16-v3
|
||||||
paths:
|
paths:
|
||||||
- llvm-project/clang/lib/Headers
|
- llvm-project/clang/lib/Headers
|
||||||
- llvm-project/clang/include
|
- llvm-project/clang/include
|
||||||
@@ -89,7 +89,7 @@ commands:
|
|||||||
# formatting of generated files.
|
# formatting of generated files.
|
||||||
name: Check Go code formatting
|
name: Check Go code formatting
|
||||||
command: make fmt-check
|
command: make fmt-check
|
||||||
- run: make gen-device -j4
|
- run: make gen-device
|
||||||
- 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-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
@@ -98,12 +98,12 @@ commands:
|
|||||||
- /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-buster
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "14"
|
llvm: "15"
|
||||||
resource_class: large
|
resource_class: large
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
@@ -111,4 +111,4 @@ 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
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-15-macos-v3
|
key: llvm-source-16-macos-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-15-macos-v4
|
key: llvm-build-16-macos-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
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: make gen-device
|
- name: make gen-device
|
||||||
run: make -j3 gen-device
|
run: make gen-device
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make test GOTESTFLAGS="-short"
|
run: make test GOTESTFLAGS="-short"
|
||||||
@@ -108,7 +108,7 @@ jobs:
|
|||||||
# 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@v2
|
||||||
with:
|
with:
|
||||||
name: release-double-zipped
|
name: darwin-amd64-double-zipped
|
||||||
path: build/tinygo.darwin-amd64.tar.gz
|
path: build/tinygo.darwin-amd64.tar.gz
|
||||||
- name: Smoke tests
|
- name: Smoke tests
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -120,13 +120,13 @@ jobs:
|
|||||||
- name: Install LLVM
|
- name: Install LLVM
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@16
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Build TinyGo
|
- name: Build TinyGo
|
||||||
run: go install
|
run: go install
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
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-15
|
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
|
||||||
|
|||||||
+37
-140
@@ -18,7 +18,7 @@ jobs:
|
|||||||
# statically linked binary.
|
# statically linked binary.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: golang:1.20-alpine
|
image: golang:1.21-alpine
|
||||||
steps:
|
steps:
|
||||||
- name: Install apk dependencies
|
- name: Install apk dependencies
|
||||||
# tar: needed for actions/cache@v3
|
# tar: needed for actions/cache@v3
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-15-linux-alpine-v3
|
key: llvm-source-16-linux-alpine-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-15-linux-alpine-v4
|
key: llvm-build-16-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'
|
||||||
@@ -135,7 +135,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
@@ -153,6 +153,7 @@ jobs:
|
|||||||
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
|
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
|
||||||
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||||
- run: make tinygo-test-wasi-fast
|
- run: make tinygo-test-wasi-fast
|
||||||
|
- run: make tinygo-test-wasip1-fast
|
||||||
- run: make smoketest
|
- run: make smoketest
|
||||||
assert-test-linux:
|
assert-test-linux:
|
||||||
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
||||||
@@ -177,12 +178,12 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '16'
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||||
@@ -193,7 +194,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-15-linux-asserts-v3
|
key: llvm-source-16-linux-asserts-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -218,7 +219,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-15-linux-asserts-v4
|
key: llvm-build-16-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'
|
||||||
@@ -254,7 +255,7 @@ jobs:
|
|||||||
- 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
|
||||||
- run: make gen-device -j4
|
- run: make gen-device
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
run: make ASSERT=1 test
|
run: make ASSERT=1 test
|
||||||
- name: Build TinyGo
|
- name: Build TinyGo
|
||||||
@@ -266,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
|
||||||
@@ -275,6 +276,16 @@ 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:
|
||||||
@@ -285,18 +296,18 @@ jobs:
|
|||||||
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@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-15-linux-v3
|
key: llvm-source-16-linux-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -321,7 +332,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-15-linux-arm-v4
|
key: llvm-build-16-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'
|
||||||
@@ -332,7 +343,7 @@ 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
|
||||||
@@ -345,14 +356,14 @@ jobs:
|
|||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
id: cache-binaryen
|
id: cache-binaryen
|
||||||
with:
|
with:
|
||||||
key: binaryen-linux-arm-v1
|
key: binaryen-linux-${{ matrix.goarch }}-v1
|
||||||
path: build/wasm-opt
|
path: build/wasm-opt
|
||||||
- name: Build Binaryen
|
- name: Build Binaryen
|
||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
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 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
|
||||||
@@ -360,7 +371,7 @@ 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@v3
|
||||||
with:
|
with:
|
||||||
@@ -373,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@v3
|
||||||
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.20'
|
|
||||||
cache: true
|
|
||||||
- name: Restore LLVM source cache
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
id: cache-llvm-source
|
|
||||||
with:
|
|
||||||
key: llvm-source-15-linux-v3
|
|
||||||
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-15-linux-arm64-v4
|
|
||||||
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
|
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ jobs:
|
|||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
tinygo/llvm-15
|
tinygo/llvm-16
|
||||||
ghcr.io/${{ github.repository_owner }}/llvm-15
|
ghcr.io/${{ github.repository_owner }}/llvm-16
|
||||||
tags: |
|
tags: |
|
||||||
type=sha,format=long
|
type=sha,format=long
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
|
|||||||
@@ -24,19 +24,19 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
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 -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends -y \
|
sudo apt-get install --no-install-recommends -y \
|
||||||
llvm-15-dev \
|
llvm-16-dev \
|
||||||
clang-15 \
|
clang-16 \
|
||||||
libclang-15-dev \
|
libclang-16-dev \
|
||||||
lld-15
|
lld-16
|
||||||
- name: Restore LLVM source cache
|
- name: Restore LLVM source cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-15-sizediff-v1
|
key: llvm-source-16-sizediff-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/compiler-rt
|
llvm-project/compiler-rt
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
- run: make gen-device -j4
|
- run: make gen-device
|
||||||
- name: Download drivers repo
|
- name: Download drivers repo
|
||||||
run: git clone https://github.com/tinygo-org/drivers.git
|
run: git clone https://github.com/tinygo-org/drivers.git
|
||||||
- name: Save HEAD
|
- name: Save HEAD
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Restore cached LLVM source
|
- name: Restore cached LLVM source
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-15-windows-v4
|
key: llvm-source-16-windows-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-15-windows-v6
|
key: llvm-build-16-windows-v2
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
scoop install wasmtime
|
scoop install wasmtime
|
||||||
- name: make gen-device
|
- name: make gen-device
|
||||||
run: make -j3 gen-device
|
run: make gen-device
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make test GOTESTFLAGS="-short"
|
run: make test GOTESTFLAGS="-short"
|
||||||
@@ -118,7 +118,7 @@ jobs:
|
|||||||
# 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@v3
|
||||||
with:
|
with:
|
||||||
name: release-double-zipped
|
name: windows-amd64-double-zipped
|
||||||
path: build/release/release.zip
|
path: build/release/release.zip
|
||||||
|
|
||||||
smoke-test-windows:
|
smoke-test-windows:
|
||||||
@@ -143,12 +143,12 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: release-double-zipped
|
name: windows-amd64-double-zipped
|
||||||
path: build/
|
path: build/
|
||||||
- name: Unzip TinyGo build
|
- name: Unzip TinyGo build
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -173,12 +173,12 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: release-double-zipped
|
name: windows-amd64-double-zipped
|
||||||
path: build/
|
path: build/
|
||||||
- name: Unzip TinyGo build
|
- name: Unzip TinyGo build
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -209,12 +209,12 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version: '1.21'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download TinyGo build
|
- name: Download TinyGo build
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: release-double-zipped
|
name: windows-amd64-double-zipped
|
||||||
path: build/
|
path: build/
|
||||||
- name: Unzip TinyGo build
|
- name: Unzip TinyGo build
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -31,3 +31,6 @@
|
|||||||
[submodule "lib/macos-minimal-sdk"]
|
[submodule "lib/macos-minimal-sdk"]
|
||||||
path = lib/macos-minimal-sdk
|
path = lib/macos-minimal-sdk
|
||||||
url = https://github.com/aykevl/macos-minimal-sdk.git
|
url = https://github.com/aykevl/macos-minimal-sdk.git
|
||||||
|
[submodule "lib/renesas-svd"]
|
||||||
|
path = lib/renesas-svd
|
||||||
|
url = https://github.com/tinygo-org/renesas-svd.git
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,3 +1,96 @@
|
|||||||
|
0.30.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **general**
|
||||||
|
- add LLVM 16 support, use it by default
|
||||||
|
* **compiler**
|
||||||
|
- `build`: work around a race condition by building Go SSA serially
|
||||||
|
- `compiler`: fix a crash by not using the LLVM global context types
|
||||||
|
- `interp`: don't copy unknown values in `runtime.sliceCopy` to fix miscompile
|
||||||
|
- `interp`: fix crash in error report by not returning raw LLVM values
|
||||||
|
* **standard library**
|
||||||
|
- `machine/usb/adc/midi`: various improvements and API changes
|
||||||
|
- `reflect`: add support for `[...]T` → `[]T` in reflect
|
||||||
|
* **targets**
|
||||||
|
- `atsamd21`, `atsamd51`: add support for USB INTERRUPT OUT
|
||||||
|
- `rp2040`: always use the USB device enumeration fix, even in chips that supposedly have the HW fix
|
||||||
|
- `wasm`: increase default stack size to 32k for wasi/wasm
|
||||||
|
* **boards**
|
||||||
|
- `gobadge`: add GoBadge target as alias for PyBadge :)
|
||||||
|
- `gemma-m0`: add support for the Adafruit Gemma M0
|
||||||
|
|
||||||
|
|
||||||
|
0.29.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **general**
|
||||||
|
- Go 1.21 support
|
||||||
|
- use https for renesas submodule #3856
|
||||||
|
- ci: rename release-double-zipped to something more useful
|
||||||
|
- ci: update Node.js from version 14 to version 16
|
||||||
|
- ci: switch GH actions builds to use Go 1.21 final release
|
||||||
|
- docker: update clang to version 15
|
||||||
|
- docker: use Go 1.21 for Docker dev container build
|
||||||
|
- `main`: add target JSON file in `tinygo info` output
|
||||||
|
- `main`: improve detection of filesystems
|
||||||
|
- `main`: use `go env` instead of doing all detection manually
|
||||||
|
- make: add make task to generate Renesas device wrappers
|
||||||
|
- make: add task to check NodeJS version before running tests
|
||||||
|
- add submodule for Renesas SVD file mirror repo
|
||||||
|
- update to go-serial package v1.6.0
|
||||||
|
- `testing`: add Testing function
|
||||||
|
- `tools/gen-device-svd`: small changes needed for Renesas MCUs
|
||||||
|
* **compiler**
|
||||||
|
- `builder`: update message for max supported Go version
|
||||||
|
- `compiler,reflect`: NumMethods reports exported methods only
|
||||||
|
- `compiler`: add compiler-rt and wasm symbols to table
|
||||||
|
- `compiler`: add compiler-rt to wasm.json
|
||||||
|
- `compiler`: add min and max builtin support
|
||||||
|
- `compiler`: implement clear builtin for maps
|
||||||
|
- `compiler`: implement clear builtin for slices
|
||||||
|
- `compiler`: improve panic message when a runtime call is unavailable
|
||||||
|
- `compiler`: update .ll test output
|
||||||
|
- `loader`: merge go.env file which is now required starting in Go 1.21 to correctly get required packages
|
||||||
|
* **standard library**
|
||||||
|
- `os`: define ErrNoDeadline
|
||||||
|
- `reflect`: Add FieldByNameFunc
|
||||||
|
- `reflect`: add SetZero
|
||||||
|
- `reflect`: fix iterating over maps with interface{} keys
|
||||||
|
- `reflect`: implement Value.Grow
|
||||||
|
- `reflect`: remove unecessary heap allocations
|
||||||
|
- `reflect`: use .key() instead of a type assert
|
||||||
|
- `sync`: add implementation from upstream Go for OnceFunc, OnceValue, and OnceValues
|
||||||
|
* **targets**
|
||||||
|
- `machine`: UART refactor (#3832)
|
||||||
|
- `machine/avr`: pin change interrupt
|
||||||
|
- `machine/macropad_rp2040`: add machine.BUTTON
|
||||||
|
- `machine/nrf`: add I2C timeout
|
||||||
|
- `machine/nrf`: wait for stop condition after reading from the I2C bus
|
||||||
|
- `machine/nRF52`: set SPI TX/RX lengths even data is empty. Fixes #3868 (#3877)
|
||||||
|
- `machine/rp2040`: add missing suffix to CMD_READ_STATUS
|
||||||
|
- `machine/rp2040`: add NoPin support
|
||||||
|
- `machine/rp2040`: move flash related functions into separate file from C imports for correct - LSP. Fixes #3852
|
||||||
|
- `machine/rp2040`: wait for 1000 us after flash reset to avoid issues with busy USB bus
|
||||||
|
- `machine/samd51,rp2040,nrf528xx,stm32`: implement watchdog
|
||||||
|
- `machine/samd51`: fix i2cTimeout was decreasing due to cache activation
|
||||||
|
- `machine/usb`: Add support for HID Keyboard LEDs
|
||||||
|
- `machine/usb`: allow USB Endpoint settings to be changed externally
|
||||||
|
- `machine/usb`: refactor endpoint configuration
|
||||||
|
- `machine/usb`: remove usbDescriptorConfig
|
||||||
|
- `machine/usb/hid,joystick`: fix hidreport (3) (#3802)
|
||||||
|
- `machine/usb/hid`: add RxHandler interface
|
||||||
|
- `machine/usb/hid`: rename Handler() to TxHandler()
|
||||||
|
- `wasi`: allow zero inodes when reading directories
|
||||||
|
- `wasm`: add support for GOOS=wasip1
|
||||||
|
- `wasm`: fix functions exported through //export
|
||||||
|
- `wasm`: remove i64 workaround, use BigInt instead
|
||||||
|
- `example`: adjust time offset
|
||||||
|
- `example`: simplify pininterrupt
|
||||||
|
* **boards**
|
||||||
|
- `targets`: add AKIZUKI DENSHI AE-RP2040
|
||||||
|
- `targets`: adding new uf2 target for PCA10056 (#3765)
|
||||||
|
|
||||||
|
|
||||||
0.28.0
|
0.28.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||||
FROM golang:1.20 AS tinygo-llvm
|
FROM golang:1.21 AS tinygo-llvm
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y apt-utils make cmake clang-11 ninja-build
|
apt-get install -y apt-utils make cmake clang-15 ninja-build
|
||||||
|
|
||||||
COPY ./Makefile /tinygo/Makefile
|
COPY ./Makefile /tinygo/Makefile
|
||||||
|
|
||||||
|
|||||||
+34
-8
@@ -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 = 15 14 13 12 11
|
LLVM_VERSIONS = 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)
|
||||||
@@ -113,7 +113,7 @@ endif
|
|||||||
|
|
||||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||||
|
|
||||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
|
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendhlsl frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
@@ -154,7 +154,7 @@ LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
|
|||||||
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
||||||
|
|
||||||
# Other libraries that are needed to link TinyGo.
|
# Other libraries that are needed to link TinyGo.
|
||||||
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA
|
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMRISCVTargetMCA LLVMX86TargetMCA
|
||||||
|
|
||||||
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
|
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
|
||||||
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
|
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
|
||||||
@@ -170,7 +170,7 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(ad
|
|||||||
# For static linking.
|
# For static linking.
|
||||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||||
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||||
CGO_CXXFLAGS=-std=c++14
|
CGO_CXXFLAGS=-std=c++17
|
||||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -232,9 +232,13 @@ gen-device-rp: build/gen-device-svd
|
|||||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/RaspberryPi lib/cmsis-svd/data/RaspberryPi/ src/device/rp/
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/RaspberryPi lib/cmsis-svd/data/RaspberryPi/ src/device/rp/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/rp
|
GO111MODULE=off $(GO) fmt ./src/device/rp
|
||||||
|
|
||||||
|
gen-device-renesas: build/gen-device-svd
|
||||||
|
./build/gen-device-svd -source=https://github.com/tinygo-org/renesas-svd lib/renesas-svd/ src/device/renesas/
|
||||||
|
GO111MODULE=off $(GO) fmt ./src/device/renesas
|
||||||
|
|
||||||
# Get LLVM sources.
|
# Get LLVM sources.
|
||||||
$(LLVM_PROJECTDIR)/llvm:
|
$(LLVM_PROJECTDIR)/llvm:
|
||||||
git clone -b xtensa_release_15.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
git clone -b xtensa_release_16.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
||||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||||
|
|
||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
@@ -261,14 +265,24 @@ endif
|
|||||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||||
cd lib/wasi-libc && make -j4 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.
|
||||||
|
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
||||||
|
MIN_NODEJS_VERSION=16
|
||||||
|
|
||||||
|
.PHONY: check-nodejs-version
|
||||||
|
check-nodejs-version:
|
||||||
|
ifeq (, $(shell which node))
|
||||||
|
@echo "Install NodeJS version 16+ to run tests."; exit 1;
|
||||||
|
endif
|
||||||
|
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 16+ 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
|
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 .
|
||||||
|
|
||||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||||
@@ -413,8 +427,12 @@ tinygo-bench-fast:
|
|||||||
# Same thing, except for wasi rather than the current platform.
|
# Same thing, except for wasi rather than the current platform.
|
||||||
tinygo-test-wasi:
|
tinygo-test-wasi:
|
||||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||||
|
tinygo-test-wasip1:
|
||||||
|
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||||
tinygo-test-wasi-fast:
|
tinygo-test-wasi-fast:
|
||||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||||
|
tinygo-test-wasip1-fast:
|
||||||
|
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
|
||||||
tinygo-bench-wasi:
|
tinygo-bench-wasi:
|
||||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
||||||
tinygo-bench-wasi-fast:
|
tinygo-bench-wasi-fast:
|
||||||
@@ -478,12 +496,16 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/time-offset
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target
|
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
|
||||||
|
@$(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
|
||||||
@@ -536,6 +558,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=trinket-m0 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=trinket-m0 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=gemma-m0 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
||||||
@@ -642,6 +666,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 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
|
||||||
+19
-1
@@ -12,6 +12,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
wasm "github.com/aykevl/go-wasm"
|
||||||
"github.com/blakesmith/ar"
|
"github.com/blakesmith/ar"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -74,8 +75,25 @@ func makeArchive(arfile *os.File, objs []string) error {
|
|||||||
fileIndex int
|
fileIndex int
|
||||||
}{symbol.Name, i})
|
}{symbol.Name, i})
|
||||||
}
|
}
|
||||||
|
} else if dbg, err := wasm.Parse(objfile); err == nil {
|
||||||
|
for _, s := range dbg.Sections {
|
||||||
|
switch section := s.(type) {
|
||||||
|
case *wasm.SectionImport:
|
||||||
|
for _, ln := range section.Entries {
|
||||||
|
|
||||||
|
if ln.Kind != wasm.ExtKindFunction {
|
||||||
|
// Not a function
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
symbolTable = append(symbolTable, struct {
|
||||||
|
name string
|
||||||
|
fileIndex int
|
||||||
|
}{ln.Field, i})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("failed to open file %s as ELF or PE/COFF: %w", objpath, err)
|
return fmt.Errorf("failed to open file %s as WASM, ELF or PE/COFF: %w", objpath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close file, to avoid issues with too many open files (especially on
|
// Close file, to avoid issues with too many open files (especially on
|
||||||
|
|||||||
+26
-28
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +157,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(),
|
||||||
@@ -217,19 +216,27 @@ 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"] == "" {
|
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
||||||
version := goenv.Version
|
version := goenv.Version
|
||||||
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
|
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
|
||||||
version += "-" + goenv.GitSha1
|
version += "-" + goenv.GitSha1
|
||||||
}
|
}
|
||||||
if config.Options.GlobalValues == nil {
|
|
||||||
config.Options.GlobalValues = make(map[string]map[string]string)
|
|
||||||
}
|
|
||||||
if config.Options.GlobalValues["runtime"] == nil {
|
if config.Options.GlobalValues["runtime"] == nil {
|
||||||
config.Options.GlobalValues["runtime"] = make(map[string]string)
|
config.Options.GlobalValues["runtime"] = make(map[string]string)
|
||||||
}
|
}
|
||||||
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
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() {
|
||||||
@@ -313,7 +320,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 {
|
||||||
@@ -336,6 +342,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
|
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
|
||||||
|
|
||||||
|
// Build the SSA for the given package.
|
||||||
|
ssaPkg := program.Package(pkg.Pkg)
|
||||||
|
ssaPkg.Build()
|
||||||
|
|
||||||
// Now create the job to actually build the package. It will exit early
|
// Now create the job to actually build the package. It will exit early
|
||||||
// if the package is already compiled.
|
// if the package is already compiled.
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
@@ -520,13 +530,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()
|
||||||
|
|
||||||
@@ -731,17 +741,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"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -752,7 +762,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")
|
||||||
}
|
}
|
||||||
@@ -1054,21 +1064,9 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Browsers cannot handle external functions that have type i64 because it
|
// Run most of the whole-program optimizations (including the whole
|
||||||
// cannot be represented exactly in JavaScript (JS only has doubles). To
|
// O0/O1/O2/Os/Oz optimization pipeline).
|
||||||
// keep functions interoperable, pass int64 types as pointers to
|
errs := transform.Optimize(mod, config)
|
||||||
// stack-allocated values.
|
|
||||||
if config.WasmAbi() == "js" {
|
|
||||||
err := transform.ExternalInt64AsPtr(mod, config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimization levels here are roughly the same as Clang, but probably not
|
|
||||||
// exactly.
|
|
||||||
optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
|
|
||||||
errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold)
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return newMultiError(errs)
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
{GOOS: "darwin", GOARCH: "arm64"},
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
{GOOS: "windows", GOARCH: "amd64"},
|
{GOOS: "windows", GOARCH: "amd64"},
|
||||||
{GOOS: "windows", GOARCH: "arm64"},
|
{GOOS: "windows", GOARCH: "arm64"},
|
||||||
|
{GOOS: "wasip1", GOARCH: "wasm"},
|
||||||
} {
|
} {
|
||||||
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
|
||||||
if options.GOARCH == "arm" {
|
if options.GOARCH == "arm" {
|
||||||
|
|||||||
+19
-2
@@ -56,6 +56,7 @@
|
|||||||
#include "llvm/Support/Timer.h"
|
#include "llvm/Support/Timer.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace clang::driver;
|
using namespace clang::driver;
|
||||||
@@ -103,6 +104,14 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||||
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
|
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
|
||||||
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
|
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
|
||||||
|
if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
|
||||||
|
VersionTuple Version;
|
||||||
|
if (Version.tryParse(A->getValue()))
|
||||||
|
Diags.Report(diag::err_drv_invalid_value)
|
||||||
|
<< A->getAsString(Args) << A->getValue();
|
||||||
|
else
|
||||||
|
Opts.DarwinTargetVariantSDKVersion = Version;
|
||||||
|
}
|
||||||
|
|
||||||
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
|
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
|
||||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||||
@@ -122,11 +131,12 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
Opts.CompressDebugSections =
|
Opts.CompressDebugSections =
|
||||||
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||||
.Case("none", llvm::DebugCompressionType::None)
|
.Case("none", llvm::DebugCompressionType::None)
|
||||||
.Case("zlib", llvm::DebugCompressionType::Z)
|
.Case("zlib", llvm::DebugCompressionType::Zlib)
|
||||||
|
.Case("zstd", llvm::DebugCompressionType::Zstd)
|
||||||
.Default(llvm::DebugCompressionType::None);
|
.Default(llvm::DebugCompressionType::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
|
Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
|
||||||
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
|
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
|
||||||
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
|
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
|
||||||
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
||||||
@@ -189,6 +199,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
|
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
|
||||||
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
|
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
|
||||||
Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn);
|
Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn);
|
||||||
|
Opts.NoTypeCheck = Args.hasArg(OPT_mno_type_check);
|
||||||
Opts.RelocationModel =
|
Opts.RelocationModel =
|
||||||
std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic"));
|
std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic"));
|
||||||
Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi));
|
Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi));
|
||||||
@@ -214,6 +225,8 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
.Case("default", EmitDwarfUnwindType::Default);
|
.Case("default", EmitDwarfUnwindType::Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +278,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
MCTargetOptions MCOptions;
|
MCTargetOptions MCOptions;
|
||||||
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
||||||
|
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
||||||
|
|
||||||
std::unique_ptr<MCAsmInfo> MAI(
|
std::unique_ptr<MCAsmInfo> MAI(
|
||||||
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
|
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
|
||||||
@@ -314,6 +328,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
TheTarget->createMCObjectFileInfo(Ctx, PIC));
|
TheTarget->createMCObjectFileInfo(Ctx, PIC));
|
||||||
if (Opts.DarwinTargetVariantTriple)
|
if (Opts.DarwinTargetVariantTriple)
|
||||||
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
|
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
|
||||||
|
if (!Opts.DarwinTargetVariantSDKVersion.empty())
|
||||||
|
MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
|
||||||
Ctx.setObjectFileInfo(MOFI.get());
|
Ctx.setObjectFileInfo(MOFI.get());
|
||||||
|
|
||||||
if (Opts.SaveTemporaryLabels)
|
if (Opts.SaveTemporaryLabels)
|
||||||
@@ -353,6 +369,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
MCOptions.MCNoWarn = Opts.NoWarn;
|
MCOptions.MCNoWarn = Opts.NoWarn;
|
||||||
MCOptions.MCFatalWarnings = Opts.FatalWarnings;
|
MCOptions.MCFatalWarnings = Opts.FatalWarnings;
|
||||||
|
MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
|
||||||
MCOptions.ABIName = Opts.TargetABI;
|
MCOptions.ABIName = Opts.TargetABI;
|
||||||
|
|
||||||
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||||
|
|||||||
+10
-1
@@ -82,6 +82,7 @@ struct AssemblerInvocation {
|
|||||||
unsigned NoExecStack : 1;
|
unsigned NoExecStack : 1;
|
||||||
unsigned FatalWarnings : 1;
|
unsigned FatalWarnings : 1;
|
||||||
unsigned NoWarn : 1;
|
unsigned NoWarn : 1;
|
||||||
|
unsigned NoTypeCheck : 1;
|
||||||
unsigned IncrementalLinkerCompatible : 1;
|
unsigned IncrementalLinkerCompatible : 1;
|
||||||
unsigned EmbedBitcode : 1;
|
unsigned EmbedBitcode : 1;
|
||||||
|
|
||||||
@@ -97,7 +98,14 @@ struct AssemblerInvocation {
|
|||||||
|
|
||||||
/// Darwin target variant triple, the variant of the deployment target
|
/// Darwin target variant triple, the variant of the deployment target
|
||||||
/// for which the code is being compiled.
|
/// for which the code is being compiled.
|
||||||
llvm::Optional<llvm::Triple> DarwinTargetVariantTriple;
|
std::optional<llvm::Triple> DarwinTargetVariantTriple;
|
||||||
|
|
||||||
|
/// The version of the darwin target variant SDK which was used during the
|
||||||
|
/// compilation
|
||||||
|
llvm::VersionTuple DarwinTargetVariantSDKVersion;
|
||||||
|
|
||||||
|
/// The name of a file to use with \c .secure_log_unique directives.
|
||||||
|
std::string AsSecureLogFile;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -114,6 +122,7 @@ public:
|
|||||||
NoExecStack = 0;
|
NoExecStack = 0;
|
||||||
FatalWarnings = 0;
|
FatalWarnings = 0;
|
||||||
NoWarn = 0;
|
NoWarn = 0;
|
||||||
|
NoTypeCheck = 0;
|
||||||
IncrementalLinkerCompatible = 0;
|
IncrementalLinkerCompatible = 0;
|
||||||
Dwarf64 = 0;
|
Dwarf64 = 0;
|
||||||
DwarfVersion = 0;
|
DwarfVersion = 0;
|
||||||
|
|||||||
+4
-10
@@ -1,7 +1,6 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -24,19 +23,14 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
spec.OpenOCDCommands = options.OpenOCDCommands
|
spec.OpenOCDCommands = options.OpenOCDCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
goroot := goenv.Get("GOROOT")
|
major, minor, err := goenv.GetGorootVersion()
|
||||||
if goroot == "" {
|
|
||||||
return nil, errors.New("cannot locate $GOROOT, please set it manually")
|
|
||||||
}
|
|
||||||
|
|
||||||
major, minor, err := goenv.GetGorootVersion(goroot)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
if major != 1 || minor < 18 || minor > 20 {
|
if major != 1 || minor < 18 || minor > 21 {
|
||||||
// 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.20, got go%d.%d", major, minor)
|
return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
|
|||||||
@@ -178,12 +178,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
|
||||||
|
|
||||||
|
|||||||
+1
-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") },
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ func TestBinarySize(t *testing.T) {
|
|||||||
// This is a small number of very diverse targets that we want to test.
|
// This is a small number of very diverse targets that we want to test.
|
||||||
tests := []sizeTest{
|
tests := []sizeTest{
|
||||||
// microcontrollers
|
// microcontrollers
|
||||||
{"hifive1b", "examples/echo", 4612, 280, 0, 2252},
|
{"hifive1b", "examples/echo", 4484, 280, 0, 2252},
|
||||||
{"microbit", "examples/serial", 2724, 388, 8, 2256},
|
{"microbit", "examples/serial", 2724, 388, 8, 2256},
|
||||||
{"wioterminal", "examples/pininterrupt", 6159, 1485, 116, 6816},
|
{"wioterminal", "examples/pininterrupt", 6000, 1484, 116, 6816},
|
||||||
|
|
||||||
// TODO: also check wasm. Right now this is difficult, because
|
// TODO: also check wasm. Right now this is difficult, because
|
||||||
// wasm binaries are run through wasm-opt and therefore the
|
// wasm binaries are run through wasm-opt and therefore the
|
||||||
|
|||||||
+4
-2
@@ -88,6 +88,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
|
||||||
@@ -311,10 +312,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) {
|
||||||
|
|||||||
+16
-2
@@ -60,6 +60,7 @@ CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
|||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||||
long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
|
long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
|
||||||
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
|
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
|
||||||
|
unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c);
|
||||||
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
||||||
|
|
||||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
@@ -653,8 +654,19 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
|||||||
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
// Strip typedefs, if any.
|
// Strip typedefs, if any.
|
||||||
underlyingType := typ
|
underlyingType := typ
|
||||||
|
if underlyingType.kind == C.CXType_Elaborated {
|
||||||
|
// Starting with LLVM 16, the elaborated type is used for more types.
|
||||||
|
// According to the Clang documentation, the elaborated type has no
|
||||||
|
// semantic meaning so can be stripped (it is used to better convey type
|
||||||
|
// name information).
|
||||||
|
// Source:
|
||||||
|
// https://clang.llvm.org/doxygen/classclang_1_1ElaboratedType.html#details
|
||||||
|
// > The type itself is always "sugar", used to express what was written
|
||||||
|
// > in the source code but containing no additional semantic information.
|
||||||
|
underlyingType = C.clang_Type_getNamedType(underlyingType)
|
||||||
|
}
|
||||||
if underlyingType.kind == C.CXType_Typedef {
|
if underlyingType.kind == C.CXType_Typedef {
|
||||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
c := C.tinygo_clang_getTypeDeclaration(underlyingType)
|
||||||
underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
// TODO: support a chain of typedefs. At the moment, it seems to get
|
// TODO: support a chain of typedefs. At the moment, it seems to get
|
||||||
// stuck in an endless loop when trying to get to the most underlying
|
// stuck in an endless loop when trying to get to the most underlying
|
||||||
@@ -788,6 +800,8 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
return f.makeASTType(underlying, pos)
|
return f.makeASTType(underlying, pos)
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
return f.makeASTType(underlying, pos)
|
return f.makeASTType(underlying, pos)
|
||||||
|
case C.CXType_Typedef:
|
||||||
|
return f.makeASTType(underlying, pos)
|
||||||
default:
|
default:
|
||||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
||||||
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||||
@@ -806,7 +820,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
// makeASTRecordType will create an appropriate error.
|
// makeASTRecordType will create an appropriate error.
|
||||||
cgoRecordPrefix = "record_"
|
cgoRecordPrefix = "record_"
|
||||||
}
|
}
|
||||||
if name == "" {
|
if name == "" || C.tinygo_clang_Cursor_isAnonymous(cursor) != 0 {
|
||||||
// Anonymous record, probably inside a typedef.
|
// Anonymous record, probably inside a typedef.
|
||||||
location := f.getUniqueLocationID(pos, cursor)
|
location := f.getUniqueLocationID(pos, cursor)
|
||||||
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
||||||
|
|||||||
@@ -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"
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !byollvm && !llvm14
|
//go:build !byollvm && llvm15
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//go:build !byollvm && !llvm15
|
||||||
|
|
||||||
|
package cgo
|
||||||
|
|
||||||
|
// As of 2023-05-05, there is a packaging issue on Debian:
|
||||||
|
// https://github.com/llvm/llvm-project/issues/62199
|
||||||
|
// A workaround is to fix this locally, using something like this:
|
||||||
|
//
|
||||||
|
// ln -sf ../../x86_64-linux-gnu/libclang-16.so.1 /usr/lib/llvm-16/lib/libclang.so
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux CFLAGS: -I/usr/lib/llvm-16/include
|
||||||
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@16/include
|
||||||
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@16/include
|
||||||
|
#cgo freebsd CFLAGS: -I/usr/local/llvm16/include
|
||||||
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-16/lib -lclang
|
||||||
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang -lffi
|
||||||
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang -lffi
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
@@ -77,6 +77,10 @@ CXType tinygo_clang_getEnumDeclIntegerType(CXCursor c) {
|
|||||||
return clang_getEnumDeclIntegerType(c);
|
return clang_getEnumDeclIntegerType(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned tinygo_clang_Cursor_isAnonymous(CXCursor c) {
|
||||||
|
return clang_Cursor_isAnonymous(c);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
|
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
|
||||||
return clang_Cursor_isBitField(c);
|
return clang_Cursor_isBitField(c);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
-2
@@ -51,10 +51,10 @@ type (
|
|||||||
C.longlong int64
|
C.longlong int64
|
||||||
C.ulonglong uint64
|
C.ulonglong uint64
|
||||||
)
|
)
|
||||||
type C._Ctype_struct___0 struct {
|
type C.struct_point_t struct {
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
}
|
}
|
||||||
type C.point_t = C._Ctype_struct___0
|
type C.point_t = C.struct_point_t
|
||||||
|
|
||||||
const C.SOME_CONST_3 = 1234
|
const C.SOME_CONST_3 = 1234
|
||||||
|
|||||||
Vendored
+34
-36
@@ -38,11 +38,11 @@ type (
|
|||||||
C.ulonglong uint64
|
C.ulonglong uint64
|
||||||
)
|
)
|
||||||
type C.myint = C.int
|
type C.myint = C.int
|
||||||
type C._Ctype_struct___0 struct {
|
type C.struct_point2d_t struct {
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
}
|
}
|
||||||
type C.point2d_t = C._Ctype_struct___0
|
type C.point2d_t = C.struct_point2d_t
|
||||||
type C.struct_point3d struct {
|
type C.struct_point3d struct {
|
||||||
x C.int
|
x C.int
|
||||||
y C.int
|
y C.int
|
||||||
@@ -55,21 +55,19 @@ type C.struct_type1 struct {
|
|||||||
___type C.int
|
___type C.int
|
||||||
}
|
}
|
||||||
type C.struct_type2 struct{ _type C.int }
|
type C.struct_type2 struct{ _type C.int }
|
||||||
type C._Ctype_union___1 struct{ i C.int }
|
type C.union_union1_t struct{ i C.int }
|
||||||
type C.union1_t = C._Ctype_union___1
|
type C.union1_t = C.union_union1_t
|
||||||
type C._Ctype_union___2 struct{ $union uint64 }
|
type C.union_union3_t struct{ $union uint64 }
|
||||||
|
|
||||||
func (union *C._Ctype_union___2) unionfield_i() *C.int {
|
func (union *C.union_union3_t) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
func (union *C.union_union3_t) unionfield_d() *float64 {
|
||||||
}
|
|
||||||
func (union *C._Ctype_union___2) unionfield_d() *float64 {
|
|
||||||
return (*float64)(unsafe.Pointer(&union.$union))
|
return (*float64)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
func (union *C._Ctype_union___2) unionfield_s() *C.short {
|
func (union *C.union_union3_t) unionfield_s() *C.short {
|
||||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.union3_t = C._Ctype_union___2
|
type C.union3_t = C.union_union3_t
|
||||||
type C.union_union2d struct{ $union [2]uint64 }
|
type C.union_union2d struct{ $union [2]uint64 }
|
||||||
|
|
||||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
@@ -78,50 +76,50 @@ func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type C.union2d_t = C.union_union2d
|
type C.union2d_t = C.union_union2d
|
||||||
type C._Ctype_union___3 struct{ arr [10]C.uchar }
|
type C.union_unionarray_t struct{ arr [10]C.uchar }
|
||||||
type C.unionarray_t = C._Ctype_union___3
|
type C.unionarray_t = C.union_unionarray_t
|
||||||
type C._Ctype_union___5 struct{ $union [3]uint32 }
|
type C._Ctype_union___0 struct{ $union [3]uint32 }
|
||||||
|
|
||||||
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
|
func (union *C._Ctype_union___0) unionfield_area() *C.point2d_t {
|
||||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
|
func (union *C._Ctype_union___0) unionfield_solid() *C.point3d_t {
|
||||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C._Ctype_struct___4 struct {
|
type C.struct_struct_nested_t struct {
|
||||||
begin C.point2d_t
|
begin C.point2d_t
|
||||||
end C.point2d_t
|
end C.point2d_t
|
||||||
tag C.int
|
tag C.int
|
||||||
|
|
||||||
coord C._Ctype_union___5
|
coord C._Ctype_union___0
|
||||||
}
|
}
|
||||||
type C.struct_nested_t = C._Ctype_struct___4
|
type C.struct_nested_t = C.struct_struct_nested_t
|
||||||
type C._Ctype_union___6 struct{ $union [2]uint64 }
|
type C.union_union_nested_t struct{ $union [2]uint64 }
|
||||||
|
|
||||||
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
|
func (union *C.union_union_nested_t) unionfield_point() *C.point3d_t {
|
||||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
|
func (union *C.union_union_nested_t) unionfield_array() *C.unionarray_t {
|
||||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
|
func (union *C.union_union_nested_t) unionfield_thing() *C.union3_t {
|
||||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|
||||||
type C.union_nested_t = C._Ctype_union___6
|
type C.union_nested_t = C.union_union_nested_t
|
||||||
type C.enum_option = C.int
|
type C.enum_option = C.int
|
||||||
type C.option_t = C.enum_option
|
type C.option_t = C.enum_option
|
||||||
type C._Ctype_enum___7 = C.uint
|
type C.enum_option2_t = C.uint
|
||||||
type C.option2_t = C._Ctype_enum___7
|
type C.option2_t = C.enum_option2_t
|
||||||
type C._Ctype_struct___8 struct {
|
type C.struct_types_t struct {
|
||||||
f float32
|
f float32
|
||||||
d float64
|
d float64
|
||||||
ptr *C.int
|
ptr *C.int
|
||||||
}
|
}
|
||||||
type C.types_t = C._Ctype_struct___8
|
type C.types_t = C.struct_types_t
|
||||||
type C.myIntArray = [10]C.int
|
type C.myIntArray = [10]C.int
|
||||||
type C._Ctype_struct___9 struct {
|
type C.struct_bitfield_t struct {
|
||||||
start C.uchar
|
start C.uchar
|
||||||
__bitfield_1 C.uchar
|
__bitfield_1 C.uchar
|
||||||
|
|
||||||
@@ -129,21 +127,21 @@ type C._Ctype_struct___9 struct {
|
|||||||
e C.uchar
|
e C.uchar
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
func (s *C.struct_bitfield_t) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||||
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
|
func (s *C.struct_bitfield_t) set_bitfield_a(value C.uchar) {
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||||
}
|
}
|
||||||
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
|
func (s *C.struct_bitfield_t) bitfield_b() C.uchar {
|
||||||
return s.__bitfield_1 >> 5 & 0x1
|
return s.__bitfield_1 >> 5 & 0x1
|
||||||
}
|
}
|
||||||
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
|
func (s *C.struct_bitfield_t) set_bitfield_b(value C.uchar) {
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||||
}
|
}
|
||||||
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
|
func (s *C.struct_bitfield_t) bitfield_c() C.uchar {
|
||||||
return s.__bitfield_1 >> 6
|
return s.__bitfield_1 >> 6
|
||||||
}
|
}
|
||||||
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
|
func (s *C.struct_bitfield_t) set_bitfield_c(value C.uchar,
|
||||||
|
|
||||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||||
|
|
||||||
type C.bitfield_t = C._Ctype_struct___9
|
type C.bitfield_t = C.struct_bitfield_t
|
||||||
|
|||||||
+6
-11
@@ -145,18 +145,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.
|
||||||
@@ -492,11 +492,6 @@ func (c *Config) RelocationModel() string {
|
|||||||
return "static"
|
return "static"
|
||||||
}
|
}
|
||||||
|
|
||||||
// WasmAbi returns the WASM ABI which is specified in the target JSON file.
|
|
||||||
func (c *Config) WasmAbi() string {
|
|
||||||
return c.Target.WasmAbi
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmulatorName is a shorthand to get the command for this emulator, something
|
// EmulatorName is a shorthand to get the command for this emulator, something
|
||||||
// like qemu-system-arm or simavr.
|
// like qemu-system-arm or simavr.
|
||||||
func (c *Config) EmulatorName() string {
|
func (c *Config) EmulatorName() string {
|
||||||
|
|||||||
+75
-42
@@ -23,46 +23,45 @@ import (
|
|||||||
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html
|
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html
|
||||||
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
|
||||||
type TargetSpec struct {
|
type TargetSpec struct {
|
||||||
Inherits []string `json:"inherits"`
|
Inherits []string `json:"inherits,omitempty"`
|
||||||
Triple string `json:"llvm-target"`
|
Triple string `json:"llvm-target,omitempty"`
|
||||||
CPU string `json:"cpu"`
|
CPU string `json:"cpu,omitempty"`
|
||||||
ABI string `json:"target-abi"` // rougly equivalent to -mabi= flag
|
ABI string `json:"target-abi,omitempty"` // rougly equivalent to -mabi= flag
|
||||||
Features string `json:"features"`
|
Features string `json:"features,omitempty"`
|
||||||
GOOS string `json:"goos"`
|
GOOS string `json:"goos,omitempty"`
|
||||||
GOARCH string `json:"goarch"`
|
GOARCH string `json:"goarch,omitempty"`
|
||||||
BuildTags []string `json:"build-tags"`
|
BuildTags []string `json:"build-tags,omitempty"`
|
||||||
GC string `json:"gc"`
|
GC string `json:"gc,omitempty"`
|
||||||
Scheduler string `json:"scheduler"`
|
Scheduler string `json:"scheduler,omitempty"`
|
||||||
Serial string `json:"serial"` // which serial output to use (uart, usb, none)
|
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
|
||||||
Linker string `json:"linker"`
|
Linker string `json:"linker,omitempty"`
|
||||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
||||||
Libc string `json:"libc"`
|
Libc string `json:"libc,omitempty"`
|
||||||
AutoStackSize *bool `json:"automatic-stack-size"` // Determine stack size automatically at compile time.
|
AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time.
|
||||||
DefaultStackSize uint64 `json:"default-stack-size"` // Default stack size if the size couldn't be determined at compile time.
|
DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
|
||||||
CFlags []string `json:"cflags"`
|
CFlags []string `json:"cflags,omitempty"`
|
||||||
LDFlags []string `json:"ldflags"`
|
LDFlags []string `json:"ldflags,omitempty"`
|
||||||
LinkerScript string `json:"linkerscript"`
|
LinkerScript string `json:"linkerscript,omitempty"`
|
||||||
ExtraFiles []string `json:"extra-files"`
|
ExtraFiles []string `json:"extra-files,omitempty"`
|
||||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
|
||||||
Emulator string `json:"emulator"`
|
Emulator string `json:"emulator,omitempty"`
|
||||||
FlashCommand string `json:"flash-command"`
|
FlashCommand string `json:"flash-command,omitempty"`
|
||||||
GDB []string `json:"gdb"`
|
GDB []string `json:"gdb,omitempty"`
|
||||||
PortReset string `json:"flash-1200-bps-reset"`
|
PortReset string `json:"flash-1200-bps-reset,omitempty"`
|
||||||
SerialPort []string `json:"serial-port"` // serial port IDs in the form "vid:pid"
|
SerialPort []string `json:"serial-port,omitempty"` // serial port IDs in the form "vid:pid"
|
||||||
FlashMethod string `json:"flash-method"`
|
FlashMethod string `json:"flash-method,omitempty"`
|
||||||
FlashVolume []string `json:"msd-volume-name"`
|
FlashVolume []string `json:"msd-volume-name,omitempty"`
|
||||||
FlashFilename string `json:"msd-firmware-name"`
|
FlashFilename string `json:"msd-firmware-name,omitempty"`
|
||||||
UF2FamilyID string `json:"uf2-family-id"`
|
UF2FamilyID string `json:"uf2-family-id,omitempty"`
|
||||||
BinaryFormat string `json:"binary-format"`
|
BinaryFormat string `json:"binary-format,omitempty"`
|
||||||
OpenOCDInterface string `json:"openocd-interface"`
|
OpenOCDInterface string `json:"openocd-interface,omitempty"`
|
||||||
OpenOCDTarget string `json:"openocd-target"`
|
OpenOCDTarget string `json:"openocd-target,omitempty"`
|
||||||
OpenOCDTransport string `json:"openocd-transport"`
|
OpenOCDTransport string `json:"openocd-transport,omitempty"`
|
||||||
OpenOCDCommands []string `json:"openocd-commands"`
|
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
|
||||||
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
|
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
|
||||||
JLinkDevice string `json:"jlink-device"`
|
JLinkDevice string `json:"jlink-device,omitempty"`
|
||||||
CodeModel string `json:"code-model"`
|
CodeModel string `json:"code-model,omitempty"`
|
||||||
RelocationModel string `json:"relocation-model"`
|
RelocationModel string `json:"relocation-model,omitempty"`
|
||||||
WasmAbi string `json:"wasm-abi"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideProperties overrides all properties that are set in child into itself using reflection.
|
// overrideProperties overrides all properties that are set in child into itself using reflection.
|
||||||
@@ -192,12 +191,15 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
||||||
}
|
}
|
||||||
|
case "wasm":
|
||||||
|
llvmarch = "wasm32"
|
||||||
default:
|
default:
|
||||||
llvmarch = options.GOARCH
|
llvmarch = options.GOARCH
|
||||||
}
|
}
|
||||||
llvmvendor := "unknown"
|
llvmvendor := "unknown"
|
||||||
llvmos := options.GOOS
|
llvmos := options.GOOS
|
||||||
if llvmos == "darwin" {
|
switch llvmos {
|
||||||
|
case "darwin":
|
||||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
||||||
// to iOS!
|
// to iOS!
|
||||||
llvmos = "macosx10.12.0"
|
llvmos = "macosx10.12.0"
|
||||||
@@ -208,6 +210,8 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
llvmos = "macosx11.0.0"
|
llvmos = "macosx11.0.0"
|
||||||
}
|
}
|
||||||
llvmvendor = "apple"
|
llvmvendor = "apple"
|
||||||
|
case "wasip1":
|
||||||
|
llvmos = "wasi"
|
||||||
}
|
}
|
||||||
// Target triples (which actually have four components, but are called
|
// Target triples (which actually have four components, but are called
|
||||||
// triples for historical reasons) have the form:
|
// triples for historical reasons) have the form:
|
||||||
@@ -277,7 +281,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
}
|
}
|
||||||
case "arm64":
|
case "arm64":
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
spec.Features = "+neon"
|
if goos == "darwin" {
|
||||||
|
spec.Features = "+neon"
|
||||||
|
} else { // windows, linux
|
||||||
|
spec.Features = "+neon,-fmv"
|
||||||
|
}
|
||||||
|
case "wasm":
|
||||||
|
spec.CPU = "generic"
|
||||||
|
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
|
||||||
|
spec.BuildTags = append(spec.BuildTags, "tinygo.wasm")
|
||||||
|
spec.CFlags = append(spec.CFlags,
|
||||||
|
"-mbulk-memory",
|
||||||
|
"-mnontrapping-fptoint",
|
||||||
|
"-msign-ext",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if goos == "darwin" {
|
if goos == "darwin" {
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
@@ -321,6 +338,22 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
"--no-insert-timestamp",
|
"--no-insert-timestamp",
|
||||||
"--no-dynamicbase",
|
"--no-dynamicbase",
|
||||||
)
|
)
|
||||||
|
} else if goos == "wasip1" {
|
||||||
|
spec.GC = "" // use default GC
|
||||||
|
spec.Scheduler = "asyncify"
|
||||||
|
spec.Linker = "wasm-ld"
|
||||||
|
spec.RTLib = "compiler-rt"
|
||||||
|
spec.Libc = "wasi-libc"
|
||||||
|
spec.DefaultStackSize = 1024 * 32 // 32kB
|
||||||
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
|
"--stack-first",
|
||||||
|
"--no-demangle",
|
||||||
|
)
|
||||||
|
spec.Emulator = "wasmtime --mapdir=/tmp::{tmpDir} {}"
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||||
|
"src/runtime/asm_tinygowasm.S",
|
||||||
|
"src/internal/task/task_asyncify_wasm.S",
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+6
-2
@@ -36,12 +36,16 @@ const (
|
|||||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||||
// createRuntimeInvoke instead.
|
// createRuntimeInvoke instead.
|
||||||
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
|
||||||
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
member := b.program.ImportedPackage("runtime").Members[fnName]
|
||||||
|
if member == nil {
|
||||||
|
panic("unknown runtime call: " + fnName)
|
||||||
|
}
|
||||||
|
fn := member.(*ssa.Function)
|
||||||
fnType, llvmFn := b.getFunction(fn)
|
fnType, llvmFn := b.getFunction(fn)
|
||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||||
}
|
}
|
||||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
args = append(args, llvm.Undef(b.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, "")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+91
-54
@@ -75,10 +75,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 +122,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 +415,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 +437,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 +531,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 +1534,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
|
||||||
@@ -1600,6 +1583,42 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||||
return cplx, nil
|
return cplx, nil
|
||||||
|
case "clear":
|
||||||
|
value := argValues[0]
|
||||||
|
switch typ := argTypes[0].Underlying().(type) {
|
||||||
|
case *types.Slice:
|
||||||
|
elementType := b.getLLVMType(typ.Elem())
|
||||||
|
elementSize := b.targetData.TypeAllocSize(elementType)
|
||||||
|
elementAlign := b.targetData.ABITypeAlignment(elementType)
|
||||||
|
|
||||||
|
// The pointer to the data to be cleared.
|
||||||
|
llvmBuf := b.CreateExtractValue(value, 0, "buf")
|
||||||
|
|
||||||
|
// The length (in bytes) to be cleared.
|
||||||
|
llvmLen := b.CreateExtractValue(value, 1, "len")
|
||||||
|
llvmLen = b.CreateMul(llvmLen, llvm.ConstInt(llvmLen.Type(), elementSize, false), "")
|
||||||
|
|
||||||
|
// Do the clear operation using the LLVM memset builtin.
|
||||||
|
// This is also correct for nil slices: in those cases, len will be
|
||||||
|
// 0 which means the memset call is a no-op (according to the LLVM
|
||||||
|
// LangRef).
|
||||||
|
memset := b.getMemsetFunc()
|
||||||
|
call := b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
|
||||||
|
llvmBuf, // dest
|
||||||
|
llvm.ConstInt(b.ctx.Int8Type(), 0, false), // val
|
||||||
|
llvmLen, // len
|
||||||
|
llvm.ConstInt(b.ctx.Int1Type(), 0, false), // isVolatile
|
||||||
|
}, "")
|
||||||
|
call.AddCallSiteAttribute(1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elementAlign)))
|
||||||
|
|
||||||
|
return llvm.Value{}, nil
|
||||||
|
case *types.Map:
|
||||||
|
m := argValues[0]
|
||||||
|
b.createMapClear(m)
|
||||||
|
return llvm.Value{}, nil
|
||||||
|
default:
|
||||||
|
return llvm.Value{}, b.makeError(pos, "unsupported type in clear builtin: "+typ.String())
|
||||||
|
}
|
||||||
case "copy":
|
case "copy":
|
||||||
dst := argValues[0]
|
dst := argValues[0]
|
||||||
src := argValues[1]
|
src := argValues[1]
|
||||||
@@ -1608,8 +1627,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":
|
||||||
@@ -1637,6 +1654,24 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int")
|
llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int")
|
||||||
}
|
}
|
||||||
return llvmLen, nil
|
return llvmLen, nil
|
||||||
|
case "min", "max":
|
||||||
|
// min and max builtins, added in Go 1.21.
|
||||||
|
// We can simply reuse the existing binop comparison code, which has all
|
||||||
|
// the edge cases figured out already.
|
||||||
|
tok := token.LSS
|
||||||
|
if callName == "max" {
|
||||||
|
tok = token.GTR
|
||||||
|
}
|
||||||
|
result := argValues[0]
|
||||||
|
typ := argTypes[0]
|
||||||
|
for _, arg := range argValues[1:] {
|
||||||
|
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result = b.CreateSelect(cmp, result, arg, "")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
case "print", "println":
|
case "print", "println":
|
||||||
for i, value := range argValues {
|
for i, value := range argValues {
|
||||||
if i >= 1 && callName == "println" {
|
if i >= 1 && callName == "println" {
|
||||||
@@ -1831,14 +1866,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
|
||||||
@@ -1866,13 +1900,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")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1905,7 +1940,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() {
|
||||||
@@ -1973,7 +2008,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)
|
||||||
@@ -2019,10 +2053,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())
|
||||||
}
|
}
|
||||||
@@ -2099,12 +2129,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")
|
||||||
@@ -2207,7 +2237,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.
|
||||||
@@ -2253,7 +2282,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:
|
||||||
@@ -2421,7 +2450,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
|
||||||
@@ -2887,16 +2915,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)
|
||||||
@@ -2940,7 +2968,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:
|
||||||
@@ -2948,12 +2976,16 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
|
|||||||
panic("expected nil pointer constant")
|
panic("expected nil pointer constant")
|
||||||
}
|
}
|
||||||
return llvm.ConstPointerNull(c.getLLVMType(typ))
|
return llvm.ConstPointerNull(c.getLLVMType(typ))
|
||||||
|
case *types.Array:
|
||||||
|
if expr.Value != nil {
|
||||||
|
panic("expected nil array constant")
|
||||||
|
}
|
||||||
|
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil slice constant")
|
panic("expected nil slice constant")
|
||||||
}
|
}
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
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
|
||||||
@@ -2961,6 +2993,11 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
|
|||||||
llvmLen, // cap
|
llvmLen, // cap
|
||||||
}, false)
|
}, false)
|
||||||
return slice
|
return slice
|
||||||
|
case *types.Struct:
|
||||||
|
if expr.Value != nil {
|
||||||
|
panic("expected nil struct constant")
|
||||||
|
}
|
||||||
|
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
if !expr.IsNil() {
|
if !expr.IsNil() {
|
||||||
// I believe this is not allowed by the Go spec.
|
// I believe this is not allowed by the Go spec.
|
||||||
@@ -2989,7 +3026,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) {
|
||||||
@@ -3228,7 +3265,7 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
|||||||
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, "")
|
||||||
|
|||||||
+11
-10
@@ -29,7 +29,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
_, goMinor, err := goenv.GetGorootVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not read Go version:", err)
|
t.Fatal("could not read Go version:", err)
|
||||||
}
|
}
|
||||||
@@ -54,6 +54,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
if goMinor >= 20 {
|
if goMinor >= 20 {
|
||||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||||
}
|
}
|
||||||
|
if goMinor >= 21 {
|
||||||
|
tests = append(tests, testCase{"go1.21.go", "", ""})
|
||||||
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
name := tc.file
|
name := tc.file
|
||||||
@@ -88,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 != "" {
|
||||||
@@ -166,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)
|
||||||
|
|||||||
+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:
|
||||||
|
|||||||
+28
-17
@@ -128,6 +128,15 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
hasMethodSet = false
|
hasMethodSet = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var numMethods int
|
||||||
|
if hasMethodSet {
|
||||||
|
for i := 0; i < ms.Len(); i++ {
|
||||||
|
if ms.At(i).Obj().Exported() {
|
||||||
|
numMethods++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Short-circuit all the global pointer logic here for pointers to pointers.
|
// Short-circuit all the global pointer logic here for pointers to pointers.
|
||||||
if typ, ok := typ.(*types.Pointer); ok {
|
if typ, ok := typ.(*types.Pointer); ok {
|
||||||
if _, ok := typ.Elem().(*types.Pointer); ok {
|
if _, ok := typ.Elem().(*types.Pointer); ok {
|
||||||
@@ -138,7 +147,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
c.addError(token.NoPos, fmt.Sprintf("too many levels of pointers for typecode: %s", typstr))
|
c.addError(token.NoPos, fmt.Sprintf("too many levels of pointers for typecode: %s", typstr))
|
||||||
}
|
}
|
||||||
return llvm.ConstGEP(c.ctx.Int8Type(), ptr, []llvm.Value{
|
return llvm.ConstGEP(c.ctx.Int8Type(), ptr, []llvm.Value{
|
||||||
llvm.ConstInt(llvm.Int32Type(), 1, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,6 +218,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||||
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||||
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
||||||
|
types.NewVar(token.NoPos, nil, "sliceOf", types.Typ[types.UnsafePointer]),
|
||||||
)
|
)
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
typeFieldTypes = append(typeFieldTypes,
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
@@ -277,11 +287,11 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
}
|
}
|
||||||
pkgPathPtr := c.pkgPathPtr(pkgpath)
|
pkgPathPtr := c.pkgPathPtr(pkgpath)
|
||||||
typeFields = []llvm.Value{
|
typeFields = []llvm.Value{
|
||||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
|
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
c.getTypeCode(typ.Underlying()), // underlying
|
c.getTypeCode(typ.Underlying()), // underlying
|
||||||
pkgPathPtr, // pkgpath pointer
|
pkgPathPtr, // pkgpath pointer
|
||||||
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
|
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
|
||||||
}
|
}
|
||||||
metabyte |= 1 << 5 // "named" flag
|
metabyte |= 1 << 5 // "named" flag
|
||||||
case *types.Chan:
|
case *types.Chan:
|
||||||
@@ -308,7 +318,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
}
|
}
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
typeFields = []llvm.Value{
|
typeFields = []llvm.Value{
|
||||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
|
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||||
c.getTypeCode(typ.Elem()),
|
c.getTypeCode(typ.Elem()),
|
||||||
}
|
}
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
@@ -317,6 +327,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
c.getTypeCode(typ.Elem()), // elementType
|
c.getTypeCode(typ.Elem()), // elementType
|
||||||
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
|
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
|
||||||
|
c.getTypeCode(types.NewSlice(typ.Elem())), // slicePtr
|
||||||
}
|
}
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
typeFields = []llvm.Value{
|
typeFields = []llvm.Value{
|
||||||
@@ -337,8 +348,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
llvmStructType := c.getLLVMType(typ)
|
llvmStructType := c.getLLVMType(typ)
|
||||||
size := c.targetData.TypeStoreSize(llvmStructType)
|
size := c.targetData.TypeStoreSize(llvmStructType)
|
||||||
typeFields = []llvm.Value{
|
typeFields = []llvm.Value{
|
||||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
|
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
pkgPathPtr,
|
pkgPathPtr,
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size
|
llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size
|
||||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
|
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
|
||||||
@@ -403,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
|
||||||
}
|
}
|
||||||
@@ -441,8 +452,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
offset = 1
|
offset = 1
|
||||||
}
|
}
|
||||||
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
|
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
|
||||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(llvm.Int32Type(), offset, false),
|
llvm.ConstInt(c.ctx.Int32Type(), offset, false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,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)
|
||||||
@@ -768,7 +779,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))
|
||||||
@@ -791,7 +802,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)))
|
||||||
@@ -831,7 +842,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
|
||||||
|
|||||||
+14
-15
@@ -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
|
||||||
@@ -70,15 +66,7 @@ func (b *builder) createMemoryCopyImpl() {
|
|||||||
// regular libc memset calls if they aren't optimized out in a different way.
|
// regular libc memset calls if they aren't optimized out in a different way.
|
||||||
func (b *builder) createMemoryZeroImpl() {
|
func (b *builder) createMemoryZeroImpl() {
|
||||||
b.createFunctionStart(true)
|
b.createFunctionStart(true)
|
||||||
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
llvmFn := b.getMemsetFunc()
|
||||||
if llvmutil.Major() < 15 { // compatibility with LLVM 14
|
|
||||||
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
|
||||||
}
|
|
||||||
llvmFn := b.mod.NamedFunction(fnName)
|
|
||||||
if llvmFn.IsNil() {
|
|
||||||
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
|
|
||||||
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
|
|
||||||
}
|
|
||||||
params := []llvm.Value{
|
params := []llvm.Value{
|
||||||
b.getValue(b.fn.Params[0], getPos(b.fn)),
|
b.getValue(b.fn.Params[0], getPos(b.fn)),
|
||||||
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
|
||||||
@@ -89,6 +77,17 @@ func (b *builder) createMemoryZeroImpl() {
|
|||||||
b.CreateRetVoid()
|
b.CreateRetVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the llvm.memset.p0.i8 function declaration.
|
||||||
|
func (c *compilerContext) getMemsetFunc() llvm.Value {
|
||||||
|
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
|
||||||
|
llvmFn := c.mod.NamedFunction(fnName)
|
||||||
|
if llvmFn.IsNil() {
|
||||||
|
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
|
||||||
|
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
|
||||||
|
}
|
||||||
|
return llvmFn
|
||||||
|
}
|
||||||
|
|
||||||
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
|
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
|
||||||
// using inline assembly.
|
// using inline assembly.
|
||||||
func (b *builder) createKeepAliveImpl() {
|
func (b *builder) createKeepAliveImpl() {
|
||||||
@@ -105,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)
|
||||||
|
|||||||
+28
-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
|
||||||
@@ -185,6 +185,11 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the given map.
|
||||||
|
func (b *builder) createMapClear(m llvm.Value) {
|
||||||
|
b.createRuntimeCall("hashmapClear", []llvm.Value{m}, "")
|
||||||
|
}
|
||||||
|
|
||||||
// createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
|
// createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
|
||||||
// map. It returns a tuple of {bool, key, value} with the result of the
|
// map. It returns a tuple of {bool, key, value} with the result of the
|
||||||
// iteration.
|
// iteration.
|
||||||
@@ -220,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, "")
|
||||||
|
|
||||||
@@ -233,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))
|
||||||
@@ -328,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}, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-58
@@ -23,9 +23,9 @@ import (
|
|||||||
// The linkName value contains a valid link name, even if //go:linkname is not
|
// The linkName value contains a valid link name, even if //go:linkname is not
|
||||||
// present.
|
// present.
|
||||||
type functionInfo struct {
|
type functionInfo struct {
|
||||||
module string // go:wasm-module
|
wasmModule string // go:wasm-module
|
||||||
importName string // go:linkname, go:export - The name the developer assigns
|
wasmName string // wasm-export-name or wasm-import-name in the IR
|
||||||
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
|
linkName string // go:linkname, go:export - the IR function name
|
||||||
section string // go:section - object file section name
|
section string // go:section - object file section name
|
||||||
exported bool // go:export, CGo
|
exported bool // go:export, CGo
|
||||||
interrupt bool // go:interrupt
|
interrupt bool // go:interrupt
|
||||||
@@ -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.
|
||||||
@@ -180,7 +178,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
if strings.Split(c.Triple, "-")[0] == "avr" {
|
if strings.Split(c.Triple, "-")[0] == "avr" {
|
||||||
// These functions are compiler-rt/libgcc functions that are
|
// These functions are compiler-rt/libgcc functions that are
|
||||||
// currently implemented in Go. Assembly versions should appear in
|
// currently implemented in Go. Assembly versions should appear in
|
||||||
// LLVM 16 hopefully. Until then, they need to be made available to
|
// LLVM 17 hopefully. Until then, they need to be made available to
|
||||||
// the linker and the best way to do that is llvm.compiler.used.
|
// the linker and the best way to do that is llvm.compiler.used.
|
||||||
// I considered adding a pragma for this, but the LLVM language
|
// I considered adding a pragma for this, but the LLVM language
|
||||||
// reference explicitly says that this feature should not be exposed
|
// reference explicitly says that this feature should not be exposed
|
||||||
@@ -194,20 +192,14 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
// External/exported functions may not retain pointer values.
|
// External/exported functions may not retain pointer values.
|
||||||
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
|
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
|
||||||
if info.exported {
|
if info.exported {
|
||||||
if c.archFamily() == "wasm32" {
|
if c.archFamily() == "wasm32" && len(fn.Blocks) == 0 {
|
||||||
// We need to add the wasm-import-module and the wasm-import-name
|
// We need to add the wasm-import-module and the wasm-import-name
|
||||||
// attributes.
|
// attributes.
|
||||||
module := info.module
|
if info.wasmModule != "" {
|
||||||
if module == "" {
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", info.wasmModule))
|
||||||
module = "env"
|
|
||||||
}
|
}
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module))
|
|
||||||
|
|
||||||
name := info.importName
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName))
|
||||||
if name == "" {
|
|
||||||
name = info.linkName
|
|
||||||
}
|
|
||||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name))
|
|
||||||
}
|
}
|
||||||
nocaptureKind := llvm.AttributeKindID("nocapture")
|
nocaptureKind := llvm.AttributeKindID("nocapture")
|
||||||
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
||||||
@@ -260,10 +252,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
||||||
|
|
||||||
// Our importName for a wasm module (if we are compiling to wasm), or llvm link name
|
|
||||||
var importName string
|
|
||||||
|
|
||||||
for _, comment := range decl.Doc.List {
|
for _, comment := range decl.Doc.List {
|
||||||
text := comment.Text
|
text := comment.Text
|
||||||
if strings.HasPrefix(text, "//export ") {
|
if strings.HasPrefix(text, "//export ") {
|
||||||
@@ -281,7 +269,8 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
importName = parts[1]
|
info.linkName = parts[1]
|
||||||
|
info.wasmName = info.linkName
|
||||||
info.exported = true
|
info.exported = true
|
||||||
case "//go:interrupt":
|
case "//go:interrupt":
|
||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||||
@@ -289,10 +278,11 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
}
|
}
|
||||||
case "//go:wasm-module":
|
case "//go:wasm-module":
|
||||||
// Alternative comment for setting the import module.
|
// Alternative comment for setting the import module.
|
||||||
|
// This is deprecated, use //go:wasmimport instead.
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
info.module = parts[1]
|
info.wasmModule = parts[1]
|
||||||
case "//go:wasmimport":
|
case "//go:wasmimport":
|
||||||
// Import a WebAssembly function, for example a WASI function.
|
// Import a WebAssembly function, for example a WASI function.
|
||||||
// Original proposal: https://github.com/golang/go/issues/38248
|
// Original proposal: https://github.com/golang/go/issues/38248
|
||||||
@@ -302,8 +292,8 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
}
|
}
|
||||||
c.checkWasmImport(f, comment.Text)
|
c.checkWasmImport(f, comment.Text)
|
||||||
info.exported = true
|
info.exported = true
|
||||||
info.module = parts[1]
|
info.wasmModule = parts[1]
|
||||||
info.importName = parts[2]
|
info.wasmName = parts[2]
|
||||||
case "//go:inline":
|
case "//go:inline":
|
||||||
info.inline = inlineHint
|
info.inline = inlineHint
|
||||||
case "//go:noinline":
|
case "//go:noinline":
|
||||||
@@ -347,17 +337,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the importName for our exported function if we have one
|
|
||||||
if importName != "" {
|
|
||||||
if info.module == "" {
|
|
||||||
info.linkName = importName
|
|
||||||
} else {
|
|
||||||
// WebAssembly import
|
|
||||||
info.importName = importName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +346,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
// The list of allowed types is based on this proposal:
|
// The list of allowed types is based on this proposal:
|
||||||
// https://github.com/golang/go/issues/59149
|
// https://github.com/golang/go/issues/59149
|
||||||
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
|
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
|
||||||
if c.pkg.Path() == "runtime" {
|
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" {
|
||||||
// The runtime is a special case. Allow all kinds of parameters
|
// The runtime is a special case. Allow all kinds of parameters
|
||||||
// (importantly, including pointers).
|
// (importantly, including pointers).
|
||||||
return
|
return
|
||||||
@@ -464,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")
|
||||||
|
|||||||
Vendored
+3
-12
@@ -196,10 +196,6 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.foo(ptr %context) unnamed_addr #2 {
|
define hidden void @main.foo(ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%complit = alloca %main.kv.0, align 8
|
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.kv.0 zeroinitializer, ptr %complit, align 8
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
|
|
||||||
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
@@ -207,15 +203,10 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
|
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%b1 = alloca %main.kv.0, align 8
|
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.kv.0 zeroinitializer, ptr %b1, align 8
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %b1, ptr nonnull %stackalloc, ptr undef) #3
|
|
||||||
store %main.kv.0 %b, ptr %b1, align 8
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+6
-9
@@ -31,12 +31,12 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
||||||
|
|
||||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1
|
declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1
|
||||||
|
|
||||||
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
@@ -57,10 +57,7 @@ declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferen
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%complit = alloca {}, align 8
|
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4
|
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #4
|
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
||||||
@@ -110,8 +107,8 @@ select.body: ; preds = %select.next
|
|||||||
|
|
||||||
declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #1
|
declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #1
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { argmemonly nocallback nofree nosync nounwind willreturn }
|
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #4 = { nounwind }
|
attributes #4 = { nounwind }
|
||||||
|
|||||||
Vendored
+3
-3
@@ -93,6 +93,6 @@ entry:
|
|||||||
ret i8 %0
|
ret i8 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
|||||||
Vendored
+3
-3
@@ -44,7 +44,7 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+3
-3
@@ -135,7 +135,7 @@ entry:
|
|||||||
ret %runtime._interface %1
|
ret %runtime._interface %1
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+3
-3
@@ -57,7 +57,7 @@ entry:
|
|||||||
ret ptr %s.data
|
ret ptr %s.data
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+65
@@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func min1(a int) int {
|
||||||
|
return min(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func min2(a, b int) int {
|
||||||
|
return min(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func min3(a, b, c int) int {
|
||||||
|
return min(a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func min4(a, b, c, d int) int {
|
||||||
|
return min(a, b, c, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func minUint8(a, b uint8) uint8 {
|
||||||
|
return min(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func minUnsigned(a, b uint) uint {
|
||||||
|
return min(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func minFloat32(a, b float32) float32 {
|
||||||
|
return min(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func minFloat64(a, b float64) float64 {
|
||||||
|
return min(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func minString(a, b string) string {
|
||||||
|
return min(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxInt(a, b int) int {
|
||||||
|
return max(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxUint(a, b uint) uint {
|
||||||
|
return max(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxFloat32(a, b float32) float32 {
|
||||||
|
return max(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxString(a, b string) string {
|
||||||
|
return max(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearSlice(s []int) {
|
||||||
|
clear(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearZeroSizedSlice(s []struct{}) {
|
||||||
|
clear(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearMap(m map[string]int) {
|
||||||
|
clear(m)
|
||||||
|
}
|
||||||
Vendored
+179
@@ -0,0 +1,179 @@
|
|||||||
|
; ModuleID = 'go1.21.go'
|
||||||
|
source_filename = "go1.21.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
%runtime._string = type { ptr, i32 }
|
||||||
|
|
||||||
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
|
|
||||||
|
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.init(ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
ret i32 %a
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
|
ret i32 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
|
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||||
|
ret i32 %1
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
|
||||||
|
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
|
||||||
|
%2 = call i32 @llvm.smin.i32(i32 %1, i32 %d)
|
||||||
|
ret i32 %2
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
|
||||||
|
ret i8 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
|
||||||
|
ret i32 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = fcmp olt float %a, %b
|
||||||
|
%1 = select i1 %0, float %a, float %b
|
||||||
|
ret float %1
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = fcmp olt double %a, %b
|
||||||
|
%1 = select i1 %0, double %a, double %b
|
||||||
|
ret double %1
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
|
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||||
|
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
|
||||||
|
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||||
|
%6 = extractvalue %runtime._string %5, 0
|
||||||
|
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||||
|
ret %runtime._string %5
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
|
||||||
|
ret i32 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
|
||||||
|
ret i32 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = fcmp ogt float %a, %b
|
||||||
|
%1 = select i1 %0, float %a, float %b
|
||||||
|
ret float %1
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||||
|
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||||
|
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
|
||||||
|
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
|
||||||
|
%stackalloc = alloca i8, align 1
|
||||||
|
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
|
||||||
|
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
|
||||||
|
%6 = extractvalue %runtime._string %5, 0
|
||||||
|
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
|
||||||
|
ret %runtime._string %5
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
%0 = shl i32 %s.len, 2
|
||||||
|
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
|
||||||
|
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
|
||||||
|
entry:
|
||||||
|
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.smin.i32(i32, i32) #4
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i8 @llvm.umin.i8(i8, i8) #4
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.umin.i32(i32, i32) #4
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.smax.i32(i32, i32) #4
|
||||||
|
|
||||||
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
|
declare i32 @llvm.umax.i32(i32, i32) #4
|
||||||
|
|
||||||
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
|
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||||
|
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
|
attributes #5 = { nounwind }
|
||||||
+14
-14
@@ -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 16384, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
|
||||||
ret void
|
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 16384, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
|
||||||
ret void
|
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 16384, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
|
||||||
%2 = load i32, ptr %n, align 4
|
%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 16384, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 32768, ptr undef) #9
|
||||||
ret void
|
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 16384, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,13 +188,13 @@ entry:
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
|
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #4 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
|
||||||
attributes #7 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
attributes #8 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
attributes #8 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
attributes #9 = { nounwind }
|
attributes #9 = { nounwind }
|
||||||
|
|||||||
Vendored
+8
-8
@@ -9,7 +9,7 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
||||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 0, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
||||||
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||||
@@ -130,11 +130,11 @@ entry:
|
|||||||
|
|
||||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
|
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
attributes #4 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
|
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
|
||||||
attributes #5 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||||
attributes #6 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
attributes #7 = { nounwind }
|
attributes #7 = { nounwind }
|
||||||
|
|||||||
Vendored
+3
-3
@@ -44,7 +44,7 @@ entry:
|
|||||||
ret ptr %x
|
ret ptr %x
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+13
@@ -62,6 +62,19 @@ func exportedFunctionInSection() {
|
|||||||
//go:wasmimport modulename import1
|
//go:wasmimport modulename import1
|
||||||
func declaredImport()
|
func declaredImport()
|
||||||
|
|
||||||
|
// Legacy way of importing a function.
|
||||||
|
//
|
||||||
|
//go:wasm-module foobar
|
||||||
|
//export imported
|
||||||
|
func foobarImport()
|
||||||
|
|
||||||
|
// The wasm-module pragma is not functional here, but it should be safe.
|
||||||
|
//
|
||||||
|
//go:wasm-module foobar
|
||||||
|
//export exported
|
||||||
|
func foobarExportModule() {
|
||||||
|
}
|
||||||
|
|
||||||
// This function should not: it's only a declaration and not a definition.
|
// This function should not: it's only a declaration and not a definition.
|
||||||
//
|
//
|
||||||
//go:section .special_function_section
|
//go:section .special_function_section
|
||||||
|
|||||||
Vendored
+19
-9
@@ -6,7 +6,7 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@extern_global = external global [0 x i8], align 1
|
@extern_global = external global [0 x i8], align 1
|
||||||
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
||||||
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
||||||
@llvm.used = appending global [2 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection]
|
@llvm.used = appending global [3 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection, ptr @exported]
|
||||||
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
|
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
|
||||||
@undefinedGlobalNotInSection = external global i32, align 4
|
@undefinedGlobalNotInSection = external global i32, align 4
|
||||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||||
@@ -62,13 +62,23 @@ entry:
|
|||||||
|
|
||||||
declare void @main.declaredImport() #7
|
declare void @main.declaredImport() #7
|
||||||
|
|
||||||
|
declare void @imported() #8
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define void @exported() #9 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
declare void @main.undefinedFunctionNotInSection(ptr) #1
|
declare void @main.undefinedFunctionNotInSection(ptr) #1
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" "wasm-import-module"="env" "wasm-import-name"="extern_func" }
|
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" }
|
||||||
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" }
|
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" }
|
||||||
attributes #7 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
||||||
|
attributes #8 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
||||||
|
attributes #9 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exported" }
|
||||||
|
|||||||
Vendored
+3
-3
@@ -322,7 +322,7 @@ unsafe.Slice.throw: ; preds = %entry
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+3
-3
@@ -97,7 +97,7 @@ lookup.throw: ; preds = %entry
|
|||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+17
-49
@@ -21,14 +21,9 @@ define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1
|
|||||||
entry:
|
entry:
|
||||||
%hashmap.key = alloca %main.hasPadding, align 8
|
%hashmap.key = alloca %main.hasPadding, align 8
|
||||||
%hashmap.value = alloca i32, align 4
|
%hashmap.value = alloca i32, align 4
|
||||||
%s = alloca %main.hasPadding, align 8
|
|
||||||
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
|
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
|
||||||
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
|
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
|
||||||
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
|
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s, align 8
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #5
|
|
||||||
store %main.hasPadding %2, ptr %s, align 8
|
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
||||||
store %main.hasPadding %2, ptr %hashmap.key, align 8
|
store %main.hasPadding %2, ptr %hashmap.key, align 8
|
||||||
@@ -43,14 +38,14 @@ entry:
|
|||||||
ret i32 %6
|
ret i32 %6
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4
|
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4
|
||||||
|
|
||||||
declare void @runtime.memzero(ptr, i32, ptr) #1
|
declare void @runtime.memzero(ptr, i32, ptr) #1
|
||||||
|
|
||||||
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1
|
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4
|
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
@@ -58,14 +53,9 @@ define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b
|
|||||||
entry:
|
entry:
|
||||||
%hashmap.key = alloca %main.hasPadding, align 8
|
%hashmap.key = alloca %main.hasPadding, align 8
|
||||||
%hashmap.value = alloca i32, align 4
|
%hashmap.value = alloca i32, align 4
|
||||||
%s = alloca %main.hasPadding, align 8
|
|
||||||
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
|
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
|
||||||
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
|
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
|
||||||
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
|
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s, align 8
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #5
|
|
||||||
store %main.hasPadding %2, ptr %s, align 8
|
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
||||||
store i32 5, ptr %hashmap.value, align 4
|
store i32 5, ptr %hashmap.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
||||||
@@ -87,24 +77,13 @@ define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2
|
|||||||
entry:
|
entry:
|
||||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
||||||
%hashmap.value = alloca i32, align 4
|
%hashmap.value = alloca i32, align 4
|
||||||
%s1 = alloca [2 x %main.hasPadding], align 8
|
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s1, align 8
|
|
||||||
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #5
|
|
||||||
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
|
||||||
store %main.hasPadding %s.elt, ptr %s1, align 8
|
|
||||||
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
|
|
||||||
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
|
|
||||||
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
|
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
|
||||||
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
|
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
||||||
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
|
store %main.hasPadding %s.elt, ptr %hashmap.key, align 8
|
||||||
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
|
%hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
|
||||||
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
|
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||||
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
|
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||||
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
|
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
|
||||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
||||||
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
|
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
|
||||||
@@ -125,25 +104,14 @@ define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2
|
|||||||
entry:
|
entry:
|
||||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
||||||
%hashmap.value = alloca i32, align 4
|
%hashmap.value = alloca i32, align 4
|
||||||
%s1 = alloca [2 x %main.hasPadding], align 8
|
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s1, align 8
|
|
||||||
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #5
|
|
||||||
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
|
||||||
store %main.hasPadding %s.elt, ptr %s1, align 8
|
|
||||||
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
|
|
||||||
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
|
|
||||||
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
|
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
||||||
store i32 5, ptr %hashmap.value, align 4
|
store i32 5, ptr %hashmap.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
|
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
|
||||||
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
|
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
||||||
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
|
store %main.hasPadding %s.elt, ptr %hashmap.key, align 8
|
||||||
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
|
%hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
|
||||||
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
|
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||||
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
|
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
|
||||||
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
|
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
|
||||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
|
||||||
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
|
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
|
||||||
@@ -164,9 +132,9 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||||
attributes #4 = { argmemonly nocallback nofree nosync nounwind willreturn }
|
attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #5 = { nounwind }
|
attributes #5 = { nounwind }
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -14,11 +14,11 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.8
|
github.com/mattn/go-colorable v0.1.8
|
||||||
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.3.5
|
go.bug.st/serial v1.6.0
|
||||||
golang.org/x/sys v0.4.0
|
golang.org/x/sys v0.11.0
|
||||||
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8
|
golang.org/x/tools v0.12.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32
|
tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
|
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
|
||||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
|
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
go.bug.st/serial v1.3.5 h1:k50SqGZCnHZ2MiBQgzccXWG+kd/XpOs1jUljpDDKzaE=
|
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
|
||||||
go.bug.st/serial v1.3.5/go.mod h1:z8CesKorE90Qr/oRSJiEuvzYRKol9r/anJZEb5kt304=
|
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-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 +56,14 @@ 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.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 h1:LHoToPgySGSr2NcUHbjENAidHz38RkKaNmmntwn9TjI=
|
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||||
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 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-20221028183034-8341240c0b32 h1:LvdmoXncO43m2cws1chRB2hkLBAxfN6CbSjDI7+gk4Y=
|
tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c h1:rS8mAFqf0CfxPCbtfsI3bWL4jkb0TBYA1wx7tY1nu28=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||||
|
|||||||
+53
-99
@@ -4,15 +4,16 @@ package goenv
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Keys is a slice of all available environment variable keys.
|
// Keys is a slice of all available environment variable keys.
|
||||||
@@ -37,6 +38,53 @@ func init() {
|
|||||||
// directory.
|
// directory.
|
||||||
var TINYGOROOT string
|
var TINYGOROOT string
|
||||||
|
|
||||||
|
// Variables read from a `go env` command invocation.
|
||||||
|
var goEnvVars struct {
|
||||||
|
GOPATH string
|
||||||
|
GOROOT string
|
||||||
|
GOVERSION string
|
||||||
|
}
|
||||||
|
|
||||||
|
var goEnvVarsOnce sync.Once
|
||||||
|
var goEnvVarsErr error // error returned from cmd.Run
|
||||||
|
|
||||||
|
// Make sure goEnvVars is fresh. This can be called multiple times, the first
|
||||||
|
// time will update all environment variables in goEnvVars.
|
||||||
|
func readGoEnvVars() error {
|
||||||
|
goEnvVarsOnce.Do(func() {
|
||||||
|
cmd := exec.Command("go", "env", "-json", "GOPATH", "GOROOT", "GOVERSION")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
// Check for "command not found" error.
|
||||||
|
if execErr, ok := err.(*exec.Error); ok {
|
||||||
|
goEnvVarsErr = fmt.Errorf("could not find '%s' command: %w", execErr.Name, execErr.Err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// It's perhaps a bit ugly to handle this error here, but I couldn't
|
||||||
|
// think of a better place further up in the call chain.
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() != 0 {
|
||||||
|
if len(exitErr.Stderr) != 0 {
|
||||||
|
// The 'go' command exited with an error message. Print that
|
||||||
|
// message and exit, so we behave in a similar way.
|
||||||
|
os.Stderr.Write(exitErr.Stderr)
|
||||||
|
os.Exit(exitErr.ExitCode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Other errors. Not sure whether there are any, but just in case.
|
||||||
|
goEnvVarsErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(output, &goEnvVars)
|
||||||
|
if err != nil {
|
||||||
|
// This should never happen if we have a sane Go toolchain
|
||||||
|
// installed.
|
||||||
|
goEnvVarsErr = fmt.Errorf("unexpected error while unmarshalling `go env` output: %w", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return goEnvVarsErr
|
||||||
|
}
|
||||||
|
|
||||||
// Get returns a single environment variable, possibly calculating it on-demand.
|
// Get returns a single environment variable, possibly calculating it on-demand.
|
||||||
// The empty string is returned for unknown environment variables.
|
// The empty string is returned for unknown environment variables.
|
||||||
func Get(name string) string {
|
func Get(name string) string {
|
||||||
@@ -70,15 +118,11 @@ func Get(name string) string {
|
|||||||
// especially when floating point instructions are involved.
|
// especially when floating point instructions are involved.
|
||||||
return "6"
|
return "6"
|
||||||
case "GOROOT":
|
case "GOROOT":
|
||||||
return getGoroot()
|
readGoEnvVars()
|
||||||
|
return goEnvVars.GOROOT
|
||||||
case "GOPATH":
|
case "GOPATH":
|
||||||
if dir := os.Getenv("GOPATH"); dir != "" {
|
readGoEnvVars()
|
||||||
return dir
|
return goEnvVars.GOPATH
|
||||||
}
|
|
||||||
|
|
||||||
// fallback
|
|
||||||
home := getHomeDir()
|
|
||||||
return filepath.Join(home, "go")
|
|
||||||
case "GOCACHE":
|
case "GOCACHE":
|
||||||
// Get the cache directory, usually ~/.cache/tinygo
|
// Get the cache directory, usually ~/.cache/tinygo
|
||||||
dir, err := os.UserCacheDir()
|
dir, err := os.UserCacheDir()
|
||||||
@@ -240,93 +284,3 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHomeDir() string {
|
|
||||||
u, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
panic("cannot get current user: " + err.Error())
|
|
||||||
}
|
|
||||||
if u.HomeDir == "" {
|
|
||||||
// This is very unlikely, so panic here.
|
|
||||||
// Not the nicest solution, however.
|
|
||||||
panic("could not find home directory")
|
|
||||||
}
|
|
||||||
return u.HomeDir
|
|
||||||
}
|
|
||||||
|
|
||||||
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
|
|
||||||
// found, it returns an empty string.
|
|
||||||
func getGoroot() string {
|
|
||||||
// An explicitly set GOROOT always has preference.
|
|
||||||
goroot := os.Getenv("GOROOT")
|
|
||||||
if goroot != "" {
|
|
||||||
// Convert to the standard GOROOT being referenced, if it's a TinyGo cache.
|
|
||||||
return getStandardGoroot(goroot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for the location of the 'go' binary and base GOROOT on that.
|
|
||||||
binpath, err := exec.LookPath("go")
|
|
||||||
if err == nil {
|
|
||||||
binpath, err = filepath.EvalSymlinks(binpath)
|
|
||||||
if err == nil {
|
|
||||||
goroot := filepath.Dir(filepath.Dir(binpath))
|
|
||||||
if isGoroot(goroot) {
|
|
||||||
return goroot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check what GOROOT was at compile time.
|
|
||||||
if isGoroot(runtime.GOROOT()) {
|
|
||||||
return runtime.GOROOT()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for some standard locations, as a last resort.
|
|
||||||
var candidates []string
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "linux":
|
|
||||||
candidates = []string{
|
|
||||||
"/usr/local/go", // manually installed
|
|
||||||
"/usr/lib/go", // from the distribution
|
|
||||||
"/snap/go/current/", // installed using snap
|
|
||||||
}
|
|
||||||
case "darwin":
|
|
||||||
candidates = []string{
|
|
||||||
"/usr/local/go", // manually installed
|
|
||||||
"/usr/local/opt/go/libexec", // from Homebrew
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, candidate := range candidates {
|
|
||||||
if isGoroot(candidate) {
|
|
||||||
return candidate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't find GOROOT...
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// isGoroot checks whether the given path looks like a GOROOT.
|
|
||||||
func isGoroot(goroot string) bool {
|
|
||||||
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getStandardGoroot returns the physical path to a real, standard Go GOROOT
|
|
||||||
// implied by the given path.
|
|
||||||
// If the given path appears to be a TinyGo cached GOROOT, it returns the path
|
|
||||||
// referenced by symlinks contained in the cache. Otherwise, it returns the
|
|
||||||
// given path as-is.
|
|
||||||
func getStandardGoroot(path string) string {
|
|
||||||
// Check if the "bin" subdirectory of our given GOROOT is a symlink, and then
|
|
||||||
// return the _parent_ directory of its destination.
|
|
||||||
if dest, err := os.Readlink(filepath.Join(path, "bin")); nil == err {
|
|
||||||
// Clean the destination to remove any trailing slashes, so that
|
|
||||||
// filepath.Dir will always return the parent.
|
|
||||||
// (because both "/foo" and "/foo/" are valid symlink destinations,
|
|
||||||
// but filepath.Dir would return "/" and "/foo", respectively)
|
|
||||||
return filepath.Dir(filepath.Clean(dest))
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|||||||
+8
-26
@@ -4,15 +4,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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.28.0-dev"
|
const Version = "0.30.0"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// This variable is set at build time using -ldflags parameters.
|
// This variable is set at build time using -ldflags parameters.
|
||||||
@@ -22,8 +19,8 @@ var (
|
|||||||
|
|
||||||
// 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(goroot string) (major, minor int, err error) {
|
func GetGorootVersion() (major, minor int, err error) {
|
||||||
s, err := GorootVersionString(goroot)
|
s, err := GorootVersionString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
@@ -51,24 +48,9 @@ func GetGorootVersion(goroot string) (major, minor int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GorootVersionString returns the version string as reported by the Go
|
// GorootVersionString returns the version string as reported by the Go
|
||||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
// toolchain. It is usually of the form `go1.x.y` but can have some variations
|
||||||
// can have some variations (for beta releases, for example).
|
// (for beta releases, for example).
|
||||||
func GorootVersionString(goroot string) (string, error) {
|
func GorootVersionString() (string, error) {
|
||||||
if data, err := os.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
err := readGoEnvVars()
|
||||||
return string(data), nil
|
return goEnvVars.GOVERSION, err
|
||||||
|
|
||||||
} else if data, err := os.ReadFile(filepath.Join(
|
|
||||||
goroot, "src", "internal", "buildcfg", "zbootstrap.go")); err == nil {
|
|
||||||
|
|
||||||
r := regexp.MustCompile("const version = `(.*)`")
|
|
||||||
matches := r.FindSubmatch(data)
|
|
||||||
if len(matches) != 2 {
|
|
||||||
return "", errors.New("Invalid go version output:\n" + string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(matches[1]), nil
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -49,7 +49,10 @@ func (inst *instruction) String() string {
|
|||||||
operands[i] = op.String()
|
operands[i] = op.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
name := instructionNameMap[inst.opcode]
|
name := ""
|
||||||
|
if int(inst.opcode) < len(instructionNameMap) {
|
||||||
|
name = instructionNameMap[inst.opcode]
|
||||||
|
}
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = "<unknown op>"
|
name = "<unknown op>"
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -35,14 +35,14 @@ func isRecoverableError(err error) bool {
|
|||||||
// ErrorLine is one line in a traceback. The position may be missing.
|
// ErrorLine is one line in a traceback. The position may be missing.
|
||||||
type ErrorLine struct {
|
type ErrorLine struct {
|
||||||
Pos token.Position
|
Pos token.Position
|
||||||
Inst llvm.Value
|
Inst string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error encapsulates compile-time interpretation errors with an associated
|
// Error encapsulates compile-time interpretation errors with an associated
|
||||||
// import path. The errors may not have a precise location attached.
|
// import path. The errors may not have a precise location attached.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
ImportPath string
|
ImportPath string
|
||||||
Inst llvm.Value
|
Inst string
|
||||||
Pos token.Position
|
Pos token.Position
|
||||||
Err error
|
Err error
|
||||||
Traceback []ErrorLine
|
Traceback []ErrorLine
|
||||||
@@ -60,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.llvmInst,
|
Inst: inst.llvmInst.String(),
|
||||||
Pos: pos,
|
Pos: pos,
|
||||||
Err: err,
|
Err: err,
|
||||||
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
Traceback: []ErrorLine{{pos, inst.llvmInst.String()}},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-6
@@ -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
|
||||||
@@ -46,9 +46,9 @@ func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
|
|||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
|||||||
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.
|
||||||
@@ -174,8 +174,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
|||||||
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)
|
||||||
|
|||||||
+6
-11
@@ -57,16 +57,14 @@ func runTest(t *testing.T, pathPrefix string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if err, match := err.(*Error); match {
|
if err, match := err.(*Error); match {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
if !err.Inst.IsNil() {
|
if len(err.Inst) != 0 {
|
||||||
err.Inst.Dump()
|
println(err.Inst)
|
||||||
println()
|
|
||||||
}
|
}
|
||||||
if len(err.Traceback) > 0 {
|
if len(err.Traceback) > 0 {
|
||||||
println("\ntraceback:")
|
println("\ntraceback:")
|
||||||
for _, line := range err.Traceback {
|
for _, line := range err.Traceback {
|
||||||
println(line.Pos.String() + ":")
|
println(line.Pos.String() + ":")
|
||||||
line.Inst.Dump()
|
println(line.Inst)
|
||||||
println()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,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")
|
||||||
|
|||||||
+15
-13
@@ -338,6 +338,20 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
|
if mem.hasExternalStore(src) || mem.hasExternalLoadOrStore(dst) {
|
||||||
|
// These are the same checks as there are on llvm.Load
|
||||||
|
// and llvm.Store in the interpreter. Copying is
|
||||||
|
// essentially loading from the source array and storing
|
||||||
|
// to the destination array, hence why we need to do the
|
||||||
|
// same checks here.
|
||||||
|
// This fixes the following bug:
|
||||||
|
// https://github.com/tinygo-org/tinygo/issues/3890
|
||||||
|
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
nBytes := uint32(n * elemSize)
|
nBytes := uint32(n * elemSize)
|
||||||
dstObj := mem.getWritable(dst.index())
|
dstObj := mem.getWritable(dst.index())
|
||||||
dstBuf := dstObj.buffer.asRawValue(r)
|
dstBuf := dstObj.buffer.asRawValue(r)
|
||||||
@@ -529,7 +543,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// 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.llvmInst,
|
Inst: inst.llvmInst.String(),
|
||||||
})
|
})
|
||||||
return nil, mem, callErr
|
return nil, mem, callErr
|
||||||
}
|
}
|
||||||
@@ -1032,15 +1046,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
|
|
||||||
}
|
|
||||||
|
|||||||
+4
-15
@@ -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
+23
@@ -7,6 +7,10 @@ target triple = "x86_64--linux"
|
|||||||
@main.int16SliceSrc.buf = internal global [3 x i16] [i16 5, i16 123, i16 1024]
|
@main.int16SliceSrc.buf = internal global [3 x i16] [i16 5, i16 123, i16 1024]
|
||||||
@main.int16SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.int16SliceSrc.buf, i64 3, i64 3 }
|
@main.int16SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.int16SliceSrc.buf, i64 3, i64 3 }
|
||||||
@main.int16SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer
|
@main.int16SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer
|
||||||
|
@main.sliceSrcUntaint.buf = internal global [2 x i8] c"ab"
|
||||||
|
@main.sliceDstUntaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
|
||||||
|
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
|
||||||
declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
|
declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
|
||||||
|
|
||||||
@@ -16,6 +20,8 @@ declare void @runtime.printuint8(i8)
|
|||||||
|
|
||||||
declare void @runtime.printint16(i16)
|
declare void @runtime.printint16(i16)
|
||||||
|
|
||||||
|
declare void @use(ptr)
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
call void @main.init()
|
call void @main.init()
|
||||||
@@ -43,6 +49,15 @@ entry:
|
|||||||
%int16SliceDst.buf = load ptr, ptr @main.int16SliceDst
|
%int16SliceDst.buf = load ptr, ptr @main.int16SliceDst
|
||||||
%int16SliceDst.val = load i16, ptr %int16SliceDst.buf
|
%int16SliceDst.val = load i16, ptr %int16SliceDst.buf
|
||||||
call void @runtime.printint16(i16 %int16SliceDst.val)
|
call void @runtime.printint16(i16 %int16SliceDst.val)
|
||||||
|
|
||||||
|
; print(sliceDstUntaint[0])
|
||||||
|
%sliceDstUntaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstUntaint.buf, i32 0)
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstUntaint.val)
|
||||||
|
|
||||||
|
; print(sliceDstTaint[0])
|
||||||
|
%sliceDstTaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstTaint.buf, i32 0)
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstTaint.val)
|
||||||
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,5 +94,13 @@ entry:
|
|||||||
%int16SliceSrc.buf = extractvalue { ptr, i64, i64 } %int16SliceSrc, 0
|
%int16SliceSrc.buf = extractvalue { ptr, i64, i64 } %int16SliceSrc, 0
|
||||||
%copy.n2 = call i64 @runtime.sliceCopy(ptr %int16SliceDst.buf, ptr %int16SliceSrc.buf, i64 %int16SliceSrc.len, i64 %int16SliceSrc.len, i64 2)
|
%copy.n2 = call i64 @runtime.sliceCopy(ptr %int16SliceDst.buf, ptr %int16SliceSrc.buf, i64 %int16SliceSrc.len, i64 %int16SliceSrc.len, i64 2)
|
||||||
|
|
||||||
|
; Copy slice that has a known value.
|
||||||
|
%copy.n3 = call i64 @runtime.sliceCopy(ptr @main.sliceDstUntaint.buf, ptr @main.sliceSrcUntaint.buf, i64 2, i64 2, i64 1)
|
||||||
|
|
||||||
|
; Copy slice that might have been modified by the external @use call.
|
||||||
|
; This is a fix for https://github.com/tinygo-org/tinygo/issues/3890.
|
||||||
|
call void @use(ptr @main.sliceSrcTaint.buf)
|
||||||
|
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
|
||||||
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+12
@@ -1,12 +1,21 @@
|
|||||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
target triple = "x86_64--linux"
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
|
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
|
||||||
|
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
|
||||||
|
declare i64 @runtime.sliceCopy(ptr, ptr, i64, i64, i64) unnamed_addr
|
||||||
|
|
||||||
declare void @runtime.printuint8(i8) local_unnamed_addr
|
declare void @runtime.printuint8(i8) local_unnamed_addr
|
||||||
|
|
||||||
declare void @runtime.printint16(i16) local_unnamed_addr
|
declare void @runtime.printint16(i16) local_unnamed_addr
|
||||||
|
|
||||||
|
declare void @use(ptr) local_unnamed_addr
|
||||||
|
|
||||||
define void @runtime.initAll() unnamed_addr {
|
define void @runtime.initAll() unnamed_addr {
|
||||||
entry:
|
entry:
|
||||||
|
call void @use(ptr @main.sliceSrcTaint.buf)
|
||||||
|
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,5 +25,8 @@ entry:
|
|||||||
call void @runtime.printuint8(i8 3)
|
call void @runtime.printuint8(i8 3)
|
||||||
call void @runtime.printint16(i16 5)
|
call void @runtime.printint16(i16 5)
|
||||||
call void @runtime.printint16(i16 5)
|
call void @runtime.printint16(i16 5)
|
||||||
|
call void @runtime.printuint8(i8 97)
|
||||||
|
%sliceDstTaint.val = load i8, ptr @main.sliceDstTaint.buf, align 1
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstTaint.val)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule
+1
Submodule lib/renesas-svd added at 03d7688085
@@ -207,6 +207,11 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
|
|||||||
merges[dir] = filepath.Join(goroot, dir)
|
merges[dir] = filepath.Join(goroot, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Required starting in Go 1.21 due to https://github.com/golang/go/issues/61928
|
||||||
|
if _, err := os.Stat(filepath.Join(goroot, "go.env")); err == nil {
|
||||||
|
merges["go.env"] = filepath.Join(goroot, "go.env")
|
||||||
|
}
|
||||||
|
|
||||||
return merges, nil
|
return merges, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -946,112 +947,130 @@ func touchSerialPortAt1200bps(port string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
||||||
// find standard UF2 info path
|
for start := time.Now(); time.Since(start) < options.Timeout; {
|
||||||
infoPaths := make([]string, 0, len(volumes))
|
// Find a UF2 mount point.
|
||||||
for _, volume := range volumes {
|
mounts, err := findFATMounts(options)
|
||||||
switch runtime.GOOS {
|
if err != nil {
|
||||||
case "linux", "freebsd":
|
return err
|
||||||
fi, err := os.Stat("/run/media")
|
}
|
||||||
if err != nil || !fi.IsDir() {
|
for _, mount := range mounts {
|
||||||
infoPaths = append(infoPaths, "/media/*/"+volume+"/INFO_UF2.TXT")
|
for _, volume := range volumes {
|
||||||
} else {
|
if mount.name != volume {
|
||||||
infoPaths = append(infoPaths, "/run/media/*/"+volume+"/INFO_UF2.TXT")
|
continue
|
||||||
}
|
}
|
||||||
case "darwin":
|
if _, err := os.Stat(filepath.Join(mount.path, "INFO_UF2.TXT")); err != nil {
|
||||||
infoPaths = append(infoPaths, "/Volumes/"+volume+"/INFO_UF2.TXT")
|
// No INFO_UF2.TXT found, which is expected on a UF2
|
||||||
case "windows":
|
// filesystem.
|
||||||
path, err := windowsFindUSBDrive(volume, options)
|
continue
|
||||||
if err == nil {
|
}
|
||||||
infoPaths = append(infoPaths, path+"/INFO_UF2.TXT")
|
// Found the filesystem, so flash the device!
|
||||||
|
return moveFile(tmppath, filepath.Join(mount.path, "flash.uf2"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
||||||
d, err := locateDevice(volumes, infoPaths, options.Timeout)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
|
||||||
// find expected volume path
|
for start := time.Now(); time.Since(start) < options.Timeout; {
|
||||||
destPaths := make([]string, 0, len(volumes))
|
// Find all mount points.
|
||||||
for _, volume := range volumes {
|
mounts, err := findFATMounts(options)
|
||||||
switch runtime.GOOS {
|
if err != nil {
|
||||||
case "linux", "freebsd":
|
return err
|
||||||
fi, err := os.Stat("/run/media")
|
}
|
||||||
if err != nil || !fi.IsDir() {
|
for _, mount := range mounts {
|
||||||
destPaths = append(destPaths, "/media/*/"+volume)
|
for _, volume := range volumes {
|
||||||
} else {
|
if mount.name != volume {
|
||||||
destPaths = append(destPaths, "/run/media/*/"+volume)
|
continue
|
||||||
}
|
}
|
||||||
case "darwin":
|
// Found the filesystem, so flash the device!
|
||||||
destPaths = append(destPaths, "/Volumes/"+volume)
|
return moveFile(tmppath, filepath.Join(mount.path, "flash.hex"))
|
||||||
case "windows":
|
|
||||||
path, err := windowsFindUSBDrive(volume, options)
|
|
||||||
if err == nil {
|
|
||||||
destPaths = append(destPaths, path+"/")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
d, err := locateDevice(volumes, destPaths, options.Timeout)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return moveFile(tmppath, d+"/flash.hex")
|
|
||||||
}
|
|
||||||
|
|
||||||
func locateDevice(volumes, paths []string, timeout time.Duration) (string, error) {
|
|
||||||
var d []string
|
|
||||||
var err error
|
|
||||||
for start := time.Now(); time.Since(start) < timeout; {
|
|
||||||
for _, path := range paths {
|
|
||||||
d, err = filepath.Glob(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if d != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if d != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
}
|
}
|
||||||
if d == nil {
|
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
||||||
return "", errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
|
|
||||||
}
|
|
||||||
return d[0], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) {
|
type mountPoint struct {
|
||||||
cmd := executeCommand(options, "wmic",
|
name string
|
||||||
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
|
path string
|
||||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
}
|
||||||
|
|
||||||
var out bytes.Buffer
|
// Find all the mount points on the system that use the FAT filesystem.
|
||||||
cmd.Stdout = &out
|
func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
|
||||||
err := cmd.Run()
|
var points []mountPoint
|
||||||
if err != nil {
|
switch runtime.GOOS {
|
||||||
return "", err
|
case "darwin":
|
||||||
}
|
list, err := os.ReadDir("/Volumes")
|
||||||
|
if err != nil {
|
||||||
for _, line := range strings.Split(out.String(), "\n") {
|
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||||
words := strings.Fields(line)
|
|
||||||
if len(words) >= 3 {
|
|
||||||
if words[1] == "2" && words[2] == "FAT" {
|
|
||||||
return words[0], nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
for _, elem := range list {
|
||||||
|
// TODO: find a way to check for the filesystem type.
|
||||||
|
// (Only return FAT filesystems).
|
||||||
|
points = append(points, mountPoint{
|
||||||
|
name: elem.Name(),
|
||||||
|
path: filepath.Join("/Volumes", elem.Name()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(points, func(i, j int) bool {
|
||||||
|
return points[i].path < points[j].name
|
||||||
|
})
|
||||||
|
return points, nil
|
||||||
|
case "linux":
|
||||||
|
tab, err := os.ReadFile("/proc/mounts") // symlink to /proc/self/mounts on my system
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||||
|
}
|
||||||
|
for _, line := range strings.Split(string(tab), "\n") {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) <= 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fstype := fields[2]
|
||||||
|
if fstype != "vfat" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
points = append(points, mountPoint{
|
||||||
|
name: filepath.Base(fields[1]),
|
||||||
|
path: fields[1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return points, nil
|
||||||
|
case "windows":
|
||||||
|
// Obtain a list of all currently mounted volumes.
|
||||||
|
cmd := executeCommand(options, "wmic",
|
||||||
|
"PATH", "Win32_LogicalDisk",
|
||||||
|
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract data to convert to a []mountPoint slice.
|
||||||
|
for _, line := range strings.Split(out.String(), "\n") {
|
||||||
|
words := strings.Fields(line)
|
||||||
|
if len(words) < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if words[1] != "2" || words[2] != "FAT" {
|
||||||
|
// - DriveType 2 is removable (which we're looking for).
|
||||||
|
// - We only want to return FAT filesystems.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
points = append(points, mountPoint{
|
||||||
|
name: words[3],
|
||||||
|
path: words[0],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return points, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown GOOS for listing mount points: %s", runtime.GOOS)
|
||||||
}
|
}
|
||||||
return "", errors.New("unable to locate a USB device to be flashed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDefaultPort returns the default serial port depending on the operating system.
|
// getDefaultPort returns the default serial port depending on the operating system.
|
||||||
@@ -1270,16 +1289,14 @@ func printCompilerError(logln func(...interface{}), err error) {
|
|||||||
case *interp.Error:
|
case *interp.Error:
|
||||||
logln("#", err.ImportPath)
|
logln("#", err.ImportPath)
|
||||||
logln(err.Error())
|
logln(err.Error())
|
||||||
if !err.Inst.IsNil() {
|
if len(err.Inst) != 0 {
|
||||||
err.Inst.Dump()
|
logln(err.Inst)
|
||||||
logln()
|
|
||||||
}
|
}
|
||||||
if len(err.Traceback) > 0 {
|
if len(err.Traceback) > 0 {
|
||||||
logln("\ntraceback:")
|
logln("\ntraceback:")
|
||||||
for _, line := range err.Traceback {
|
for _, line := range err.Traceback {
|
||||||
logln(line.Pos.String() + ":")
|
logln(line.Pos.String() + ":")
|
||||||
line.Inst.Dump()
|
logln(line.Inst)
|
||||||
logln()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case loader.Errors:
|
case loader.Errors:
|
||||||
@@ -1777,15 +1794,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
if flagJSON {
|
if flagJSON {
|
||||||
json, _ := json.MarshalIndent(struct {
|
json, _ := json.MarshalIndent(struct {
|
||||||
GOROOT string `json:"goroot"`
|
Target *compileopts.TargetSpec `json:"target"`
|
||||||
GOOS string `json:"goos"`
|
GOROOT string `json:"goroot"`
|
||||||
GOARCH string `json:"goarch"`
|
GOOS string `json:"goos"`
|
||||||
GOARM string `json:"goarm"`
|
GOARCH string `json:"goarch"`
|
||||||
BuildTags []string `json:"build_tags"`
|
GOARM string `json:"goarm"`
|
||||||
GC string `json:"garbage_collector"`
|
BuildTags []string `json:"build_tags"`
|
||||||
Scheduler string `json:"scheduler"`
|
GC string `json:"garbage_collector"`
|
||||||
LLVMTriple string `json:"llvm_triple"`
|
Scheduler string `json:"scheduler"`
|
||||||
|
LLVMTriple string `json:"llvm_triple"`
|
||||||
}{
|
}{
|
||||||
|
Target: config.Target,
|
||||||
GOROOT: cachedGOROOT,
|
GOROOT: cachedGOROOT,
|
||||||
GOOS: config.GOOS(),
|
GOOS: config.GOOS(),
|
||||||
GOARCH: config.GOARCH(),
|
GOARCH: config.GOARCH(),
|
||||||
@@ -1852,7 +1871,7 @@ func main() {
|
|||||||
usage(command)
|
usage(command)
|
||||||
case "version":
|
case "version":
|
||||||
goversion := "<unknown>"
|
goversion := "<unknown>"
|
||||||
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
|
if s, err := goenv.GorootVersionString(); err == nil {
|
||||||
goversion = s
|
goversion = s
|
||||||
}
|
}
|
||||||
version := goenv.Version
|
version := goenv.Version
|
||||||
|
|||||||
+24
-2
@@ -34,6 +34,16 @@ var supportedLinuxArches = map[string]string{
|
|||||||
"X86Linux": "linux/386",
|
"X86Linux": "linux/386",
|
||||||
"ARMLinux": "linux/arm/6",
|
"ARMLinux": "linux/arm/6",
|
||||||
"ARM64Linux": "linux/arm64",
|
"ARM64Linux": "linux/arm64",
|
||||||
|
"WASIp1": "wasip1/wasm",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
major, _, _ := goenv.GetGorootVersion()
|
||||||
|
if major < 21 {
|
||||||
|
// Go 1.20 backwards compatibility.
|
||||||
|
// Should be removed once we drop support for Go 1.20.
|
||||||
|
delete(supportedLinuxArches, "WASIp1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sema = make(chan struct{}, runtime.NumCPU())
|
var sema = make(chan struct{}, runtime.NumCPU())
|
||||||
@@ -71,6 +81,16 @@ func TestBuild(t *testing.T) {
|
|||||||
"zeroalloc.go",
|
"zeroalloc.go",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go 1.21 made some changes to the language, which we can only test when
|
||||||
|
// we're actually on Go 1.21.
|
||||||
|
_, minor, err := goenv.GetGorootVersion()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not get version:", minor)
|
||||||
|
}
|
||||||
|
if minor >= 21 {
|
||||||
|
tests = append(tests, "go1.21.go")
|
||||||
|
}
|
||||||
|
|
||||||
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),
|
||||||
// which is especially useful to quickly check whether some changes
|
// which is especially useful to quickly check whether some changes
|
||||||
@@ -166,6 +186,8 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
t.Fatal("failed to load target spec:", err)
|
t.Fatal("failed to load target spec:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isWebAssembly := options.Target == "wasi" || options.Target == "wasm" || (options.Target == "" && options.GOARCH == "wasm")
|
||||||
|
|
||||||
for _, name := range tests {
|
for _, name := range tests {
|
||||||
if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") {
|
if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") {
|
||||||
switch name {
|
switch name {
|
||||||
@@ -216,7 +238,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if options.Target == "wasi" || options.Target == "wasm" {
|
if isWebAssembly {
|
||||||
t.Run("alias.go-scheduler-none", func(t *testing.T) {
|
t.Run("alias.go-scheduler-none", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
options := compileopts.Options(options)
|
options := compileopts.Options(options)
|
||||||
@@ -236,7 +258,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
runTest("rand.go", options, t, nil, nil)
|
runTest("rand.go", options, t, nil, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if options.Target != "wasi" && options.Target != "wasm" {
|
if !isWebAssembly {
|
||||||
// The recover() builtin isn't supported yet on WebAssembly and Windows.
|
// The recover() builtin isn't supported yet on WebAssembly and Windows.
|
||||||
t.Run("recover.go", func(t *testing.T) {
|
t.Run("recover.go", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build arduino
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
const (
|
||||||
|
button = machine.D2
|
||||||
|
buttonMode = machine.PinInputPullup
|
||||||
|
buttonPinChange = machine.PinRising
|
||||||
|
)
|
||||||
@@ -5,6 +5,7 @@ package main
|
|||||||
import "machine"
|
import "machine"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
button = machine.BUTTON
|
||||||
buttonMode = machine.PinInputPulldown
|
buttonMode = machine.PinInputPulldown
|
||||||
buttonPinChange = machine.PinFalling
|
buttonPinChange = machine.PinFalling
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package main
|
|||||||
import "machine"
|
import "machine"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
button = machine.BUTTON
|
||||||
buttonMode = machine.PinInputPullup
|
buttonMode = machine.PinInputPullup
|
||||||
buttonPinChange = machine.PinRising
|
buttonPinChange = machine.PinRising
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,18 +8,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
"machine"
|
||||||
"runtime/volatile"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
button = machine.BUTTON
|
led = machine.LED
|
||||||
led = machine.LED
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var lightLed volatile.Register8
|
|
||||||
lightLed.Set(0)
|
|
||||||
|
|
||||||
// Configure the LED, defaulting to on (usually setting the pin to low will
|
// Configure the LED, defaulting to on (usually setting the pin to low will
|
||||||
// turn the LED on).
|
// turn the LED on).
|
||||||
@@ -33,13 +29,7 @@ func main() {
|
|||||||
|
|
||||||
// Set an interrupt on this pin.
|
// Set an interrupt on this pin.
|
||||||
err := button.SetInterrupt(buttonPinChange, func(machine.Pin) {
|
err := button.SetInterrupt(buttonPinChange, func(machine.Pin) {
|
||||||
if lightLed.Get() != 0 {
|
led.Set(!led.Get())
|
||||||
lightLed.Set(0)
|
|
||||||
led.Low()
|
|
||||||
} else {
|
|
||||||
lightLed.Set(1)
|
|
||||||
led.High()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("could not configure pin interrupt:", err.Error())
|
println("could not configure pin interrupt:", err.Error())
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package main
|
|||||||
import "machine"
|
import "machine"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
button = machine.BUTTON
|
||||||
buttonMode = machine.PinInputPulldown
|
buttonMode = machine.PinInputPulldown
|
||||||
buttonPinChange = machine.PinRising | machine.PinFalling
|
buttonPinChange = machine.PinRising | machine.PinFalling
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package main
|
|||||||
import "machine"
|
import "machine"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
button = machine.BUTTON
|
||||||
buttonMode = machine.PinInput
|
buttonMode = machine.PinInput
|
||||||
buttonPinChange = machine.PinFalling
|
buttonPinChange = machine.PinFalling
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// This example demonstrates how to set the system time.
|
||||||
|
//
|
||||||
|
// Usually, boards don't keep time on power cycles and restarts, it resets to Unix epoch.
|
||||||
|
// The system time can be set by calling runtime.AdjustTimeOffset().
|
||||||
|
//
|
||||||
|
// Possible time sources: an external RTC clock or network (WiFi access point or NTP server)
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const myTime = "2006-01-02T15:04:05Z" // this is an example time you source somewhere
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// measure how many nanoseconds the internal clock is behind
|
||||||
|
timeOfMeasurement := time.Now()
|
||||||
|
actualTime, _ := time.Parse(time.RFC3339, myTime)
|
||||||
|
offset := actualTime.Sub(timeOfMeasurement)
|
||||||
|
|
||||||
|
// adjust internal clock by adding the offset to the internal clock
|
||||||
|
runtime.AdjustTimeOffset(int64(offset))
|
||||||
|
|
||||||
|
for {
|
||||||
|
fmt.Printf("%v\r\n", time.Now().Format(time.RFC3339))
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,47 +10,57 @@ import (
|
|||||||
// Try it easily by opening the following site in Chrome.
|
// Try it easily by opening the following site in Chrome.
|
||||||
// https://www.onlinemusictools.com/kb/
|
// https://www.onlinemusictools.com/kb/
|
||||||
|
|
||||||
|
const (
|
||||||
|
cable = 0
|
||||||
|
channel = 1
|
||||||
|
velocity = 0x40
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
led := machine.LED
|
led := machine.LED
|
||||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
button := machine.BUTTON
|
button := machine.BUTTON
|
||||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
|
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
m := midi.Port()
|
m := midi.Port()
|
||||||
m.SetHandler(func(b []byte) {
|
m.SetRxHandler(func(b []byte) {
|
||||||
|
// blink when we receive a MIDI message
|
||||||
|
led.Set(!led.Get())
|
||||||
|
})
|
||||||
|
|
||||||
|
m.SetTxHandler(func() {
|
||||||
|
// blink when we send a MIDI message
|
||||||
led.Set(!led.Get())
|
led.Set(!led.Get())
|
||||||
m.Write(b)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
prev := true
|
prev := true
|
||||||
chords := []struct {
|
chords := []struct {
|
||||||
name string
|
name string
|
||||||
keys []midi.Note
|
notes []midi.Note
|
||||||
}{
|
}{
|
||||||
{name: "C ", keys: []midi.Note{midi.C4, midi.E4, midi.G4}},
|
{name: "C ", notes: []midi.Note{midi.C4, midi.E4, midi.G4}},
|
||||||
{name: "G ", keys: []midi.Note{midi.G3, midi.B3, midi.D4}},
|
{name: "G ", notes: []midi.Note{midi.G3, midi.B3, midi.D4}},
|
||||||
{name: "Am", keys: []midi.Note{midi.A3, midi.C4, midi.E4}},
|
{name: "Am", notes: []midi.Note{midi.A3, midi.C4, midi.E4}},
|
||||||
{name: "F ", keys: []midi.Note{midi.F3, midi.A3, midi.C4}},
|
{name: "F ", notes: []midi.Note{midi.F3, midi.A3, midi.C4}},
|
||||||
}
|
}
|
||||||
index := 0
|
index := 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
current := button.Get()
|
current := button.Get()
|
||||||
if prev != current {
|
if prev != current {
|
||||||
led.Set(current)
|
|
||||||
if current {
|
if current {
|
||||||
for _, c := range chords[index].keys {
|
for _, note := range chords[index].notes {
|
||||||
m.NoteOff(0, 0, c, 0x40)
|
m.NoteOff(cable, channel, note, velocity)
|
||||||
}
|
}
|
||||||
index = (index + 1) % len(chords)
|
index = (index + 1) % len(chords)
|
||||||
} else {
|
} else {
|
||||||
for _, c := range chords[index].keys {
|
for _, note := range chords[index].notes {
|
||||||
m.NoteOn(0, 0, c, 0x40)
|
m.NoteOn(cable, channel, note, velocity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prev = current
|
prev = current
|
||||||
}
|
}
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
//sleep for 2 secs for console
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
config := machine.WatchdogConfig{
|
||||||
|
TimeoutMillis: 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
println("configuring watchdog for max 1 second updates")
|
||||||
|
machine.Watchdog.Configure(config)
|
||||||
|
|
||||||
|
// From this point the watchdog is running and Update must be
|
||||||
|
// called periodically, per the config
|
||||||
|
machine.Watchdog.Start()
|
||||||
|
|
||||||
|
// This loop should complete because watchdog update is called
|
||||||
|
// every 100ms.
|
||||||
|
start := time.Now()
|
||||||
|
println("updating watchdog for 3 seconds")
|
||||||
|
for i := 0; i < 30; i++ {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
machine.Watchdog.Update()
|
||||||
|
fmt.Printf("alive @ %v\n", time.Now().Sub(start))
|
||||||
|
}
|
||||||
|
|
||||||
|
// This loop should cause a watchdog reset after 1s since
|
||||||
|
// there is no update call.
|
||||||
|
start = time.Now()
|
||||||
|
println("entering tight loop")
|
||||||
|
for {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
fmt.Printf("alive @ %v\n", time.Now().Sub(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -251,3 +251,13 @@ func IndexRabinKarp(s, substr string) int {
|
|||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeNoZero makes a slice of length and capacity n without zeroing the bytes.
|
||||||
|
// It is the caller's responsibility to ensure uninitialized bytes
|
||||||
|
// do not leak to the end user.
|
||||||
|
func MakeNoZero(n int) []byte {
|
||||||
|
// Note: this does zero the buffer even though that's not necessary.
|
||||||
|
// For performance reasons we might want to change this (similar to the
|
||||||
|
// malloc function implemented in the runtime).
|
||||||
|
return make([]byte, n)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
//go:build ae_rp2040
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/rp"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GPIO pins
|
||||||
|
const (
|
||||||
|
GP0 Pin = GPIO0
|
||||||
|
GP1 Pin = GPIO1
|
||||||
|
GP2 Pin = GPIO2
|
||||||
|
GP3 Pin = GPIO3
|
||||||
|
GP4 Pin = GPIO4
|
||||||
|
GP5 Pin = GPIO5
|
||||||
|
GP6 Pin = GPIO6
|
||||||
|
GP7 Pin = GPIO7
|
||||||
|
GP8 Pin = GPIO8
|
||||||
|
GP9 Pin = GPIO9
|
||||||
|
GP10 Pin = GPIO10
|
||||||
|
GP11 Pin = GPIO11
|
||||||
|
GP12 Pin = GPIO12
|
||||||
|
GP13 Pin = GPIO13
|
||||||
|
GP14 Pin = GPIO14
|
||||||
|
GP15 Pin = GPIO15
|
||||||
|
GP16 Pin = GPIO16
|
||||||
|
GP17 Pin = GPIO17
|
||||||
|
GP18 Pin = GPIO18
|
||||||
|
GP19 Pin = GPIO19
|
||||||
|
GP20 Pin = GPIO20
|
||||||
|
GP21 Pin = GPIO21
|
||||||
|
GP22 Pin = GPIO22
|
||||||
|
GP26 Pin = GPIO26
|
||||||
|
GP27 Pin = GPIO27
|
||||||
|
GP28 Pin = GPIO28
|
||||||
|
GP29 Pin = GPIO29
|
||||||
|
|
||||||
|
// Onboard crystal oscillator frequency, in MHz.
|
||||||
|
xoscFreq = 12 // MHz
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C Default pins on Raspberry Pico.
|
||||||
|
const (
|
||||||
|
I2C0_SDA_PIN = GP4
|
||||||
|
I2C0_SCL_PIN = GP5
|
||||||
|
|
||||||
|
I2C1_SDA_PIN = GP2
|
||||||
|
I2C1_SCL_PIN = GP3
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI default pins
|
||||||
|
const (
|
||||||
|
// Default Serial Clock Bus 0 for SPI communications
|
||||||
|
SPI0_SCK_PIN = GPIO18
|
||||||
|
// Default Serial Out Bus 0 for SPI communications
|
||||||
|
SPI0_SDO_PIN = GPIO19 // Tx
|
||||||
|
// Default Serial In Bus 0 for SPI communications
|
||||||
|
SPI0_SDI_PIN = GPIO16 // Rx
|
||||||
|
|
||||||
|
// Default Serial Clock Bus 1 for SPI communications
|
||||||
|
SPI1_SCK_PIN = GPIO10
|
||||||
|
// Default Serial Out Bus 1 for SPI communications
|
||||||
|
SPI1_SDO_PIN = GPIO11 // Tx
|
||||||
|
// Default Serial In Bus 1 for SPI communications
|
||||||
|
SPI1_SDI_PIN = GPIO12 // Rx
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART pins
|
||||||
|
const (
|
||||||
|
UART0_TX_PIN = GPIO0
|
||||||
|
UART0_RX_PIN = GPIO1
|
||||||
|
UART1_TX_PIN = GPIO8
|
||||||
|
UART1_RX_PIN = GPIO9
|
||||||
|
UART_TX_PIN = UART0_TX_PIN
|
||||||
|
UART_RX_PIN = UART0_RX_PIN
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART on the RP2040
|
||||||
|
var (
|
||||||
|
UART0 = &_UART0
|
||||||
|
_UART0 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: rp.UART0,
|
||||||
|
}
|
||||||
|
|
||||||
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: rp.UART1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultUART = UART0
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
||||||
|
UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// USB identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "AE-RP2040"
|
||||||
|
usb_STRING_MANUFACTURER = "AKIZUKI DENSHI"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x2E8A
|
||||||
|
usb_PID uint16 = 0x000A
|
||||||
|
)
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
//go:build sam && atsamd21 && gemma_m0
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// Used to reset into bootloader.
|
||||||
|
const resetMagicValue = 0xf01669ef
|
||||||
|
|
||||||
|
// GPIO Pins.
|
||||||
|
const (
|
||||||
|
D0 = PA04 // SERCOM0/PAD[0]
|
||||||
|
D1 = PA02
|
||||||
|
D2 = PA05 // SERCOM0/PAD[1]
|
||||||
|
D3 = PA00 // DotStar LED: SERCOM1/PAD[0]: APA102/MOSI
|
||||||
|
D4 = PA01 // DotStar LED: SERCOM1/PAD[1]: APA102/SCK
|
||||||
|
D11 = PA30 // Flash Access: SERCOM1/PAD[2]
|
||||||
|
D12 = PA31 // Flash Access: SERCOM1/PAD[3]
|
||||||
|
D13 = PA23 // LED: SERCOM3/PAD[1] SERCOM5/PAD[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog pins.
|
||||||
|
const (
|
||||||
|
A0 = D1
|
||||||
|
A1 = D2
|
||||||
|
A2 = D0
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = PA23
|
||||||
|
)
|
||||||
|
|
||||||
|
// USBCDC pins.
|
||||||
|
const (
|
||||||
|
USBCDC_DM_PIN = PA24
|
||||||
|
USBCDC_DP_PIN = PA25
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0 pins.
|
||||||
|
const (
|
||||||
|
UART_TX_PIN = PA04 // TX: SERCOM0/PAD[0]
|
||||||
|
UART_RX_PIN = PA05 // RX: SERCOM0/PAD[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0s on the Gemma M0.
|
||||||
|
var UART0 = &sercomUSART0
|
||||||
|
|
||||||
|
// SPI pins.
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = PA05 // SCK: SERCOM0/PAD[1]
|
||||||
|
SPI0_SDO_PIN = PA04 // MOSI: SERCOM0/PAD[0]
|
||||||
|
SPI0_SDI_PIN = NoPin
|
||||||
|
SPI0_CS_PIN = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI on the Gemma M0.
|
||||||
|
var SPI0 = sercomSPIM0
|
||||||
|
|
||||||
|
// SPI pins for DotStar LED (using APA102 software SPI) and Flash.
|
||||||
|
const (
|
||||||
|
SPI1_SCK_PIN = PA01 // SCK: SERCOM1/PAD[0]
|
||||||
|
SPI1_SDO_PIN = PA00 // MOSI: SERCOM1/PAD[1]
|
||||||
|
SPI1_SDI_PIN = PA31 // MISO: SERCOM1/PAD[3]
|
||||||
|
SPI1_CS_PIN = PA30 // CS: SERCOM1/PAD[2]
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins.
|
||||||
|
const (
|
||||||
|
SDA_PIN = PA04 // SDA: SERCOM0/PAD[0]
|
||||||
|
SCL_PIN = PA05 // SCL: SERCOM0/PAD[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C on the Gemma M0.
|
||||||
|
var (
|
||||||
|
I2C0 = sercomI2CM0
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S (not connected, needed for atsamd21).
|
||||||
|
const (
|
||||||
|
I2S_SCK_PIN = NoPin
|
||||||
|
I2S_SD_PIN = NoPin
|
||||||
|
I2S_WS_PIN = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers.
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "Adafruit Gemma M0"
|
||||||
|
usb_STRING_MANUFACTURER = "Adafruit"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x239A
|
||||||
|
usb_PID uint16 = 0x801E
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
)
|
||||||
@@ -16,6 +16,7 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
SWITCH = GPIO0
|
SWITCH = GPIO0
|
||||||
|
BUTTON = GPIO0
|
||||||
|
|
||||||
KEY1 = GPIO1
|
KEY1 = GPIO1
|
||||||
KEY2 = GPIO2
|
KEY2 = GPIO2
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func (i2c *I2C) stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// writeByte writes a single byte to the I2C bus.
|
// writeByte writes a single byte to the I2C bus.
|
||||||
func (i2c *I2C) writeByte(data byte) {
|
func (i2c *I2C) writeByte(data byte) error {
|
||||||
// Write data to register.
|
// Write data to register.
|
||||||
avr.TWDR.Set(data)
|
avr.TWDR.Set(data)
|
||||||
|
|
||||||
@@ -107,6 +107,7 @@ func (i2c *I2C) writeByte(data byte) {
|
|||||||
// Wait till data is transmitted.
|
// Wait till data is transmitted.
|
||||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readByte reads a single byte from the I2C bus.
|
// readByte reads a single byte from the I2C bus.
|
||||||
@@ -190,7 +191,7 @@ func (uart *UART) handleInterrupt(intr interrupt.Interrupt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
func (uart *UART) WriteByte(c byte) error {
|
func (uart *UART) writeByte(c byte) error {
|
||||||
// Wait until UART buffer is not busy.
|
// Wait until UART buffer is not busy.
|
||||||
for !uart.statusRegA.HasBits(avr.UCSR0A_UDRE0) {
|
for !uart.statusRegA.HasBits(avr.UCSR0A_UDRE0) {
|
||||||
}
|
}
|
||||||
@@ -198,6 +199,8 @@ func (uart *UART) WriteByte(c byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (uart *UART) flush() {}
|
||||||
|
|
||||||
// SPIConfig is used to store config info for SPI.
|
// SPIConfig is used to store config info for SPI.
|
||||||
type SPIConfig struct {
|
type SPIConfig struct {
|
||||||
Frequency uint32
|
Frequency uint32
|
||||||
|
|||||||
@@ -466,3 +466,107 @@ var SPI0 = SPI{
|
|||||||
sdo: PB3,
|
sdo: PB3,
|
||||||
sdi: PB4,
|
sdi: PB4,
|
||||||
cs: PB2}
|
cs: PB2}
|
||||||
|
|
||||||
|
// Pin Change Interrupts
|
||||||
|
type PinChange uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
PinRising PinChange = 1 << iota
|
||||||
|
PinFalling
|
||||||
|
PinToggle = PinRising | PinFalling
|
||||||
|
)
|
||||||
|
|
||||||
|
func (pin Pin) SetInterrupt(pinChange PinChange, callback func(Pin)) (err error) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case pin >= PB0 && pin <= PB7:
|
||||||
|
// PCMSK0 - PCINT0-7
|
||||||
|
pinStates[0] = avr.PINB.Get()
|
||||||
|
pinIndex := pin - PB0
|
||||||
|
if pinChange&PinRising > 0 {
|
||||||
|
pinCallbacks[0][pinIndex][0] = callback
|
||||||
|
}
|
||||||
|
if pinChange&PinFalling > 0 {
|
||||||
|
pinCallbacks[0][pinIndex][1] = callback
|
||||||
|
}
|
||||||
|
if callback != nil {
|
||||||
|
avr.PCMSK0.SetBits(1 << pinIndex)
|
||||||
|
} else {
|
||||||
|
avr.PCMSK0.ClearBits(1 << pinIndex)
|
||||||
|
}
|
||||||
|
avr.PCICR.SetBits(avr.PCICR_PCIE0)
|
||||||
|
interrupt.New(avr.IRQ_PCINT0, handlePCINT0Interrupts)
|
||||||
|
case pin >= PC0 && pin <= PC7:
|
||||||
|
// PCMSK1 - PCINT8-14
|
||||||
|
pinStates[1] = avr.PINC.Get()
|
||||||
|
pinIndex := pin - PC0
|
||||||
|
if pinChange&PinRising > 0 {
|
||||||
|
pinCallbacks[1][pinIndex][0] = callback
|
||||||
|
}
|
||||||
|
if pinChange&PinFalling > 0 {
|
||||||
|
pinCallbacks[1][pinIndex][1] = callback
|
||||||
|
}
|
||||||
|
if callback != nil {
|
||||||
|
avr.PCMSK1.SetBits(1 << pinIndex)
|
||||||
|
} else {
|
||||||
|
avr.PCMSK1.ClearBits(1 << pinIndex)
|
||||||
|
}
|
||||||
|
avr.PCICR.SetBits(avr.PCICR_PCIE1)
|
||||||
|
interrupt.New(avr.IRQ_PCINT1, handlePCINT1Interrupts)
|
||||||
|
case pin >= PD0 && pin <= PD7:
|
||||||
|
// PCMSK2 - PCINT16-23
|
||||||
|
pinStates[2] = avr.PIND.Get()
|
||||||
|
pinIndex := pin - PD0
|
||||||
|
if pinChange&PinRising > 0 {
|
||||||
|
pinCallbacks[2][pinIndex][0] = callback
|
||||||
|
}
|
||||||
|
if pinChange&PinFalling > 0 {
|
||||||
|
pinCallbacks[2][pinIndex][1] = callback
|
||||||
|
}
|
||||||
|
if callback != nil {
|
||||||
|
avr.PCMSK2.SetBits(1 << pinIndex)
|
||||||
|
} else {
|
||||||
|
avr.PCMSK2.ClearBits(1 << pinIndex)
|
||||||
|
}
|
||||||
|
avr.PCICR.SetBits(avr.PCICR_PCIE2)
|
||||||
|
interrupt.New(avr.IRQ_PCINT2, handlePCINT2Interrupts)
|
||||||
|
default:
|
||||||
|
return ErrInvalidInputPin
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var pinCallbacks [3][8][2]func(Pin)
|
||||||
|
var pinStates [3]uint8
|
||||||
|
|
||||||
|
func handlePCINTInterrupts(intr uint8, port *volatile.Register8) {
|
||||||
|
current := port.Get()
|
||||||
|
change := pinStates[intr] ^ current
|
||||||
|
pinStates[intr] = current
|
||||||
|
for i := uint8(0); i < 8; i++ {
|
||||||
|
if (change>>i)&0x01 != 0x01 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pin := Pin(intr*8 + i)
|
||||||
|
value := pin.Get()
|
||||||
|
if value && pinCallbacks[intr][i][0] != nil {
|
||||||
|
pinCallbacks[intr][i][0](pin)
|
||||||
|
}
|
||||||
|
if !value && pinCallbacks[intr][i][1] != nil {
|
||||||
|
pinCallbacks[intr][i][1](pin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePCINT0Interrupts(intr interrupt.Interrupt) {
|
||||||
|
handlePCINTInterrupts(0, avr.PINB)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePCINT1Interrupts(intr interrupt.Interrupt) {
|
||||||
|
handlePCINTInterrupts(1, avr.PINC)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePCINT2Interrupts(intr interrupt.Interrupt) {
|
||||||
|
handlePCINTInterrupts(2, avr.PIND)
|
||||||
|
}
|
||||||
|
|||||||
@@ -626,7 +626,7 @@ func (uart *UART) SetBaudRate(br uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
func (uart *UART) WriteByte(c byte) error {
|
func (uart *UART) writeByte(c byte) error {
|
||||||
// wait until ready to receive
|
// wait until ready to receive
|
||||||
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INTFLAG_DRE) {
|
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INTFLAG_DRE) {
|
||||||
}
|
}
|
||||||
@@ -634,6 +634,8 @@ func (uart *UART) WriteByte(c byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (uart *UART) flush() {}
|
||||||
|
|
||||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||||
// this UART instance.
|
// this UART instance.
|
||||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||||
|
|||||||
@@ -225,7 +225,23 @@ func initEndpoint(ep, config uint32) {
|
|||||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||||
|
|
||||||
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
|
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
|
||||||
// TODO: not really anything, seems like...
|
// set packet size
|
||||||
|
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||||
|
|
||||||
|
// set data buffer address
|
||||||
|
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||||
|
|
||||||
|
// set endpoint type
|
||||||
|
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||||
|
|
||||||
|
// receive interrupts when current transfer complete
|
||||||
|
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||||
|
|
||||||
|
// set byte count to zero, we have not received anything yet
|
||||||
|
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
|
|
||||||
|
// ready for next transfer
|
||||||
|
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||||
|
|
||||||
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
|
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
|
||||||
// set packet size
|
// set packet size
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user