mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 03:53:42 +00:00
Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea2f0e444d | |||
| 04242fd620 | |||
| cce44b5ebb | |||
| 3767decf9e | |||
| 8d6e16019a | |||
| 3cc9c44c26 | |||
| c9bb33a73c | |||
| 8659b18d6f | |||
| 2599db2990 | |||
| 7417c14c2d | |||
| 4f39be9c3b | |||
| 7305a44f0c | |||
| 74effd2fa9 | |||
| 9e9768b51d | |||
| 127557d27e | |||
| fd89e9b83a | |||
| 5a1b885024 | |||
| b15adf23f8 | |||
| 29d2719bad | |||
| 194438cdd6 | |||
| 217f677bbe | |||
| 16b0f4cc9a | |||
| ff15b474fd | |||
| 3efc6340ad | |||
| 0426a5f902 | |||
| b898916d52 | |||
| 52983794d7 | |||
| 9d2f52805b | |||
| b18213805a | |||
| eeba90fd5b | |||
| 6507765883 | |||
| a98e35ebab | |||
| c4cfc01ba3 | |||
| f64e70e659 | |||
| 64fa7853a7 | |||
| 37f35f8c91 | |||
| 0d13e61d0c | |||
| 5701bf81f3 | |||
| 6110f0bc1b | |||
| 17302ca762 | |||
| f246599253 | |||
| ec3f387f21 | |||
| 31f7214156 | |||
| 6faf36fc64 | |||
| edb2f2a417 | |||
| 3eee686932 | |||
| 4aac3cd7b1 | |||
| 2588bf7fa0 | |||
| 72f564555e | |||
| b5a8931fb5 | |||
| d3810ecd48 | |||
| 3b8062170c | |||
| 26c36d0a2e | |||
| 09a22ac4b4 | |||
| aed555d858 | |||
| 65b085a5d5 | |||
| 392a709b77 | |||
| ee6fcd76f4 | |||
| ca80c52df1 | |||
| b76ea29520 | |||
| 9172cc15d2 | |||
| 1b83d43cfa | |||
| 7847f4ea8e | |||
| 19736e5be2 | |||
| 51504bfd2e | |||
| 79164dae71 | |||
| 8d04821639 | |||
| f75187392d | |||
| ecc6d16f22 | |||
| 5f252b3e16 | |||
| dd1ebbd31b | |||
| 95671469c7 | |||
| 6601c1b1a6 | |||
| 5ebda89d78 | |||
| b7a3fd8d2f | |||
| fd625f7265 | |||
| d1fe02df23 | |||
| 6593cf22fa | |||
| 548fba82e6 | |||
| 2d26b6cc8d | |||
| 0087d4c6bf | |||
| 289fceb3ea |
@@ -10,12 +10,12 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-18-v1
|
- llvm-source-19-v1
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-18-v1
|
key: llvm-source-19-v1
|
||||||
paths:
|
paths:
|
||||||
- llvm-project/clang/lib/Headers
|
- llvm-project/clang/lib/Headers
|
||||||
- llvm-project/clang/include
|
- llvm-project/clang/include
|
||||||
@@ -109,12 +109,12 @@ jobs:
|
|||||||
# "make lint" fails before go 1.21 because internal/tools/go.mod specifies packages that require go 1.21
|
# "make lint" fails before go 1.21 because internal/tools/go.mod specifies packages that require go 1.21
|
||||||
fmt-check: false
|
fmt-check: false
|
||||||
resource_class: large
|
resource_class: large
|
||||||
test-llvm18-go123:
|
test-llvm19-go123:
|
||||||
docker:
|
docker:
|
||||||
- image: golang:1.23-bullseye
|
- image: golang:1.23-bullseye
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "18"
|
llvm: "19"
|
||||||
resource_class: large
|
resource_class: large
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
@@ -123,5 +123,5 @@ workflows:
|
|||||||
# 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-llvm15-go119
|
- test-llvm15-go119
|
||||||
# This tests LLVM 18 support when linking against system libraries.
|
# This tests LLVM 19 support when linking against system libraries.
|
||||||
- test-llvm18-go123
|
- test-llvm19-go123
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ jobs:
|
|||||||
goarch: arm64
|
goarch: arm64
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: exit early
|
|
||||||
run: command-does-not-exist
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen
|
||||||
@@ -47,7 +45,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-18-${{ matrix.os }}-v2
|
key: llvm-source-19-${{ matrix.os }}-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -72,7 +70,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-${{ matrix.os }}-v3
|
key: llvm-build-19-${{ matrix.os }}-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -128,10 +126,8 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
version: [16, 17, 18]
|
version: [16, 17, 18, 19]
|
||||||
steps:
|
steps:
|
||||||
- name: exit early
|
|
||||||
run: command-does-not-exist
|
|
||||||
- name: Set up Homebrew
|
- name: Set up Homebrew
|
||||||
uses: Homebrew/actions/setup-homebrew@master
|
uses: Homebrew/actions/setup-homebrew@master
|
||||||
- name: Fix Python symlinks
|
- name: Fix Python symlinks
|
||||||
@@ -154,8 +150,8 @@ jobs:
|
|||||||
- name: Check binary
|
- name: Check binary
|
||||||
run: tinygo version
|
run: tinygo version
|
||||||
- name: Build TinyGo (default LLVM)
|
- name: Build TinyGo (default LLVM)
|
||||||
if: matrix.version == 18
|
if: matrix.version == 19
|
||||||
run: go install
|
run: go install
|
||||||
- name: Check binary
|
- name: Check binary
|
||||||
if: matrix.version == 18
|
if: matrix.version == 19
|
||||||
run: tinygo version
|
run: tinygo version
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.version.outputs.version }}
|
version: ${{ steps.version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: exit early
|
|
||||||
run: command-does-not-exist
|
|
||||||
- name: Install apk dependencies
|
- name: Install apk dependencies
|
||||||
# tar: needed for actions/cache@v4
|
# tar: needed for actions/cache@v4
|
||||||
# git+openssh: needed for checkout (I think?)
|
# git+openssh: needed for checkout (I think?)
|
||||||
@@ -50,7 +48,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-18-linux-alpine-v1
|
key: llvm-source-19-linux-alpine-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -75,7 +73,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-linux-alpine-v2
|
key: llvm-build-19-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'
|
||||||
@@ -171,10 +169,8 @@ jobs:
|
|||||||
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
|
||||||
# potential bugs.
|
# potential bugs.
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: exit early
|
|
||||||
run: command-does-not-exist
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -209,7 +205,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-18-linux-asserts-v1
|
key: llvm-source-19-linux-asserts-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -234,7 +230,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-linux-asserts-v2
|
key: llvm-build-19-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'
|
||||||
@@ -325,7 +321,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-18-linux-v1
|
key: llvm-source-19-linux-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -350,7 +346,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-linux-${{ matrix.goarch }}-v2
|
key: llvm-build-19-linux-${{ matrix.goarch }}-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'
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ jobs:
|
|||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
tinygo/llvm-18
|
tinygo/llvm-19
|
||||||
ghcr.io/${{ github.repository_owner }}/llvm-18
|
ghcr.io/${{ github.repository_owner }}/llvm-19
|
||||||
tags: |
|
tags: |
|
||||||
type=sha,format=long
|
type=sha,format=long
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ jobs:
|
|||||||
nix-test:
|
nix-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: exit early
|
|
||||||
run: command-does-not-exist
|
|
||||||
- name: Uninstall system LLVM
|
- name: Uninstall system LLVM
|
||||||
# Hack to work around issue where we still include system headers for
|
# Hack to work around issue where we still include system headers for
|
||||||
# some reason.
|
# some reason.
|
||||||
@@ -31,7 +29,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-18-linux-nix-v1
|
key: llvm-source-19-linux-nix-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/compiler-rt
|
llvm-project/compiler-rt
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
# still works after checking out the dev branch (that is, when going from LLVM
|
# still works after checking out the dev branch (that is, when going from LLVM
|
||||||
# 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed).
|
# 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed).
|
||||||
|
|
||||||
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-18 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-19 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-18-dev \
|
llvm-19-dev \
|
||||||
clang-18 \
|
clang-19 \
|
||||||
libclang-18-dev \
|
libclang-19-dev \
|
||||||
lld-18
|
lld-19
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: exit early
|
|
||||||
run: command-does-not-exist
|
|
||||||
# Prepare, install tools
|
# Prepare, install tools
|
||||||
- name: Add GOBIN to $PATH
|
- name: Add GOBIN to $PATH
|
||||||
run: |
|
run: |
|
||||||
@@ -32,7 +30,7 @@ jobs:
|
|||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-18-sizediff-v1
|
key: llvm-source-19-sizediff-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/compiler-rt
|
llvm-project/compiler-rt
|
||||||
- name: Download LLVM source
|
- name: Download LLVM source
|
||||||
|
|||||||
@@ -23,35 +23,31 @@ jobs:
|
|||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
disk-root: "C:"
|
disk-root: "C:"
|
||||||
#- uses: brechtm/setup-scoop@v2
|
- uses: brechtm/setup-scoop@v2
|
||||||
# with:
|
with:
|
||||||
# scoop_update: 'false'
|
scoop_update: 'false'
|
||||||
#- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
# shell: bash
|
shell: bash
|
||||||
# run: |
|
run: |
|
||||||
# scoop install ninja binaryen
|
scoop install ninja binaryen
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: submodules
|
with:
|
||||||
shell: bash
|
submodules: true
|
||||||
run: git submodule update --init lib/mingw-w64
|
|
||||||
- name: Extract TinyGo version
|
- name: Extract TinyGo version
|
||||||
id: version
|
id: version
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||||
- name: command
|
- name: Install Go
|
||||||
shell: bash
|
uses: actions/setup-go@v5
|
||||||
run: go env
|
with:
|
||||||
#- name: Install Go
|
go-version: '1.23'
|
||||||
# uses: actions/setup-go@v5
|
cache: true
|
||||||
# with:
|
|
||||||
# go-version: '1.23'
|
|
||||||
# cache: true
|
|
||||||
- name: Restore cached LLVM source
|
- name: Restore cached LLVM source
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-source
|
id: cache-llvm-source
|
||||||
with:
|
with:
|
||||||
key: llvm-source-18-windows-v1
|
key: llvm-source-19-windows-v1
|
||||||
path: |
|
path: |
|
||||||
llvm-project/clang/lib/Headers
|
llvm-project/clang/lib/Headers
|
||||||
llvm-project/clang/include
|
llvm-project/clang/include
|
||||||
@@ -76,7 +72,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-windows-v2
|
key: llvm-build-19-windows-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -95,25 +91,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Restore Go cache
|
|
||||||
uses: actions/cache/restore@v4
|
|
||||||
with:
|
|
||||||
key: go-cache-v2
|
|
||||||
path: |
|
|
||||||
C:/Users/runneradmin/AppData/Local/go-build
|
|
||||||
C:/Users/runneradmin/go/pkg/mod
|
|
||||||
- name: Test TinyGo
|
|
||||||
shell: bash
|
|
||||||
run: make test GOTESTFLAGS="-short -run=TestBuild -v"
|
|
||||||
- name: Save Go cache
|
|
||||||
uses: actions/cache/save@v4
|
|
||||||
with:
|
|
||||||
key: go-cache-v2
|
|
||||||
path: |
|
|
||||||
C:/Users/runneradmin/AppData/Local/go-build
|
|
||||||
C:/Users/runneradmin/go/pkg/mod
|
|
||||||
- name: exit
|
|
||||||
run: command-does-not-exist
|
|
||||||
- name: Cache wasi-libc sysroot
|
- name: Cache wasi-libc sysroot
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
id: cache-wasi-libc
|
id: cache-wasi-libc
|
||||||
@@ -123,11 +100,21 @@ 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
|
||||||
|
- name: Cache Go cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
|
||||||
|
path: |
|
||||||
|
C:/Users/runneradmin/AppData/Local/go-build
|
||||||
|
C:/Users/runneradmin/go/pkg/mod
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
run: |
|
run: |
|
||||||
scoop install wasmtime@14.0.4
|
scoop install wasmtime@14.0.4
|
||||||
- name: make gen-device
|
- name: make gen-device
|
||||||
run: make -j3 gen-device
|
run: make -j3 gen-device
|
||||||
|
- name: Test TinyGo
|
||||||
|
shell: bash
|
||||||
|
run: make test GOTESTFLAGS="-short"
|
||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make build/release -j4
|
run: make build/release -j4
|
||||||
|
|||||||
@@ -37,5 +37,9 @@ test.exe
|
|||||||
test.gba
|
test.gba
|
||||||
test.hex
|
test.hex
|
||||||
test.nro
|
test.nro
|
||||||
|
test.uf2
|
||||||
test.wasm
|
test.wasm
|
||||||
wasm.wasm
|
wasm.wasm
|
||||||
|
|
||||||
|
*.uf2
|
||||||
|
*.elf
|
||||||
@@ -1,3 +1,71 @@
|
|||||||
|
0.35.0
|
||||||
|
---
|
||||||
|
* **general**
|
||||||
|
- update cmsis-svd library
|
||||||
|
- use default UART settings in the echo example
|
||||||
|
- `goenv`: also show git hash with custom build of TinyGo
|
||||||
|
- `goenv`: support parsing development versions of Go
|
||||||
|
- `main`: parse extldflags early so we can report the error message
|
||||||
|
* **compiler**
|
||||||
|
- `builder`: whitelist temporary directory env var for Clang invocation to fix Windows bug
|
||||||
|
- `builder`: fix cache paths in `-size=full` output
|
||||||
|
- `builder`: work around incorrectly escaped DWARF paths on Windows (Clang bug)
|
||||||
|
- `builder`: fix wasi-libc path names on Windows with `-size=full`
|
||||||
|
- `builder`: write HTML size report
|
||||||
|
- `cgo`: support C identifiers only referred to from within macros
|
||||||
|
- `cgo`: support function-like macros
|
||||||
|
- `cgo`: support errno value as second return parameter
|
||||||
|
- `cgo`: add support for `#cgo noescape` lines
|
||||||
|
- `compiler`: fix bug in interrupt lowering
|
||||||
|
- `compiler`: allow panic directly in `defer`
|
||||||
|
- `compiler`: fix wasmimport -> wasmexport in error message
|
||||||
|
- `compiler`: support `//go:noescape` pragma
|
||||||
|
- `compiler`: report error instead of crashing when instantiating a generic function without body
|
||||||
|
- `interp`: align created globals
|
||||||
|
* **standard library**
|
||||||
|
- `machine`: modify i2s interface/implementation to better match specification
|
||||||
|
- `os`: implement `StartProcess`
|
||||||
|
- `reflect`: add `Value.Clear`
|
||||||
|
- `reflect`: add interface support to `NumMethods`
|
||||||
|
- `reflect`: fix `AssignableTo` for named + non-named types
|
||||||
|
- `reflect`: implement `CanConvert`
|
||||||
|
- `reflect`: handle more cases in `Convert`
|
||||||
|
- `reflect`: fix Copy of non-pointer array with size > 64bits
|
||||||
|
- `runtime`: don't call sleepTicks with a negative duration
|
||||||
|
- `runtime`: optimize GC scanning (findHead)
|
||||||
|
- `runtime`: move constants into shared package
|
||||||
|
- `runtime`: add `runtime.fcntl` function for internal/syscall/unix
|
||||||
|
- `runtime`: heapptr only needs to be initialized once
|
||||||
|
- `runtime`: refactor scheduler (this fixes a few bugs with `-scheduler=none`)
|
||||||
|
- `runtime`: rewrite channel implementation to be smaller and more flexible
|
||||||
|
- `runtime`: use `SA_RESTART` when registering a signal for os/signal
|
||||||
|
- `runtime`: implement race-free signals using futexes
|
||||||
|
- `runtime`: run deferred functions in `Goexit`
|
||||||
|
- `runtime`: remove `Cond` which seems to be unused
|
||||||
|
- `runtime`: properly handle unix read on directory
|
||||||
|
- `runtime/trace`: stub all public methods
|
||||||
|
- `sync`: don't use volatile in `Mutex`
|
||||||
|
- `sync`: implement `WaitGroup` using a (pseudo)futex
|
||||||
|
- `sync`: make `Cond` parallelism-safe
|
||||||
|
- `syscall`: use wasi-libc tables for wasm/js target
|
||||||
|
* **targets**
|
||||||
|
- `mips`: fix a bug when scanning the stack
|
||||||
|
- `nintendoswitch`: get this target to compile again
|
||||||
|
- `rp2350`: add support for the new RP2350
|
||||||
|
- `rp2040/rp2350` : make I2C implementation shared for rp2040/rp2350
|
||||||
|
- `rp2040/rp2350` : make SPI implementation shared for rp2040/rp2350
|
||||||
|
- `rp2040/rp2350` : make RNG implementation shared for rp2040/rp2350
|
||||||
|
- `wasm`: revise and simplify wasmtime argument handling
|
||||||
|
- `wasm`: support `//go:wasmexport` functions after a call to `time.Sleep`
|
||||||
|
- `wasm`: correctly return from run() in wasm_exec.js
|
||||||
|
- `wasm`: call process.exit() when go.run() returns
|
||||||
|
- `windows`: don't return, exit via exit(0) instead to flush stdout buffer
|
||||||
|
* **boards**
|
||||||
|
- add support for the Tillitis TKey
|
||||||
|
- add support for the Raspberry Pi Pico2 (based on the RP2040)
|
||||||
|
- add support for Pimoroni Tiny2350
|
||||||
|
|
||||||
|
|
||||||
0.34.0
|
0.34.0
|
||||||
---
|
---
|
||||||
* **general**
|
* **general**
|
||||||
|
|||||||
+23
-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 = 18 17 16 15
|
LLVM_VERSIONS = 19 18 17 16 15
|
||||||
errifempty = $(if $(1),$(1),$(error $(2)))
|
errifempty = $(if $(1),$(1),$(error $(2)))
|
||||||
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
||||||
toolSearchPathsVersion = $(1)-$(2)
|
toolSearchPathsVersion = $(1)-$(2)
|
||||||
@@ -147,7 +147,7 @@ endif
|
|||||||
MD5SUM ?= md5sum
|
MD5SUM ?= md5sum
|
||||||
|
|
||||||
# Libraries that should be linked in for the statically linked Clang.
|
# Libraries that should be linked in for the statically linked Clang.
|
||||||
CLANG_LIB_NAMES = clangAnalysis clangAPINotes clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
CLANG_LIB_NAMES = clangAnalysis clangAPINotes clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangInstallAPI clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||||
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||||
|
|
||||||
# Libraries that should be linked in for the statically linked LLD.
|
# Libraries that should be linked in for the statically linked LLD.
|
||||||
@@ -191,7 +191,7 @@ gen-device: gen-device-stm32
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
gen-device-avr:
|
gen-device-avr:
|
||||||
#@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||||
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||||
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||||
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||||
@@ -238,7 +238,7 @@ gen-device-renesas: build/gen-device-svd
|
|||||||
GO111MODULE=off $(GO) fmt ./src/device/renesas
|
GO111MODULE=off $(GO) fmt ./src/device/renesas
|
||||||
|
|
||||||
$(LLVM_PROJECTDIR)/llvm:
|
$(LLVM_PROJECTDIR)/llvm:
|
||||||
git clone -b tinygo_xtensa_release_18.1.2 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
git clone -b xtensa_release_19.1.2 --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
|
||||||
llvm-source: $(LLVM_PROJECTDIR)/llvm ## Get LLVM sources
|
llvm-source: $(LLVM_PROJECTDIR)/llvm ## Get LLVM sources
|
||||||
|
|
||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
@@ -263,7 +263,7 @@ endif
|
|||||||
.PHONY: wasi-libc
|
.PHONY: wasi-libc
|
||||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||||
#@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; 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)
|
||||||
|
|
||||||
# Generate WASI syscall bindings
|
# Generate WASI syscall bindings
|
||||||
@@ -292,8 +292,8 @@ endif
|
|||||||
tinygo: ## Build the TinyGo compiler
|
tinygo: ## Build the TinyGo compiler
|
||||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
|
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" .
|
||||||
test: #wasi-libc check-nodejs-version
|
test: wasi-libc check-nodejs-version
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -race -buildmode exe -tags "byollvm osusergo" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS)
|
||||||
|
|
||||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||||
TEST_PACKAGES_SLOW = \
|
TEST_PACKAGES_SLOW = \
|
||||||
@@ -741,6 +741,12 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=thumby examples/echo
|
$(TINYGO) build -size short -o test.hex -target=thumby examples/echo
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pico2 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=tiny2350 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=waveshare-rp2040-tiny 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
|
||||||
@@ -822,6 +828,8 @@ endif
|
|||||||
ifneq ($(XTENSA), 0)
|
ifneq ($(XTENSA), 0)
|
||||||
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
|
$(TINYGO) build -size short -o test.bin -target=esp32c3-supermini examples/blinky1
|
||||||
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/machinetest
|
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/machinetest
|
||||||
@@ -853,6 +861,12 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=tkey examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=elecrow-rp2040 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=elecrow-rp2350 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
ifneq ($(WASM), 0)
|
ifneq ($(WASM), 0)
|
||||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
|
||||||
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
|
||||||
@@ -867,7 +881,7 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=rtt examples/echo
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=rtt examples/echo
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
$(TINYGO) build -o test.nro -target=nintendoswitch examples/echo2
|
||||||
@$(MD5SUM) test.nro
|
@$(MD5SUM) test.nro
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -935,6 +949,7 @@ endif
|
|||||||
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/musl/src/misc build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/multibyte build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/multibyte build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
|
||||||
|
|||||||
+54
-10
@@ -61,6 +61,10 @@ type BuildResult struct {
|
|||||||
// correctly printing test results: the import path isn't always the same as
|
// correctly printing test results: the import path isn't always the same as
|
||||||
// the path listed on the command line.
|
// the path listed on the command line.
|
||||||
ImportPath string
|
ImportPath string
|
||||||
|
|
||||||
|
// Map from path to package name. It is needed to attribute binary size to
|
||||||
|
// the right Go package.
|
||||||
|
PackagePathMap map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// packageAction is the struct that is serialized to JSON and hashed, to work as
|
// packageAction is the struct that is serialized to JSON and hashed, to work as
|
||||||
@@ -242,6 +246,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store which filesystem paths map to which package name.
|
||||||
|
result.PackagePathMap = make(map[string]string, len(lprogram.Packages))
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
||||||
|
}
|
||||||
|
|
||||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||||
// program so it's pretty fast and doesn't need to be parallelized.
|
// program so it's pretty fast and doesn't need to be parallelized.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
@@ -690,7 +700,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkg := pkg
|
pkg := pkg
|
||||||
for _, filename := range pkg.CFiles {
|
for _, filename := range pkg.CFiles {
|
||||||
abspath := filepath.Join(pkg.Dir, filename)
|
abspath := filepath.Join(pkg.OriginalDir(), filename)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile CGo file " + abspath,
|
description: "compile CGo file " + abspath,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
@@ -812,6 +822,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
return fmt.Errorf("could not modify stack sizes: %w", err)
|
return fmt.Errorf("could not modify stack sizes: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply patches of bootloader in the order they appear.
|
||||||
|
if len(config.Target.BootPatches) > 0 {
|
||||||
|
err = applyPatches(result.Executable, config.Target.BootPatches)
|
||||||
|
}
|
||||||
|
|
||||||
if config.RP2040BootPatch() {
|
if config.RP2040BootPatch() {
|
||||||
// Patch the second stage bootloader CRC into the .boot2 section
|
// Patch the second stage bootloader CRC into the .boot2 section
|
||||||
err = patchRP2040BootCRC(result.Executable)
|
err = patchRP2040BootCRC(result.Executable)
|
||||||
@@ -915,19 +931,16 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Print code size if requested.
|
// Print code size if requested.
|
||||||
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
if config.Options.PrintSizes != "" {
|
||||||
packagePathMap := make(map[string]string, len(lprogram.Packages))
|
sizes, err := loadProgramSize(result.Executable, result.PackagePathMap)
|
||||||
for _, pkg := range lprogram.Sorted() {
|
|
||||||
packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
|
||||||
}
|
|
||||||
sizes, err := loadProgramSize(result.Executable, packagePathMap)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if config.Options.PrintSizes == "short" {
|
switch config.Options.PrintSizes {
|
||||||
|
case "short":
|
||||||
fmt.Printf(" code data bss | flash ram\n")
|
fmt.Printf(" code data bss | flash ram\n")
|
||||||
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code+sizes.ROData, sizes.Data, sizes.BSS, sizes.Flash(), sizes.RAM())
|
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code+sizes.ROData, sizes.Data, sizes.BSS, sizes.Flash(), sizes.RAM())
|
||||||
} else {
|
case "full":
|
||||||
if !config.Debug() {
|
if !config.Debug() {
|
||||||
fmt.Println("warning: data incomplete, remove the -no-debug flag for more detail")
|
fmt.Println("warning: data incomplete, remove the -no-debug flag for more detail")
|
||||||
}
|
}
|
||||||
@@ -939,6 +952,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
fmt.Printf("------------------------------- | --------------- | -------\n")
|
fmt.Printf("------------------------------- | --------------- | -------\n")
|
||||||
fmt.Printf("%7d %7d %7d %7d | %7d %7d | total\n", sizes.Code, sizes.ROData, sizes.Data, sizes.BSS, sizes.Code+sizes.ROData+sizes.Data, sizes.Data+sizes.BSS)
|
fmt.Printf("%7d %7d %7d %7d | %7d %7d | total\n", sizes.Code, sizes.ROData, sizes.Data, sizes.BSS, sizes.Code+sizes.ROData+sizes.Data, sizes.Data+sizes.BSS)
|
||||||
|
case "html":
|
||||||
|
const filename = "size-report.html"
|
||||||
|
err := writeSizeReport(sizes, filename, pkgName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Wrote size report to", filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1428,6 +1448,23 @@ func printStacks(calculatedStacks []string, stackSizes map[string]functionStackS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyPatches(executable string, bootPatches []string) (err error) {
|
||||||
|
for _, patch := range bootPatches {
|
||||||
|
switch patch {
|
||||||
|
case "rp2040":
|
||||||
|
err = patchRP2040BootCRC(executable)
|
||||||
|
// case "rp2350":
|
||||||
|
// err = patchRP2350BootIMAGE_DEF(executable)
|
||||||
|
default:
|
||||||
|
err = errors.New("undefined boot patch name")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("apply boot patch %q: %w", patch, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RP2040 second stage bootloader CRC32 calculation
|
// RP2040 second stage bootloader CRC32 calculation
|
||||||
//
|
//
|
||||||
// Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
|
// Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
|
||||||
@@ -1439,7 +1476,7 @@ func patchRP2040BootCRC(executable string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(bytes) != 256 {
|
if len(bytes) != 256 {
|
||||||
return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes")
|
return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes, got %d", len(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// From the 'official' RP2040 checksum script:
|
// From the 'official' RP2040 checksum script:
|
||||||
@@ -1478,3 +1515,10 @@ func lock(path string) func() {
|
|||||||
|
|
||||||
return func() { flock.Close() }
|
return func() { flock.Close() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func b2u8(b bool) uint8 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
"k210",
|
"k210",
|
||||||
"nintendoswitch",
|
"nintendoswitch",
|
||||||
"riscv-qemu",
|
"riscv-qemu",
|
||||||
|
"tkey",
|
||||||
"wasip1",
|
"wasip1",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasm",
|
"wasm",
|
||||||
|
|||||||
+14
-14
@@ -145,6 +145,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
|
Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
|
||||||
|
Opts.SSE2AVX = Args.hasArg(OPT_msse2avx);
|
||||||
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);
|
||||||
@@ -234,6 +235,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
Opts.EmitCompactUnwindNonCanonical =
|
Opts.EmitCompactUnwindNonCanonical =
|
||||||
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
|
Args.hasArg(OPT_femit_compact_unwind_non_canonical);
|
||||||
|
Opts.Crel = Args.hasArg(OPT_crel);
|
||||||
|
|
||||||
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
|
||||||
|
|
||||||
@@ -287,8 +289,14 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
assert(MRI && "Unable to create target register info!");
|
assert(MRI && "Unable to create target register info!");
|
||||||
|
|
||||||
MCTargetOptions MCOptions;
|
MCTargetOptions MCOptions;
|
||||||
|
MCOptions.MCRelaxAll = Opts.RelaxAll;
|
||||||
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
|
||||||
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
|
MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
|
||||||
|
MCOptions.MCSaveTempLabels = Opts.SaveTemporaryLabels;
|
||||||
|
MCOptions.Crel = Opts.Crel;
|
||||||
|
MCOptions.X86RelaxRelocations = Opts.RelaxELFRelocations;
|
||||||
|
MCOptions.X86Sse2Avx = Opts.SSE2AVX;
|
||||||
|
MCOptions.CompressDebugSections = Opts.CompressDebugSections;
|
||||||
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
|
||||||
|
|
||||||
std::unique_ptr<MCAsmInfo> MAI(
|
std::unique_ptr<MCAsmInfo> MAI(
|
||||||
@@ -297,9 +305,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections
|
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections
|
||||||
// may be created with a combination of default and explicit settings.
|
// may be created with a combination of default and explicit settings.
|
||||||
MAI->setCompressDebugSections(Opts.CompressDebugSections);
|
|
||||||
|
|
||||||
MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
|
|
||||||
|
|
||||||
bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
|
bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
|
||||||
if (Opts.OutputPath.empty())
|
if (Opts.OutputPath.empty())
|
||||||
@@ -343,8 +349,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
|
MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
|
||||||
Ctx.setObjectFileInfo(MOFI.get());
|
Ctx.setObjectFileInfo(MOFI.get());
|
||||||
|
|
||||||
if (Opts.SaveTemporaryLabels)
|
|
||||||
Ctx.setAllowTemporaryLabels(false);
|
|
||||||
if (Opts.GenDwarfForAssembly)
|
if (Opts.GenDwarfForAssembly)
|
||||||
Ctx.setGenDwarfForAssembly(true);
|
Ctx.setGenDwarfForAssembly(true);
|
||||||
if (!Opts.DwarfDebugFlags.empty())
|
if (!Opts.DwarfDebugFlags.empty())
|
||||||
@@ -381,6 +385,9 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
MCOptions.MCNoWarn = Opts.NoWarn;
|
MCOptions.MCNoWarn = Opts.NoWarn;
|
||||||
MCOptions.MCFatalWarnings = Opts.FatalWarnings;
|
MCOptions.MCFatalWarnings = Opts.FatalWarnings;
|
||||||
MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
|
MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
|
||||||
|
MCOptions.ShowMCInst = Opts.ShowInst;
|
||||||
|
MCOptions.AsmVerbose = true;
|
||||||
|
MCOptions.MCUseDwarfDirectory = MCTargetOptions::EnableDwarfDirectory;
|
||||||
MCOptions.ABIName = Opts.TargetABI;
|
MCOptions.ABIName = Opts.TargetABI;
|
||||||
|
|
||||||
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||||
@@ -395,10 +402,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||||
|
|
||||||
auto FOut = std::make_unique<formatted_raw_ostream>(*Out);
|
auto FOut = std::make_unique<formatted_raw_ostream>(*Out);
|
||||||
Str.reset(TheTarget->createAsmStreamer(
|
Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), IP,
|
||||||
Ctx, std::move(FOut), /*asmverbose*/ true,
|
std::move(CE), std::move(MAB)));
|
||||||
/*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
|
|
||||||
Opts.ShowInst));
|
|
||||||
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
||||||
Str.reset(createNullStreamer(Ctx));
|
Str.reset(createNullStreamer(Ctx));
|
||||||
} else {
|
} else {
|
||||||
@@ -421,9 +426,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
|
|
||||||
Triple T(Opts.Triple);
|
Triple T(Opts.Triple);
|
||||||
Str.reset(TheTarget->createMCObjectStreamer(
|
Str.reset(TheTarget->createMCObjectStreamer(
|
||||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI));
|
||||||
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
|
||||||
/*DWARFMustBeAtTheEnd*/ true));
|
|
||||||
Str.get()->initSections(Opts.NoExecStack, *STI);
|
Str.get()->initSections(Opts.NoExecStack, *STI);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,9 +439,6 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
|||||||
Str.get()->emitZeros(1);
|
Str.get()->emitZeros(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assembly to object compilation should leverage assembly info.
|
|
||||||
Str->setUseAssemblerInfoForParsing(true);
|
|
||||||
|
|
||||||
bool Failed = false;
|
bool Failed = false;
|
||||||
|
|
||||||
std::unique_ptr<MCAsmParser> Parser(
|
std::unique_ptr<MCAsmParser> Parser(
|
||||||
|
|||||||
+25
-1
@@ -38,10 +38,17 @@ struct AssemblerInvocation {
|
|||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
std::vector<std::string> IncludePaths;
|
std::vector<std::string> IncludePaths;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned NoInitialTextSection : 1;
|
unsigned NoInitialTextSection : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned SaveTemporaryLabels : 1;
|
unsigned SaveTemporaryLabels : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned GenDwarfForAssembly : 1;
|
unsigned GenDwarfForAssembly : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned RelaxELFRelocations : 1;
|
unsigned RelaxELFRelocations : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
|
unsigned SSE2AVX : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned Dwarf64 : 1;
|
unsigned Dwarf64 : 1;
|
||||||
unsigned DwarfVersion;
|
unsigned DwarfVersion;
|
||||||
std::string DwarfDebugFlags;
|
std::string DwarfDebugFlags;
|
||||||
@@ -66,7 +73,9 @@ struct AssemblerInvocation {
|
|||||||
FT_Obj ///< Object file output.
|
FT_Obj ///< Object file output.
|
||||||
};
|
};
|
||||||
FileType OutputType;
|
FileType OutputType;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned ShowHelp : 1;
|
unsigned ShowHelp : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned ShowVersion : 1;
|
unsigned ShowVersion : 1;
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
@@ -74,28 +83,41 @@ struct AssemblerInvocation {
|
|||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
unsigned OutputAsmVariant;
|
unsigned OutputAsmVariant;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned ShowEncoding : 1;
|
unsigned ShowEncoding : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned ShowInst : 1;
|
unsigned ShowInst : 1;
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Assembler Options
|
/// @name Assembler Options
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned RelaxAll : 1;
|
unsigned RelaxAll : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned NoExecStack : 1;
|
unsigned NoExecStack : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned FatalWarnings : 1;
|
unsigned FatalWarnings : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned NoWarn : 1;
|
unsigned NoWarn : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned NoTypeCheck : 1;
|
unsigned NoTypeCheck : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned IncrementalLinkerCompatible : 1;
|
unsigned IncrementalLinkerCompatible : 1;
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned EmbedBitcode : 1;
|
unsigned EmbedBitcode : 1;
|
||||||
|
|
||||||
/// Whether to emit DWARF unwind info.
|
/// Whether to emit DWARF unwind info.
|
||||||
EmitDwarfUnwindType EmitDwarfUnwind;
|
EmitDwarfUnwindType EmitDwarfUnwind;
|
||||||
|
|
||||||
// Whether to emit compact-unwind for non-canonical entries.
|
// Whether to emit compact-unwind for non-canonical entries.
|
||||||
// Note: maybe overridden by other constraints.
|
// Note: maybe overriden by other constraints.
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
unsigned EmitCompactUnwindNonCanonical : 1;
|
unsigned EmitCompactUnwindNonCanonical : 1;
|
||||||
|
|
||||||
|
LLVM_PREFERRED_TYPE(bool)
|
||||||
|
unsigned Crel : 1;
|
||||||
|
|
||||||
/// The name of the relocation model to use.
|
/// The name of the relocation model to use.
|
||||||
std::string RelocationModel;
|
std::string RelocationModel;
|
||||||
|
|
||||||
@@ -126,6 +148,7 @@ public:
|
|||||||
ShowInst = 0;
|
ShowInst = 0;
|
||||||
ShowEncoding = 0;
|
ShowEncoding = 0;
|
||||||
RelaxAll = 0;
|
RelaxAll = 0;
|
||||||
|
SSE2AVX = 0;
|
||||||
NoExecStack = 0;
|
NoExecStack = 0;
|
||||||
FatalWarnings = 0;
|
FatalWarnings = 0;
|
||||||
NoWarn = 0;
|
NoWarn = 0;
|
||||||
@@ -136,6 +159,7 @@ public:
|
|||||||
EmbedBitcode = 0;
|
EmbedBitcode = 0;
|
||||||
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
EmitDwarfUnwind = EmitDwarfUnwindType::Default;
|
||||||
EmitCompactUnwindNonCanonical = false;
|
EmitCompactUnwindNonCanonical = false;
|
||||||
|
Crel = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ var libMusl = Library{
|
|||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string) ([]string, error) {
|
||||||
arch := compileopts.MuslArchitecture(target)
|
arch := compileopts.MuslArchitecture(target)
|
||||||
globs := []string{
|
globs := []string{
|
||||||
|
"conf/*.c",
|
||||||
"env/*.c",
|
"env/*.c",
|
||||||
"errno/*.c",
|
"errno/*.c",
|
||||||
"exit/*.c",
|
"exit/*.c",
|
||||||
@@ -128,6 +129,7 @@ var libMusl = Library{
|
|||||||
"malloc/mallocng/*.c",
|
"malloc/mallocng/*.c",
|
||||||
"mman/*.c",
|
"mman/*.c",
|
||||||
"math/*.c",
|
"math/*.c",
|
||||||
|
"misc/*.c",
|
||||||
"multibyte/*.c",
|
"multibyte/*.c",
|
||||||
"signal/" + arch + "/*.s",
|
"signal/" + arch + "/*.s",
|
||||||
"signal/*.c",
|
"signal/*.c",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ var libPicolibc = Library{
|
|||||||
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
|
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
|
||||||
"-D__OBSOLETE_MATH_DOUBLE=0",
|
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||||
"-D_WANT_IO_C99_FORMATS",
|
"-D_WANT_IO_C99_FORMATS",
|
||||||
|
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", newlibDir + "/libc/include",
|
"-isystem", newlibDir + "/libc/include",
|
||||||
"-I" + newlibDir + "/libc/tinystdio",
|
"-I" + newlibDir + "/libc/tinystdio",
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed size-report.html
|
||||||
|
var sizeReportBase string
|
||||||
|
|
||||||
|
func writeSizeReport(sizes *programSize, filename, pkgName string) error {
|
||||||
|
tmpl, err := template.New("report").Parse(sizeReportBase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not open report file: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Prepare data for the report.
|
||||||
|
type sizeLine struct {
|
||||||
|
Name string
|
||||||
|
Size *packageSize
|
||||||
|
}
|
||||||
|
programData := []sizeLine{}
|
||||||
|
for _, name := range sizes.sortedPackageNames() {
|
||||||
|
pkgSize := sizes.Packages[name]
|
||||||
|
programData = append(programData, sizeLine{
|
||||||
|
Name: name,
|
||||||
|
Size: pkgSize,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sizeTotal := map[string]uint64{
|
||||||
|
"code": sizes.Code,
|
||||||
|
"rodata": sizes.ROData,
|
||||||
|
"data": sizes.Data,
|
||||||
|
"bss": sizes.BSS,
|
||||||
|
"flash": sizes.Flash(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the report.
|
||||||
|
err = tmpl.Execute(f, map[string]any{
|
||||||
|
"pkgName": pkgName,
|
||||||
|
"sizes": programData,
|
||||||
|
"sizeTotal": sizeTotal,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not create report file: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Size Report for {{.pkgName}}</title>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.table-vertical-border {
|
||||||
|
border-left: calc(var(--bs-border-width) * 2) solid currentcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover on only the rows that are clickable. */
|
||||||
|
.row-package:hover > * {
|
||||||
|
--bs-table-color-state: var(--bs-table-hover-color);
|
||||||
|
--bs-table-bg-state: var(--bs-table-hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container-xxl">
|
||||||
|
<h1>Size Report for {{.pkgName}}</h1>
|
||||||
|
|
||||||
|
<p>How much space is used by Go packages, C libraries, and other bits to set up the program environment.</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><strong>Code</strong> is the actual program code (machine code instructions).</li>
|
||||||
|
<li><strong>Read-only data</strong> are read-only global variables. On most microcontrollers, these are stored in flash and do not take up any RAM.</li>
|
||||||
|
<li><strong>Data</strong> are writable global variables with a non-zero initializer. On microcontrollers, they are copied from flash to RAM on reset.</li>
|
||||||
|
<li><strong>BSS</strong> are writable global variables that are zero initialized. They do not take up any space in the binary, but do take up RAM. On microcontrollers, this area is zeroed on reset.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>The binary size consists of code, read-only data, and data. On microcontrollers, this is exactly the size of the firmware image. On other systems, there is some extra overhead: binary metadata (headers of the ELF/MachO/COFF file), debug information, exception tables, symbol names, etc. Using <code>-no-debug</code> strips most of those.</p>
|
||||||
|
|
||||||
|
<h2>Program breakdown</h2>
|
||||||
|
|
||||||
|
<p>You can click on the rows below to see which files contribute to the binary size.</p>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table w-auto">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Package</th>
|
||||||
|
<th class="table-vertical-border">Code</th>
|
||||||
|
<th>Read-only data</th>
|
||||||
|
<th>Data</th>
|
||||||
|
<th title="zero-initialized data">BSS</th>
|
||||||
|
<th class="table-vertical-border" style="min-width: 16em">Binary size</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="table-group-divider">
|
||||||
|
{{range $i, $pkg := .sizes}}
|
||||||
|
<tr class="row-package" data-collapse=".collapse-row-{{$i}}">
|
||||||
|
<td>{{.Name}}</td>
|
||||||
|
<td class="table-vertical-border">{{.Size.Code}}</td>
|
||||||
|
<td>{{.Size.ROData}}</td>
|
||||||
|
<td>{{.Size.Data}}</td>
|
||||||
|
<td>{{.Size.BSS}}</td>
|
||||||
|
<td class="table-vertical-border" style="background: linear-gradient(to right, var(--bs-info-bg-subtle) {{.Size.FlashPercent}}%, var(--bs-table-bg) {{.Size.FlashPercent}}%)">
|
||||||
|
{{.Size.Flash}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{range $filename, $sizes := .Size.Sub}}
|
||||||
|
<tr class="table-secondary collapse collapse-row-{{$i}}">
|
||||||
|
<td class="ps-4">
|
||||||
|
{{if eq $filename ""}}
|
||||||
|
(unknown file)
|
||||||
|
{{else}}
|
||||||
|
{{$filename}}
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
<td class="table-vertical-border">{{$sizes.Code}}</td>
|
||||||
|
<td>{{$sizes.ROData}}</td>
|
||||||
|
<td>{{$sizes.Data}}</td>
|
||||||
|
<td>{{$sizes.BSS}}</td>
|
||||||
|
<td class="table-vertical-border" style="background: linear-gradient(to right, var(--bs-info-bg-subtle) {{$sizes.FlashPercent}}%, var(--bs-table-bg) {{$sizes.FlashPercent}}%)">
|
||||||
|
{{$sizes.Flash}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
<tfoot class="table-group-divider">
|
||||||
|
<tr>
|
||||||
|
<th>Total</th>
|
||||||
|
<td class="table-vertical-border">{{.sizeTotal.code}}</td>
|
||||||
|
<td>{{.sizeTotal.rodata}}</td>
|
||||||
|
<td>{{.sizeTotal.data}}</td>
|
||||||
|
<td>{{.sizeTotal.bss}}</td>
|
||||||
|
<td class="table-vertical-border">{{.sizeTotal.flash}}</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
// Make table rows toggleable to show filenames.
|
||||||
|
for (let clickable of document.querySelectorAll('.row-package')) {
|
||||||
|
clickable.addEventListener('click', e => {
|
||||||
|
for (let row of document.querySelectorAll(clickable.dataset.collapse)) {
|
||||||
|
row.classList.toggle('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+104
-49
@@ -12,6 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ const sizesDebug = false
|
|||||||
|
|
||||||
// programSize contains size statistics per package of a compiled program.
|
// programSize contains size statistics per package of a compiled program.
|
||||||
type programSize struct {
|
type programSize struct {
|
||||||
Packages map[string]packageSize
|
Packages map[string]*packageSize
|
||||||
Code uint64
|
Code uint64
|
||||||
ROData uint64
|
ROData uint64
|
||||||
Data uint64
|
Data uint64
|
||||||
@@ -52,13 +53,29 @@ func (ps *programSize) RAM() uint64 {
|
|||||||
return ps.Data + ps.BSS
|
return ps.Data + ps.BSS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the package size information for a given package path, creating it if
|
||||||
|
// it doesn't exist yet.
|
||||||
|
func (ps *programSize) getPackage(path string) *packageSize {
|
||||||
|
if field, ok := ps.Packages[path]; ok {
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
field := &packageSize{
|
||||||
|
Program: ps,
|
||||||
|
Sub: map[string]*packageSize{},
|
||||||
|
}
|
||||||
|
ps.Packages[path] = field
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
// packageSize contains the size of a package, calculated from the linked object
|
// packageSize contains the size of a package, calculated from the linked object
|
||||||
// file.
|
// file.
|
||||||
type packageSize struct {
|
type packageSize struct {
|
||||||
Code uint64
|
Program *programSize
|
||||||
ROData uint64
|
Code uint64
|
||||||
Data uint64
|
ROData uint64
|
||||||
BSS uint64
|
Data uint64
|
||||||
|
BSS uint64
|
||||||
|
Sub map[string]*packageSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flash usage in regular microcontrollers.
|
// Flash usage in regular microcontrollers.
|
||||||
@@ -71,6 +88,31 @@ func (ps *packageSize) RAM() uint64 {
|
|||||||
return ps.Data + ps.BSS
|
return ps.Data + ps.BSS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flash usage in regular microcontrollers, as a percentage of the total flash
|
||||||
|
// usage of the program.
|
||||||
|
func (ps *packageSize) FlashPercent() float64 {
|
||||||
|
return float64(ps.Flash()) / float64(ps.Program.Flash()) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a single size data point to this package.
|
||||||
|
// This must only be called while calculating package size, not afterwards.
|
||||||
|
func (ps *packageSize) addSize(getField func(*packageSize, bool) *uint64, filename string, size uint64, isVariable bool) {
|
||||||
|
if size == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add size for the package.
|
||||||
|
*getField(ps, isVariable) += size
|
||||||
|
|
||||||
|
// Add size for file inside package.
|
||||||
|
sub, ok := ps.Sub[filename]
|
||||||
|
if !ok {
|
||||||
|
sub = &packageSize{Program: ps.Program}
|
||||||
|
ps.Sub[filename] = sub
|
||||||
|
}
|
||||||
|
*getField(sub, isVariable) += size
|
||||||
|
}
|
||||||
|
|
||||||
// A mapping of a single chunk of code or data to a file path.
|
// A mapping of a single chunk of code or data to a file path.
|
||||||
type addressLine struct {
|
type addressLine struct {
|
||||||
Address uint64
|
Address uint64
|
||||||
@@ -194,11 +236,22 @@ func readProgramSizeFromDWARF(data *dwarf.Data, codeOffset, codeAlignment uint64
|
|||||||
if !prevLineEntry.EndSequence {
|
if !prevLineEntry.EndSequence {
|
||||||
// The chunk describes the code from prevLineEntry to
|
// The chunk describes the code from prevLineEntry to
|
||||||
// lineEntry.
|
// lineEntry.
|
||||||
|
path := prevLineEntry.File.Name
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// Work around a Clang bug on Windows:
|
||||||
|
// https://github.com/llvm/llvm-project/issues/117317
|
||||||
|
path = strings.ReplaceAll(path, "\\\\", "\\")
|
||||||
|
|
||||||
|
// wasi-libc likes to use forward slashes, but we
|
||||||
|
// canonicalize everything to use backwards slashes as
|
||||||
|
// is common on Windows.
|
||||||
|
path = strings.ReplaceAll(path, "/", "\\")
|
||||||
|
}
|
||||||
line := addressLine{
|
line := addressLine{
|
||||||
Address: prevLineEntry.Address + codeOffset,
|
Address: prevLineEntry.Address + codeOffset,
|
||||||
Length: lineEntry.Address - prevLineEntry.Address,
|
Length: lineEntry.Address - prevLineEntry.Address,
|
||||||
Align: codeAlignment,
|
Align: codeAlignment,
|
||||||
File: prevLineEntry.File.Name,
|
File: path,
|
||||||
}
|
}
|
||||||
if line.Length != 0 {
|
if line.Length != 0 {
|
||||||
addresses = append(addresses, line)
|
addresses = append(addresses, line)
|
||||||
@@ -437,9 +490,9 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if section.Type == elf.SHT_NOBITS {
|
if section.Type == elf.SHT_NOBITS {
|
||||||
if section.Name == ".stack" {
|
if section.Name == ".stack" || section.Name == ".stack1" {
|
||||||
// TinyGo emits stack sections on microcontroller using the
|
// TinyGo emits stack sections on microcontroller using the
|
||||||
// ".stack" name.
|
// ".stack" (or ".stack1") name.
|
||||||
// This is a bit ugly, but I don't think there is a way to
|
// This is a bit ugly, but I don't think there is a way to
|
||||||
// mark the stack section in a linker script.
|
// mark the stack section in a linker script.
|
||||||
sections = append(sections, memorySection{
|
sections = append(sections, memorySection{
|
||||||
@@ -773,49 +826,40 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
|
|
||||||
// Now finally determine the binary/RAM size usage per package by going
|
// Now finally determine the binary/RAM size usage per package by going
|
||||||
// through each allocated section.
|
// through each allocated section.
|
||||||
sizes := make(map[string]packageSize)
|
sizes := make(map[string]*packageSize)
|
||||||
|
program := &programSize{
|
||||||
|
Packages: sizes,
|
||||||
|
}
|
||||||
for _, section := range sections {
|
for _, section := range sections {
|
||||||
switch section.Type {
|
switch section.Type {
|
||||||
case memoryCode:
|
case memoryCode:
|
||||||
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 {
|
||||||
field := sizes[path]
|
|
||||||
if isVariable {
|
if isVariable {
|
||||||
field.ROData += size
|
return &ps.ROData
|
||||||
} else {
|
|
||||||
field.Code += size
|
|
||||||
}
|
}
|
||||||
sizes[path] = field
|
return &ps.Code
|
||||||
}, packagePathMap)
|
}, packagePathMap)
|
||||||
case memoryROData:
|
case memoryROData:
|
||||||
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 {
|
||||||
field := sizes[path]
|
return &ps.ROData
|
||||||
field.ROData += size
|
|
||||||
sizes[path] = field
|
|
||||||
}, packagePathMap)
|
}, packagePathMap)
|
||||||
case memoryData:
|
case memoryData:
|
||||||
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 {
|
||||||
field := sizes[path]
|
return &ps.Data
|
||||||
field.Data += size
|
|
||||||
sizes[path] = field
|
|
||||||
}, packagePathMap)
|
}, packagePathMap)
|
||||||
case memoryBSS:
|
case memoryBSS:
|
||||||
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
|
readSection(section, addresses, program, func(ps *packageSize, isVariable bool) *uint64 {
|
||||||
field := sizes[path]
|
return &ps.BSS
|
||||||
field.BSS += size
|
|
||||||
sizes[path] = field
|
|
||||||
}, packagePathMap)
|
}, packagePathMap)
|
||||||
case memoryStack:
|
case memoryStack:
|
||||||
// We store the C stack as a pseudo-package.
|
// We store the C stack as a pseudo-package.
|
||||||
sizes["C stack"] = packageSize{
|
program.getPackage("C stack").addSize(func(ps *packageSize, isVariable bool) *uint64 {
|
||||||
BSS: section.Size,
|
return &ps.BSS
|
||||||
}
|
}, "", section.Size, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...and summarize the results.
|
// ...and summarize the results.
|
||||||
program := &programSize{
|
|
||||||
Packages: sizes,
|
|
||||||
}
|
|
||||||
for _, pkg := range sizes {
|
for _, pkg := range sizes {
|
||||||
program.Code += pkg.Code
|
program.Code += pkg.Code
|
||||||
program.ROData += pkg.ROData
|
program.ROData += pkg.ROData
|
||||||
@@ -826,8 +870,8 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
}
|
}
|
||||||
|
|
||||||
// readSection determines for each byte in this section to which package it
|
// readSection determines for each byte in this section to which package it
|
||||||
// belongs. It reports this usage through the addSize callback.
|
// belongs.
|
||||||
func readSection(section memorySection, addresses []addressLine, addSize func(string, uint64, bool), packagePathMap map[string]string) {
|
func readSection(section memorySection, addresses []addressLine, program *programSize, getField func(*packageSize, bool) *uint64, packagePathMap map[string]string) {
|
||||||
// The addr variable tracks at which address we are while going through this
|
// The addr variable tracks at which address we are while going through this
|
||||||
// section. We start at the beginning.
|
// section. We start at the beginning.
|
||||||
addr := section.Address
|
addr := section.Address
|
||||||
@@ -849,9 +893,9 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
|||||||
addrAligned := (addr + line.Align - 1) &^ (line.Align - 1)
|
addrAligned := (addr + line.Align - 1) &^ (line.Align - 1)
|
||||||
if line.Align > 1 && addrAligned >= line.Address {
|
if line.Align > 1 && addrAligned >= line.Address {
|
||||||
// It is, assume that's what causes the gap.
|
// It is, assume that's what causes the gap.
|
||||||
addSize("(padding)", line.Address-addr, true)
|
program.getPackage("(padding)").addSize(getField, "", line.Address-addr, true)
|
||||||
} else {
|
} else {
|
||||||
addSize("(unknown)", line.Address-addr, false)
|
program.getPackage("(unknown)").addSize(getField, "", line.Address-addr, false)
|
||||||
if sizesDebug {
|
if sizesDebug {
|
||||||
fmt.Printf("%08x..%08x %5d: unknown (gap), alignment=%d\n", addr, line.Address, line.Address-addr, line.Align)
|
fmt.Printf("%08x..%08x %5d: unknown (gap), alignment=%d\n", addr, line.Address, line.Address-addr, line.Align)
|
||||||
}
|
}
|
||||||
@@ -873,7 +917,8 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
|||||||
length = line.Length - (addr - line.Address)
|
length = line.Length - (addr - line.Address)
|
||||||
}
|
}
|
||||||
// Finally, mark this chunk of memory as used by the given package.
|
// Finally, mark this chunk of memory as used by the given package.
|
||||||
addSize(findPackagePath(line.File, packagePathMap), length, line.IsVariable)
|
packagePath, filename := findPackagePath(line.File, packagePathMap)
|
||||||
|
program.getPackage(packagePath).addSize(getField, filename, length, line.IsVariable)
|
||||||
addr = line.Address + line.Length
|
addr = line.Address + line.Length
|
||||||
}
|
}
|
||||||
if addr < sectionEnd {
|
if addr < sectionEnd {
|
||||||
@@ -882,9 +927,9 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
|||||||
if section.Align > 1 && addrAligned >= sectionEnd {
|
if section.Align > 1 && addrAligned >= sectionEnd {
|
||||||
// The gap is caused by the section alignment.
|
// The gap is caused by the section alignment.
|
||||||
// For example, if a .rodata section ends with a non-aligned string.
|
// For example, if a .rodata section ends with a non-aligned string.
|
||||||
addSize("(padding)", sectionEnd-addr, true)
|
program.getPackage("(padding)").addSize(getField, "", sectionEnd-addr, true)
|
||||||
} else {
|
} else {
|
||||||
addSize("(unknown)", sectionEnd-addr, false)
|
program.getPackage("(unknown)").addSize(getField, "", sectionEnd-addr, false)
|
||||||
if sizesDebug {
|
if sizesDebug {
|
||||||
fmt.Printf("%08x..%08x %5d: unknown (end), alignment=%d\n", addr, sectionEnd, sectionEnd-addr, section.Align)
|
fmt.Printf("%08x..%08x %5d: unknown (end), alignment=%d\n", addr, sectionEnd, sectionEnd-addr, section.Align)
|
||||||
}
|
}
|
||||||
@@ -894,17 +939,25 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
|
|||||||
|
|
||||||
// findPackagePath returns the Go package (or a pseudo package) for the given
|
// findPackagePath returns the Go package (or a pseudo package) for the given
|
||||||
// path. It uses some heuristics, for example for some C libraries.
|
// path. It uses some heuristics, for example for some C libraries.
|
||||||
func findPackagePath(path string, packagePathMap map[string]string) string {
|
func findPackagePath(path string, packagePathMap map[string]string) (packagePath, filename string) {
|
||||||
// Check whether this path is part of one of the compiled packages.
|
// Check whether this path is part of one of the compiled packages.
|
||||||
packagePath, ok := packagePathMap[filepath.Dir(path)]
|
packagePath, ok := packagePathMap[filepath.Dir(path)]
|
||||||
if !ok {
|
if ok {
|
||||||
|
// Directory is known as a Go package.
|
||||||
|
// Add the file itself as well.
|
||||||
|
filename = filepath.Base(path)
|
||||||
|
} else {
|
||||||
if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")) {
|
if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")) {
|
||||||
// Emit C libraries (in the lib subdirectory of TinyGo) as a single
|
// Emit C libraries (in the lib subdirectory of TinyGo) as a single
|
||||||
// package, with a "C" prefix. For example: "C compiler-rt" for the
|
// package, with a "C" prefix. For example: "C picolibc" for the
|
||||||
// compiler runtime library from LLVM.
|
// baremetal libc.
|
||||||
packagePath = "C " + strings.Split(strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")), string(os.PathSeparator))[1]
|
libPath := strings.TrimPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "lib")+string(os.PathSeparator))
|
||||||
} else if strings.HasPrefix(path, filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project")) {
|
parts := strings.SplitN(libPath, string(os.PathSeparator), 2)
|
||||||
|
packagePath = "C " + parts[0]
|
||||||
|
filename = parts[1]
|
||||||
|
} else if prefix := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project", "compiler-rt"); strings.HasPrefix(path, prefix) {
|
||||||
packagePath = "C compiler-rt"
|
packagePath = "C compiler-rt"
|
||||||
|
filename = strings.TrimPrefix(path, prefix+string(os.PathSeparator))
|
||||||
} else if packageSymbolRegexp.MatchString(path) {
|
} else if packageSymbolRegexp.MatchString(path) {
|
||||||
// Parse symbol names like main$alloc or runtime$string.
|
// Parse symbol names like main$alloc or runtime$string.
|
||||||
packagePath = path[:strings.LastIndex(path, "$")]
|
packagePath = path[:strings.LastIndex(path, "$")]
|
||||||
@@ -927,9 +980,11 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
|
|||||||
// fixed in the compiler.
|
// fixed in the compiler.
|
||||||
packagePath = "-"
|
packagePath = "-"
|
||||||
} else {
|
} else {
|
||||||
// This is some other path. Not sure what it is, so just emit its directory.
|
// This is some other path. Not sure what it is, so just emit its
|
||||||
packagePath = filepath.Dir(path) // fallback
|
// directory as a fallback.
|
||||||
|
packagePath = filepath.Dir(path)
|
||||||
|
filename = filepath.Base(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return packagePath
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+70
-22
@@ -1,6 +1,7 @@
|
|||||||
package builder
|
package builder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -41,9 +42,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", 4600, 280, 0, 2268},
|
{"hifive1b", "examples/echo", 4560, 280, 0, 2268},
|
||||||
{"microbit", "examples/serial", 2908, 388, 8, 2272},
|
{"microbit", "examples/serial", 2908, 388, 8, 2272},
|
||||||
{"wioterminal", "examples/pininterrupt", 6140, 1484, 116, 6832},
|
{"wioterminal", "examples/pininterrupt", 7293, 1487, 116, 6912},
|
||||||
|
|
||||||
// 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
|
||||||
@@ -55,26 +56,7 @@ func TestBinarySize(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Build the binary.
|
// Build the binary.
|
||||||
options := compileopts.Options{
|
result := buildBinary(t, tc.target, tc.path)
|
||||||
Target: tc.target,
|
|
||||||
Opt: "z",
|
|
||||||
Semaphore: sema,
|
|
||||||
InterpTimeout: 60 * time.Second,
|
|
||||||
Debug: true,
|
|
||||||
VerifyIR: true,
|
|
||||||
}
|
|
||||||
target, err := compileopts.LoadTarget(&options)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("could not load target:", err)
|
|
||||||
}
|
|
||||||
config := &compileopts.Config{
|
|
||||||
Options: &options,
|
|
||||||
Target: target,
|
|
||||||
}
|
|
||||||
result, err := Build(tc.path, "", t.TempDir(), config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("could not build:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the size of the binary matches the expected size.
|
// Check whether the size of the binary matches the expected size.
|
||||||
sizes, err := loadProgramSize(result.Executable, nil)
|
sizes, err := loadProgramSize(result.Executable, nil)
|
||||||
@@ -90,3 +72,69 @@ func TestBinarySize(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that the -size=full flag attributes binary size to the correct package
|
||||||
|
// without filesystem paths and things like that.
|
||||||
|
func TestSizeFull(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"microbit",
|
||||||
|
"wasip1",
|
||||||
|
}
|
||||||
|
|
||||||
|
libMatch := regexp.MustCompile(`^C [a-z -]+$`) // example: "C interrupt vector"
|
||||||
|
pkgMatch := regexp.MustCompile(`^[a-z/]+$`) // example: "internal/task"
|
||||||
|
|
||||||
|
for _, target := range tests {
|
||||||
|
target := target
|
||||||
|
t.Run(target, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Build the binary.
|
||||||
|
result := buildBinary(t, target, "examples/serial")
|
||||||
|
|
||||||
|
// Check whether the binary doesn't contain any unexpected package
|
||||||
|
// names.
|
||||||
|
sizes, err := loadProgramSize(result.Executable, result.PackagePathMap)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not read program size:", err)
|
||||||
|
}
|
||||||
|
for _, pkg := range sizes.sortedPackageNames() {
|
||||||
|
if pkg == "(padding)" || pkg == "(unknown)" {
|
||||||
|
// TODO: correctly attribute all unknown binary size.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if libMatch.MatchString(pkg) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if pkgMatch.MatchString(pkg) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Error("unexpected package name in size output:", pkg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBinary(t *testing.T, targetString, pkgName string) BuildResult {
|
||||||
|
options := compileopts.Options{
|
||||||
|
Target: targetString,
|
||||||
|
Opt: "z",
|
||||||
|
Semaphore: sema,
|
||||||
|
InterpTimeout: 60 * time.Second,
|
||||||
|
Debug: true,
|
||||||
|
VerifyIR: true,
|
||||||
|
}
|
||||||
|
target, err := compileopts.LoadTarget(&options)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not load target:", err)
|
||||||
|
}
|
||||||
|
config := &compileopts.Config{
|
||||||
|
Options: &options,
|
||||||
|
Target: target,
|
||||||
|
}
|
||||||
|
result, err := Build(pkgName, "", t.TempDir(), config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not build:", err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
+5
-5
@@ -114,8 +114,8 @@ func parseLLDErrors(text string) error {
|
|||||||
|
|
||||||
// Check for undefined symbols.
|
// Check for undefined symbols.
|
||||||
// This can happen in some cases like with CGo and //go:linkname tricker.
|
// This can happen in some cases like with CGo and //go:linkname tricker.
|
||||||
if matches := regexp.MustCompile(`^ld.lld: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
||||||
symbolName := matches[1]
|
symbolName := matches[2]
|
||||||
for _, line := range strings.Split(message, "\n") {
|
for _, line := range strings.Split(message, "\n") {
|
||||||
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
|
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
|
||||||
if matches != nil {
|
if matches != nil {
|
||||||
@@ -134,9 +134,9 @@ func parseLLDErrors(text string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for flash/RAM overflow.
|
// Check for flash/RAM overflow.
|
||||||
if matches := regexp.MustCompile(`^ld.lld: error: section '(.*?)' will not fit in region '(.*?)': overflowed by ([0-9]+) bytes$`).FindStringSubmatch(message); matches != nil {
|
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: section '(.*?)' will not fit in region '(.*?)': overflowed by ([0-9]+) bytes$`).FindStringSubmatch(message); matches != nil {
|
||||||
region := matches[2]
|
region := matches[3]
|
||||||
n, err := strconv.ParseUint(matches[3], 10, 64)
|
n, err := strconv.ParseUint(matches[4], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Should not happen at all (unless it overflows an uint64 for some reason).
|
// Should not happen at all (unless it overflows an uint64 for some reason).
|
||||||
continue
|
continue
|
||||||
|
|||||||
+170
-4
@@ -18,6 +18,7 @@ import (
|
|||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ type cgoPackage struct {
|
|||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
tokenFiles map[string]*token.File
|
tokenFiles map[string]*token.File
|
||||||
definedGlobally map[string]ast.Node
|
definedGlobally map[string]ast.Node
|
||||||
|
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
|
||||||
anonDecls map[interface{}]string
|
anonDecls map[interface{}]string
|
||||||
cflags []string // CFlags from #cgo lines
|
cflags []string // CFlags from #cgo lines
|
||||||
ldflags []string // LDFlags from #cgo lines
|
ldflags []string // LDFlags from #cgo lines
|
||||||
@@ -80,6 +82,13 @@ type bitfieldInfo struct {
|
|||||||
endBit int64 // may be 0 meaning "until the end of the field"
|
endBit int64 // may be 0 meaning "until the end of the field"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Information about a #cgo noescape line in the source code.
|
||||||
|
type noescapingFunc struct {
|
||||||
|
name string
|
||||||
|
pos token.Pos
|
||||||
|
used bool // true if used somewhere in the source (for proper error reporting)
|
||||||
|
}
|
||||||
|
|
||||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||||
// in both languages. See addTypeAliases.
|
// in both languages. See addTypeAliases.
|
||||||
var cgoAliases = map[string]string{
|
var cgoAliases = map[string]string{
|
||||||
@@ -138,7 +147,8 @@ typedef unsigned long long _Cgo_ulonglong;
|
|||||||
// first.
|
// first.
|
||||||
// These functions will be modified to get a "C." prefix, so the source below
|
// These functions will be modified to get a "C." prefix, so the source below
|
||||||
// doesn't reflect the final AST.
|
// doesn't reflect the final AST.
|
||||||
const generatedGoFilePrefix = `
|
const generatedGoFilePrefixBase = `
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -169,6 +179,74 @@ func __CBytes([]byte) unsafe.Pointer
|
|||||||
func CBytes(b []byte) unsafe.Pointer {
|
func CBytes(b []byte) unsafe.Pointer {
|
||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func __get_errno_num() uintptr
|
||||||
|
`
|
||||||
|
|
||||||
|
const generatedGoFilePrefixOther = generatedGoFilePrefixBase + `
|
||||||
|
func __get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// Windows uses fake errno values in the syscall package.
|
||||||
|
// See for example: https://github.com/golang/go/issues/23468
|
||||||
|
// TinyGo uses mingw-w64 though, which does have defined errno values. Since the
|
||||||
|
// syscall package is the standard library one we can't change it, but we can
|
||||||
|
// map the errno values to match the values in the syscall package.
|
||||||
|
// Source of the errno values: lib/mingw-w64/mingw-w64-headers/crt/errno.h
|
||||||
|
const generatedGoFilePrefixWindows = generatedGoFilePrefixBase + `
|
||||||
|
var __errno_mapping = [...]syscall.Errno{
|
||||||
|
1: syscall.EPERM,
|
||||||
|
2: syscall.ENOENT,
|
||||||
|
3: syscall.ESRCH,
|
||||||
|
4: syscall.EINTR,
|
||||||
|
5: syscall.EIO,
|
||||||
|
6: syscall.ENXIO,
|
||||||
|
7: syscall.E2BIG,
|
||||||
|
8: syscall.ENOEXEC,
|
||||||
|
9: syscall.EBADF,
|
||||||
|
10: syscall.ECHILD,
|
||||||
|
11: syscall.EAGAIN,
|
||||||
|
12: syscall.ENOMEM,
|
||||||
|
13: syscall.EACCES,
|
||||||
|
14: syscall.EFAULT,
|
||||||
|
16: syscall.EBUSY,
|
||||||
|
17: syscall.EEXIST,
|
||||||
|
18: syscall.EXDEV,
|
||||||
|
19: syscall.ENODEV,
|
||||||
|
20: syscall.ENOTDIR,
|
||||||
|
21: syscall.EISDIR,
|
||||||
|
22: syscall.EINVAL,
|
||||||
|
23: syscall.ENFILE,
|
||||||
|
24: syscall.EMFILE,
|
||||||
|
25: syscall.ENOTTY,
|
||||||
|
27: syscall.EFBIG,
|
||||||
|
28: syscall.ENOSPC,
|
||||||
|
29: syscall.ESPIPE,
|
||||||
|
30: syscall.EROFS,
|
||||||
|
31: syscall.EMLINK,
|
||||||
|
32: syscall.EPIPE,
|
||||||
|
33: syscall.EDOM,
|
||||||
|
34: syscall.ERANGE,
|
||||||
|
36: syscall.EDEADLK,
|
||||||
|
38: syscall.ENAMETOOLONG,
|
||||||
|
39: syscall.ENOLCK,
|
||||||
|
40: syscall.ENOSYS,
|
||||||
|
41: syscall.ENOTEMPTY,
|
||||||
|
42: syscall.EILSEQ,
|
||||||
|
}
|
||||||
|
|
||||||
|
func __get_errno() error {
|
||||||
|
num := C.__get_errno_num()
|
||||||
|
if num < uintptr(len(__errno_mapping)) {
|
||||||
|
if mapped := __errno_mapping[num]; mapped != 0 {
|
||||||
|
return mapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return syscall.Errno(num)
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||||
@@ -178,7 +256,7 @@ func CBytes(b []byte) unsafe.Pointer {
|
|||||||
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
||||||
// hashes of the accessed C header files. If there is one or more error, it
|
// hashes of the accessed C header files. If there is one or more error, it
|
||||||
// returns these in the []error slice but still modifies the AST.
|
// returns these in the []error slice but still modifies the AST.
|
||||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, goos string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||||
p := &cgoPackage{
|
p := &cgoPackage{
|
||||||
packageName: files[0].Name.Name,
|
packageName: files[0].Name.Name,
|
||||||
currentDir: dir,
|
currentDir: dir,
|
||||||
@@ -186,6 +264,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
fset: fset,
|
fset: fset,
|
||||||
tokenFiles: map[string]*token.File{},
|
tokenFiles: map[string]*token.File{},
|
||||||
definedGlobally: map[string]ast.Node{},
|
definedGlobally: map[string]ast.Node{},
|
||||||
|
noescapingFuncs: map[string]*noescapingFunc{},
|
||||||
anonDecls: map[interface{}]string{},
|
anonDecls: map[interface{}]string{},
|
||||||
visitedFiles: map[string][]byte{},
|
visitedFiles: map[string][]byte{},
|
||||||
}
|
}
|
||||||
@@ -210,7 +289,12 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Construct a new in-memory AST for CGo declarations of this package.
|
// Construct a new in-memory AST for CGo declarations of this package.
|
||||||
// The first part is written as Go code that is then parsed, but more code
|
// The first part is written as Go code that is then parsed, but more code
|
||||||
// is added later to the AST to declare functions, globals, etc.
|
// is added later to the AST to declare functions, globals, etc.
|
||||||
goCode := "package " + files[0].Name.Name + "\n\n" + generatedGoFilePrefix
|
goCode := "package " + files[0].Name.Name + "\n\n"
|
||||||
|
if goos == "windows" {
|
||||||
|
goCode += generatedGoFilePrefixWindows
|
||||||
|
} else {
|
||||||
|
goCode += generatedGoFilePrefixOther
|
||||||
|
}
|
||||||
p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments)
|
p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is always a bug in the cgo package.
|
// This is always a bug in the cgo package.
|
||||||
@@ -225,7 +309,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
switch decl := decl.(type) {
|
switch decl := decl.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
switch decl.Name.Name {
|
switch decl.Name.Name {
|
||||||
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes", "CBytes", "__CBytes":
|
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes", "CBytes", "__CBytes", "__get_errno_num", "__get_errno", "__errno_mapping":
|
||||||
// Adjust the name to have a "C." prefix so it is correctly
|
// Adjust the name to have a "C." prefix so it is correctly
|
||||||
// resolved.
|
// resolved.
|
||||||
decl.Name.Name = "C." + decl.Name.Name
|
decl.Name.Name = "C." + decl.Name.Name
|
||||||
@@ -344,6 +428,22 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show an error when a #cgo noescape line isn't used in practice.
|
||||||
|
// This matches upstream Go. I think the goal is to avoid issues with
|
||||||
|
// misspelled function names, which seems very useful.
|
||||||
|
var unusedNoescapeLines []*noescapingFunc
|
||||||
|
for _, value := range p.noescapingFuncs {
|
||||||
|
if !value.used {
|
||||||
|
unusedNoescapeLines = append(unusedNoescapeLines, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.SliceStable(unusedNoescapeLines, func(i, j int) bool {
|
||||||
|
return unusedNoescapeLines[i].pos < unusedNoescapeLines[j].pos
|
||||||
|
})
|
||||||
|
for _, value := range unusedNoescapeLines {
|
||||||
|
p.addError(value.pos, fmt.Sprintf("function %#v in #cgo noescape line is not used", value.name))
|
||||||
|
}
|
||||||
|
|
||||||
// Print the newly generated in-memory AST, for debugging.
|
// Print the newly generated in-memory AST, for debugging.
|
||||||
//ast.Print(fset, p.generated)
|
//ast.Print(fset, p.generated)
|
||||||
|
|
||||||
@@ -409,6 +509,33 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin
|
|||||||
}
|
}
|
||||||
text = text[:lineStart] + string(spaces) + text[lineEnd:]
|
text = text[:lineStart] + string(spaces) + text[lineEnd:]
|
||||||
|
|
||||||
|
allFields := strings.Fields(line[4:])
|
||||||
|
switch allFields[0] {
|
||||||
|
case "noescape":
|
||||||
|
// The code indicates that pointer parameters will not be captured
|
||||||
|
// by the called C function.
|
||||||
|
if len(allFields) < 2 {
|
||||||
|
p.addErrorAfter(pos, text[:lineStart], "missing function name in #cgo noescape line")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(allFields) > 2 {
|
||||||
|
p.addErrorAfter(pos, text[:lineStart], "multiple function names in #cgo noescape line")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := allFields[1]
|
||||||
|
p.noescapingFuncs[name] = &noescapingFunc{
|
||||||
|
name: name,
|
||||||
|
pos: pos,
|
||||||
|
used: false,
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case "nocallback":
|
||||||
|
// We don't do anything special when calling a C function, so there
|
||||||
|
// appears to be no optimization that we can do here.
|
||||||
|
// Accept, but ignore the parameter for compatibility.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Get the text before the colon in the #cgo directive.
|
// Get the text before the colon in the #cgo directive.
|
||||||
colon := strings.IndexByte(line, ':')
|
colon := strings.IndexByte(line, ':')
|
||||||
if colon < 0 {
|
if colon < 0 {
|
||||||
@@ -1279,6 +1406,45 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
|
|||||||
// separate namespace (no _Cgo_ hacks like in gc).
|
// separate namespace (no _Cgo_ hacks like in gc).
|
||||||
func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
|
func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
|
||||||
switch node := cursor.Node().(type) {
|
switch node := cursor.Node().(type) {
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
// An assign statement could be something like this:
|
||||||
|
//
|
||||||
|
// val, errno := C.some_func()
|
||||||
|
//
|
||||||
|
// Check whether it looks like that, and if so, read the errno value and
|
||||||
|
// return it as the second return value. The call will be transformed
|
||||||
|
// into something like this:
|
||||||
|
//
|
||||||
|
// val, errno := C.some_func(), C.__get_errno()
|
||||||
|
if len(node.Lhs) != 2 || len(node.Rhs) != 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
rhs, ok := node.Rhs[0].(*ast.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
fun, ok := rhs.Fun.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
x, ok := fun.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if found, ok := names[fun.Sel.Name]; ok && x.Name == "C" {
|
||||||
|
// Replace "C"."some_func" into "C.somefunc".
|
||||||
|
rhs.Fun = &ast.Ident{
|
||||||
|
NamePos: x.NamePos,
|
||||||
|
Name: f.getASTDeclName(fun.Sel.Name, found, true),
|
||||||
|
}
|
||||||
|
// Add the errno value as the second value in the statement.
|
||||||
|
node.Rhs = append(node.Rhs, &ast.CallExpr{
|
||||||
|
Fun: &ast.Ident{
|
||||||
|
NamePos: node.Lhs[1].End(),
|
||||||
|
Name: "C.__get_errno",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
+23
-4
@@ -56,7 +56,7 @@ func TestCGo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process the AST with CGo.
|
// Process the AST with CGo.
|
||||||
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
|
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "linux")
|
||||||
|
|
||||||
// Check the AST for type errors.
|
// Check the AST for type errors.
|
||||||
var typecheckErrors []error
|
var typecheckErrors []error
|
||||||
@@ -64,7 +64,7 @@ func TestCGo(t *testing.T) {
|
|||||||
Error: func(err error) {
|
Error: func(err error) {
|
||||||
typecheckErrors = append(typecheckErrors, err)
|
typecheckErrors = append(typecheckErrors, err)
|
||||||
},
|
},
|
||||||
Importer: simpleImporter{},
|
Importer: newSimpleImporter(),
|
||||||
Sizes: types.SizesFor("gccgo", "arm"),
|
Sizes: types.SizesFor("gccgo", "arm"),
|
||||||
}
|
}
|
||||||
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
|
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
|
||||||
@@ -202,14 +202,33 @@ func Test_cgoPackage_isEquivalentAST(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// simpleImporter implements the types.Importer interface, but only allows
|
// simpleImporter implements the types.Importer interface, but only allows
|
||||||
// importing the unsafe package.
|
// importing the syscall and unsafe packages.
|
||||||
type simpleImporter struct {
|
type simpleImporter struct {
|
||||||
|
syscallPkg *types.Package
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSimpleImporter() *simpleImporter {
|
||||||
|
i := &simpleImporter{}
|
||||||
|
|
||||||
|
// Implement a dummy syscall package with the Errno type.
|
||||||
|
i.syscallPkg = types.NewPackage("syscall", "syscall")
|
||||||
|
obj := types.NewTypeName(token.NoPos, i.syscallPkg, "Errno", nil)
|
||||||
|
named := types.NewNamed(obj, nil, nil)
|
||||||
|
i.syscallPkg.Scope().Insert(obj)
|
||||||
|
named.SetUnderlying(types.Typ[types.Uintptr])
|
||||||
|
sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(types.NewParam(token.NoPos, i.syscallPkg, "", types.Typ[types.String])), false)
|
||||||
|
named.AddMethod(types.NewFunc(token.NoPos, i.syscallPkg, "Error", sig))
|
||||||
|
i.syscallPkg.MarkComplete()
|
||||||
|
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import implements the Importer interface. For testing usage only: it only
|
// Import implements the Importer interface. For testing usage only: it only
|
||||||
// supports importing the unsafe package.
|
// supports importing the unsafe package.
|
||||||
func (i simpleImporter) Import(path string) (*types.Package, error) {
|
func (i *simpleImporter) Import(path string) (*types.Package, error) {
|
||||||
switch path {
|
switch path {
|
||||||
|
case "syscall":
|
||||||
|
return i.syscallPkg, nil
|
||||||
case "unsafe":
|
case "unsafe":
|
||||||
return types.Unsafe, nil
|
return types.Unsafe, nil
|
||||||
default:
|
default:
|
||||||
|
|||||||
+23
-2
@@ -65,9 +65,22 @@ unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c);
|
|||||||
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
||||||
unsigned tinygo_clang_Cursor_isMacroFunctionLike(GoCXCursor c);
|
unsigned tinygo_clang_Cursor_isMacroFunctionLike(GoCXCursor c);
|
||||||
|
|
||||||
|
// Fix some warnings on Windows ARM. Without the __declspec(dllexport), it gives warnings like this:
|
||||||
|
// In file included from _cgo_export.c:4:
|
||||||
|
// cgo-gcc-export-header-prolog:49:34: warning: redeclaration of 'tinygo_clang_globals_visitor' should not add 'dllexport' attribute [-Wdll-attribute-on-redeclaration]
|
||||||
|
// libclang.go:68:5: note: previous declaration is here
|
||||||
|
// See: https://github.com/golang/go/issues/49721
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define CGO_DECL // __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define CGO_DECL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CGO_DECL
|
||||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
|
CGO_DECL
|
||||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
CGO_DECL
|
||||||
void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data);
|
void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
@@ -256,10 +269,18 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
var doc []string
|
||||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||||
|
doc = append(doc, "//go:variadic")
|
||||||
|
}
|
||||||
|
if _, ok := f.noescapingFuncs[name]; ok {
|
||||||
|
doc = append(doc, "//go:noescape")
|
||||||
|
f.noescapingFuncs[name].used = true
|
||||||
|
}
|
||||||
|
if len(doc) != 0 {
|
||||||
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||||
Slash: pos - 1,
|
Slash: pos - 1,
|
||||||
Text: "//go:variadic",
|
Text: strings.Join(doc, "\n"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for i := 0; i < numArgs; i++ {
|
for i := 0; i < numArgs; i++ {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17
|
//go:build !byollvm && llvm18
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18
|
||||||
|
|
||||||
|
package cgo
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux CFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib/llvm-19/include
|
||||||
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@19/include
|
||||||
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@19/include
|
||||||
|
#cgo freebsd CFLAGS: -I/usr/local/llvm19/include
|
||||||
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-19/lib -lclang
|
||||||
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@19/lib -lclang
|
||||||
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@19/lib -lclang
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm19/lib -lclang
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
Vendored
+8
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -31,6 +32,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+8
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -31,6 +32,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+5
@@ -10,6 +10,11 @@ typedef struct {
|
|||||||
|
|
||||||
typedef someType noType; // undefined type
|
typedef someType noType; // undefined type
|
||||||
|
|
||||||
|
// Some invalid noescape lines
|
||||||
|
#cgo noescape
|
||||||
|
#cgo noescape foo bar
|
||||||
|
#cgo noescape unusedFunction
|
||||||
|
|
||||||
#define SOME_CONST_1 5) // invalid const syntax
|
#define SOME_CONST_1 5) // invalid const syntax
|
||||||
#define SOME_CONST_2 6) // const not used (so no error)
|
#define SOME_CONST_2 6) // const not used (so no error)
|
||||||
#define SOME_CONST_3 1234 // const too large for byte
|
#define SOME_CONST_3 1234 // const too large for byte
|
||||||
|
|||||||
Vendored
+18
-7
@@ -1,14 +1,17 @@
|
|||||||
// CGo errors:
|
// CGo errors:
|
||||||
|
// testdata/errors.go:14:1: missing function name in #cgo noescape line
|
||||||
|
// testdata/errors.go:15:1: multiple function names in #cgo noescape line
|
||||||
// testdata/errors.go:4:2: warning: some warning
|
// testdata/errors.go:4:2: warning: some warning
|
||||||
// testdata/errors.go:11:9: error: unknown type name 'someType'
|
// testdata/errors.go:11:9: error: unknown type name 'someType'
|
||||||
// testdata/errors.go:26:5: warning: another warning
|
// testdata/errors.go:31:5: warning: another warning
|
||||||
// testdata/errors.go:13:23: unexpected token ), expected end of expression
|
// testdata/errors.go:18:23: unexpected token ), expected end of expression
|
||||||
// testdata/errors.go:21:26: unexpected token ), expected end of expression
|
// testdata/errors.go:26:26: unexpected token ), expected end of expression
|
||||||
// testdata/errors.go:16:33: unexpected token ), expected end of expression
|
// testdata/errors.go:21:33: unexpected token ), expected end of expression
|
||||||
// testdata/errors.go:17:34: unexpected token ), expected end of expression
|
// testdata/errors.go:22:34: unexpected token ), expected end of expression
|
||||||
// -: unexpected token INT, expected end of expression
|
// -: unexpected token INT, expected end of expression
|
||||||
// testdata/errors.go:30:35: unexpected number of parameters: expected 2, got 3
|
// testdata/errors.go:35:35: unexpected number of parameters: expected 2, got 3
|
||||||
// testdata/errors.go:31:31: unexpected number of parameters: expected 2, got 1
|
// testdata/errors.go:36:31: unexpected number of parameters: expected 2, got 1
|
||||||
|
// testdata/errors.go:3:1: function "unusedFunction" in #cgo noescape line is not used
|
||||||
|
|
||||||
// Type checking errors after CGo processing:
|
// Type checking errors after CGo processing:
|
||||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||||
@@ -24,6 +27,7 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -55,6 +59,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+8
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -36,6 +37,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+5
@@ -9,6 +9,10 @@ static void staticfunc(int x);
|
|||||||
|
|
||||||
// Global variable signatures.
|
// Global variable signatures.
|
||||||
extern int someValue;
|
extern int someValue;
|
||||||
|
|
||||||
|
void notEscapingFunction(int *a);
|
||||||
|
|
||||||
|
#cgo noescape notEscapingFunction
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
@@ -18,6 +22,7 @@ func accessFunctions() {
|
|||||||
C.variadic0()
|
C.variadic0()
|
||||||
C.variadic2(3, 5)
|
C.variadic2(3, 5)
|
||||||
C.staticfunc(3)
|
C.staticfunc(3)
|
||||||
|
C.notEscapingFunction(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func accessGlobals() {
|
func accessGlobals() {
|
||||||
|
|||||||
Vendored
+13
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -31,6 +32,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
@@ -67,5 +75,10 @@ func C.staticfunc!symbols.go(x C.int)
|
|||||||
|
|
||||||
var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
|
var C.staticfunc!symbols.go$funcaddr unsafe.Pointer
|
||||||
|
|
||||||
|
//export notEscapingFunction
|
||||||
|
//go:noescape
|
||||||
|
func C.notEscapingFunction(a *C.int)
|
||||||
|
|
||||||
|
var C.notEscapingFunction$funcaddr unsafe.Pointer
|
||||||
//go:extern someValue
|
//go:extern someValue
|
||||||
var C.someValue C.int
|
var C.someValue C.int
|
||||||
|
|||||||
Vendored
+8
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -31,6 +32,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
@@ -99,6 +99,11 @@ func (c *Config) BuildTags() []string {
|
|||||||
"math_big_pure_go", // to get math/big to work
|
"math_big_pure_go", // to get math/big to work
|
||||||
"gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package
|
"gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package
|
||||||
"serial." + c.Serial()}...) // used inside the machine package
|
"serial." + c.Serial()}...) // used inside the machine package
|
||||||
|
switch c.Scheduler() {
|
||||||
|
case "threads", "cores":
|
||||||
|
default:
|
||||||
|
tags = append(tags, "tinygo.unicore")
|
||||||
|
}
|
||||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||||
}
|
}
|
||||||
@@ -331,6 +336,7 @@ func (c *Config) CFlags(libclang bool) []string {
|
|||||||
"-isystem", filepath.Join(path, "include"),
|
"-isystem", filepath.Join(path, "include"),
|
||||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||||
|
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
|
||||||
)
|
)
|
||||||
case "musl":
|
case "musl":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
@@ -340,6 +346,7 @@ func (c *Config) CFlags(libclang bool) []string {
|
|||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", filepath.Join(path, "include"),
|
"-isystem", filepath.Join(path, "include"),
|
||||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
|
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
|
||||||
|
"-isystem", filepath.Join(root, "lib", "musl", "arch", "generic"),
|
||||||
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
||||||
)
|
)
|
||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
var (
|
var (
|
||||||
validBuildModeOptions = []string{"default", "c-shared"}
|
validBuildModeOptions = []string{"default", "c-shared"}
|
||||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
validSchedulerOptions = []string{"none", "tasks", "asyncify", "threads", "cores"}
|
||||||
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
validPrintSizeOptions = []string{"none", "short", "full", "html"}
|
||||||
validPanicStrategyOptions = []string{"print", "trap"}
|
validPanicStrategyOptions = []string{"print", "trap"}
|
||||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
func TestVerifyOptions(t *testing.T) {
|
func TestVerifyOptions(t *testing.T) {
|
||||||
|
|
||||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`)
|
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`)
|
||||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
|
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify, threads`)
|
||||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
|
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full, html`)
|
||||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
|||||||
+14
-5
@@ -46,6 +46,7 @@ type TargetSpec struct {
|
|||||||
LinkerScript string `json:"linkerscript,omitempty"`
|
LinkerScript string `json:"linkerscript,omitempty"`
|
||||||
ExtraFiles []string `json:"extra-files,omitempty"`
|
ExtraFiles []string `json:"extra-files,omitempty"`
|
||||||
RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
|
RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
|
||||||
|
BootPatches []string `json:"boot-patches,omitempty"` // Bootloader patches to be applied in the order they appear.
|
||||||
Emulator string `json:"emulator,omitempty"`
|
Emulator string `json:"emulator,omitempty"`
|
||||||
FlashCommand string `json:"flash-command,omitempty"`
|
FlashCommand string `json:"flash-command,omitempty"`
|
||||||
GDB []string `json:"gdb,omitempty"`
|
GDB []string `json:"gdb,omitempty"`
|
||||||
@@ -247,7 +248,6 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
GOARCH: options.GOARCH,
|
GOARCH: options.GOARCH,
|
||||||
BuildTags: []string{options.GOOS, options.GOARCH},
|
BuildTags: []string{options.GOOS, options.GOARCH},
|
||||||
GC: "precise",
|
GC: "precise",
|
||||||
Scheduler: "tasks",
|
|
||||||
Linker: "cc",
|
Linker: "cc",
|
||||||
DefaultStackSize: 1024 * 64, // 64kB
|
DefaultStackSize: 1024 * 64, // 64kB
|
||||||
GDB: []string{"gdb"},
|
GDB: []string{"gdb"},
|
||||||
@@ -327,14 +327,14 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
llvmarch = "aarch64"
|
llvmarch = "aarch64"
|
||||||
if options.GOOS == "darwin" {
|
if options.GOOS == "darwin" {
|
||||||
spec.Features = "+fp-armv8,+neon"
|
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a"
|
||||||
// Looks like Apple prefers to call this architecture ARM64
|
// Looks like Apple prefers to call this architecture ARM64
|
||||||
// instead of AArch64.
|
// instead of AArch64.
|
||||||
llvmarch = "arm64"
|
llvmarch = "arm64"
|
||||||
} else if options.GOOS == "windows" {
|
} else if options.GOOS == "windows" {
|
||||||
spec.Features = "+fp-armv8,+neon,-fmv"
|
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a,-fmv"
|
||||||
} else { // linux
|
} else { // linux
|
||||||
spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
|
spec.Features = "+ete,+fp-armv8,+neon,+trbe,+v8a,-fmv,-outline-atomics"
|
||||||
}
|
}
|
||||||
case "mips", "mipsle":
|
case "mips", "mipsle":
|
||||||
spec.CPU = "mips32"
|
spec.CPU = "mips32"
|
||||||
@@ -357,11 +357,13 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
case "wasm":
|
case "wasm":
|
||||||
llvmarch = "wasm32"
|
llvmarch = "wasm32"
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
|
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types"
|
||||||
spec.BuildTags = append(spec.BuildTags, "tinygo.wasm")
|
spec.BuildTags = append(spec.BuildTags, "tinygo.wasm")
|
||||||
spec.CFlags = append(spec.CFlags,
|
spec.CFlags = append(spec.CFlags,
|
||||||
"-mbulk-memory",
|
"-mbulk-memory",
|
||||||
"-mnontrapping-fptoint",
|
"-mnontrapping-fptoint",
|
||||||
|
"-mno-multivalue",
|
||||||
|
"-mno-reference-types",
|
||||||
"-msign-ext",
|
"-msign-ext",
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
@@ -378,6 +380,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
platformVersion = "11.0.0" // first macosx platform with arm64 support
|
platformVersion = "11.0.0" // first macosx platform with arm64 support
|
||||||
}
|
}
|
||||||
llvmvendor = "apple"
|
llvmvendor = "apple"
|
||||||
|
spec.Scheduler = "tasks"
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
spec.Libc = "darwin-libSystem"
|
spec.Libc = "darwin-libSystem"
|
||||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer to
|
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer to
|
||||||
@@ -390,10 +393,12 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
"-platform_version", "macos", platformVersion, platformVersion,
|
"-platform_version", "macos", platformVersion, platformVersion,
|
||||||
)
|
)
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||||
|
"src/internal/futex/futex_darwin.c",
|
||||||
"src/runtime/os_darwin.c",
|
"src/runtime/os_darwin.c",
|
||||||
"src/runtime/runtime_unix.c",
|
"src/runtime/runtime_unix.c",
|
||||||
"src/runtime/signal.c")
|
"src/runtime/signal.c")
|
||||||
case "linux":
|
case "linux":
|
||||||
|
spec.Scheduler = "threads"
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
spec.RTLib = "compiler-rt"
|
spec.RTLib = "compiler-rt"
|
||||||
spec.Libc = "musl"
|
spec.Libc = "musl"
|
||||||
@@ -413,9 +418,12 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
|
spec.CFlags = append(spec.CFlags, "-mno-outline-atomics")
|
||||||
}
|
}
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||||
|
"src/internal/futex/futex_linux.c",
|
||||||
|
"src/internal/task/task_threads.c",
|
||||||
"src/runtime/runtime_unix.c",
|
"src/runtime/runtime_unix.c",
|
||||||
"src/runtime/signal.c")
|
"src/runtime/signal.c")
|
||||||
case "windows":
|
case "windows":
|
||||||
|
spec.Scheduler = "tasks"
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
spec.Libc = "mingw-w64"
|
spec.Libc = "mingw-w64"
|
||||||
// Note: using a medium code model, low image base and no ASLR
|
// Note: using a medium code model, low image base and no ASLR
|
||||||
@@ -493,6 +501,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
}
|
}
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+asmGoarch+suffix+".S")
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+asmGoarch+suffix+".S")
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+asmGoarch+suffix+".S")
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+asmGoarch+suffix+".S")
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_cores.c")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the emulator.
|
// Configure the emulator.
|
||||||
|
|||||||
+7
-5
@@ -19,8 +19,9 @@ const maxFieldsPerParam = 3
|
|||||||
// useful while declaring or defining a function.
|
// useful while declaring or defining a function.
|
||||||
type paramInfo struct {
|
type paramInfo struct {
|
||||||
llvmType llvm.Type
|
llvmType llvm.Type
|
||||||
name string // name, possibly with suffixes for e.g. struct fields
|
name string // name, possibly with suffixes for e.g. struct fields
|
||||||
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
elemSize uint64 // size of pointer element type, or 0 if this isn't a pointer
|
||||||
|
flags paramFlags // extra flags for this parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
||||||
@@ -28,9 +29,9 @@ type paramInfo struct {
|
|||||||
type paramFlags uint8
|
type paramFlags uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Parameter may have the deferenceable_or_null attribute. This attribute
|
// Whether this is a full or partial Go parameter (int, slice, etc).
|
||||||
// cannot be applied to unsafe.Pointer and to the data pointer of slices.
|
// The extra context parameter is not a Go parameter.
|
||||||
paramIsDeferenceableOrNull = 1 << iota
|
paramIsGoParam = 1 << iota
|
||||||
)
|
)
|
||||||
|
|
||||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||||
@@ -195,6 +196,7 @@ func (c *compilerContext) getParamInfo(t llvm.Type, name string, goType types.Ty
|
|||||||
info := paramInfo{
|
info := paramInfo{
|
||||||
llvmType: t,
|
llvmType: t,
|
||||||
name: name,
|
name: name,
|
||||||
|
flags: paramIsGoParam,
|
||||||
}
|
}
|
||||||
if goType != nil {
|
if goType != nil {
|
||||||
switch underlying := goType.Underlying().(type) {
|
switch underlying := goType.Underlying().(type) {
|
||||||
|
|||||||
+36
-17
@@ -4,7 +4,9 @@ package compiler
|
|||||||
// or pseudo-operations that are lowered during goroutine lowering.
|
// or pseudo-operations that are lowered during goroutine lowering.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
@@ -41,17 +43,17 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
b.CreateStore(chanValue, valueAlloca)
|
b.CreateStore(chanValue, valueAlloca)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate buffer for the channel operation.
|
||||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
channelOp := b.getLLVMRuntimeType("channelOp")
|
||||||
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||||
|
|
||||||
// Do the send.
|
// Do the send.
|
||||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
|
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
||||||
|
|
||||||
// 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(channelBlockedListAlloca, channelBlockedListAllocaSize)
|
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
||||||
if !isZeroSize {
|
if !isZeroSize {
|
||||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||||
}
|
}
|
||||||
@@ -72,12 +74,12 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate buffer for the channel operation.
|
||||||
channelBlockedList := b.getLLVMRuntimeType("channelBlockedList")
|
channelOp := b.getLLVMRuntimeType("channelOp")
|
||||||
channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "")
|
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
||||||
var received llvm.Value
|
var received llvm.Value
|
||||||
if isZeroSize {
|
if isZeroSize {
|
||||||
received = llvm.ConstNull(valueType)
|
received = llvm.ConstNull(valueType)
|
||||||
@@ -85,7 +87,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
|
||||||
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
|
||||||
}
|
}
|
||||||
b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize)
|
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
|
||||||
|
|
||||||
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))
|
||||||
@@ -124,6 +126,20 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxSelectStates = math.MaxUint32 >> 2
|
||||||
|
if len(expr.States) > maxSelectStates {
|
||||||
|
// The runtime code assumes that the number of state must fit in 30 bits
|
||||||
|
// (so the select index can be stored in a uint32 with two bits reserved
|
||||||
|
// for other purposes). It seems unlikely that a real program would have
|
||||||
|
// that many states, but we check for this case anyway to be sure.
|
||||||
|
// We use a uint32 (and not a uintptr or uint64) to avoid 64-bit atomic
|
||||||
|
// operations which aren't available everywhere.
|
||||||
|
b.addError(expr.Pos(), fmt.Sprintf("too many select states: got %d but the maximum supported number is %d", len(expr.States), maxSelectStates))
|
||||||
|
|
||||||
|
// Continue as usual (we'll generate broken code but the error will
|
||||||
|
// prevent the compilation to complete).
|
||||||
|
}
|
||||||
|
|
||||||
// This code create a (stack-allocated) slice containing all the select
|
// This code create a (stack-allocated) slice containing all the select
|
||||||
// cases and then calls runtime.chanSelect to perform the actual select
|
// cases and then calls runtime.chanSelect to perform the actual select
|
||||||
// statement.
|
// statement.
|
||||||
@@ -198,10 +214,10 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
if expr.Blocking {
|
if expr.Blocking {
|
||||||
// Stack-allocate operation structures.
|
// Stack-allocate operation structures.
|
||||||
// If these were simply created as a slice, they would heap-allocate.
|
// If these were simply created as a slice, they would heap-allocate.
|
||||||
chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates))
|
opsAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelOp"), len(selectStates))
|
||||||
chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca")
|
opsAlloca, opsSize := b.createTemporaryAlloca(opsAllocaType, "select.block.alloca")
|
||||||
chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
opsLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false)
|
||||||
chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{
|
opsPtr := b.CreateGEP(opsAllocaType, opsAlloca, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
}, "select.block")
|
}, "select.block")
|
||||||
@@ -209,15 +225,18 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
results = b.createRuntimeCall("chanSelect", []llvm.Value{
|
results = b.createRuntimeCall("chanSelect", []llvm.Value{
|
||||||
recvbuf,
|
recvbuf,
|
||||||
statesPtr, statesLen, statesLen, // []chanSelectState
|
statesPtr, statesLen, statesLen, // []chanSelectState
|
||||||
chBlockPtr, chBlockLen, chBlockLen, // []channelBlockList
|
opsPtr, opsLen, opsLen, // []channelOp
|
||||||
}, "select.result")
|
}, "select.result")
|
||||||
|
|
||||||
// Terminate the lifetime of the operation structures.
|
// Terminate the lifetime of the operation structures.
|
||||||
b.emitLifetimeEnd(chBlockAlloca, chBlockSize)
|
b.emitLifetimeEnd(opsAlloca, opsSize)
|
||||||
} else {
|
} else {
|
||||||
results = b.createRuntimeCall("tryChanSelect", []llvm.Value{
|
opsPtr := llvm.ConstNull(b.dataPtrType)
|
||||||
|
opsLen := llvm.ConstInt(b.uintptrType, 0, false)
|
||||||
|
results = b.createRuntimeCall("chanSelect", []llvm.Value{
|
||||||
recvbuf,
|
recvbuf,
|
||||||
statesPtr, statesLen, statesLen, // []chanSelectState
|
statesPtr, statesLen, statesLen, // []chanSelectState
|
||||||
|
opsPtr, opsLen, opsLen, // []channelOp (nil slice)
|
||||||
}, "select.result")
|
}, "select.result")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1686,6 +1686,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
b.createRuntimeInvoke("_panic", argValues, "")
|
b.createRuntimeInvoke("_panic", argValues, "")
|
||||||
return llvm.Value{}, nil
|
return llvm.Value{}, nil
|
||||||
case "print", "println":
|
case "print", "println":
|
||||||
|
b.createRuntimeCall("printlock", nil, "")
|
||||||
for i, value := range argValues {
|
for i, value := range argValues {
|
||||||
if i >= 1 && callName == "println" {
|
if i >= 1 && callName == "println" {
|
||||||
b.createRuntimeCall("printspace", nil, "")
|
b.createRuntimeCall("printspace", nil, "")
|
||||||
@@ -1746,6 +1747,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
if callName == "println" {
|
if callName == "println" {
|
||||||
b.createRuntimeCall("printnl", nil, "")
|
b.createRuntimeCall("printnl", nil, "")
|
||||||
}
|
}
|
||||||
|
b.createRuntimeCall("printunlock", nil, "")
|
||||||
return llvm.Value{}, nil // print() or println() returns void
|
return llvm.Value{}, nil // print() or println() returns void
|
||||||
case "real":
|
case "real":
|
||||||
cplx := argValues[0]
|
cplx := argValues[0]
|
||||||
|
|||||||
+1
-1
@@ -161,7 +161,7 @@ str x2, [x1, #8]
|
|||||||
mov x0, #0
|
mov x0, #0
|
||||||
1:
|
1:
|
||||||
`
|
`
|
||||||
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
|
constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{memory}"
|
||||||
if b.GOOS != "darwin" && b.GOOS != "windows" {
|
if b.GOOS != "darwin" && b.GOOS != "windows" {
|
||||||
// These registers cause the following warning when compiling for
|
// These registers cause the following warning when compiling for
|
||||||
// MacOS and Windows:
|
// MacOS and Windows:
|
||||||
|
|||||||
+36
-6
@@ -33,6 +33,7 @@ type functionInfo struct {
|
|||||||
exported bool // go:export, CGo
|
exported bool // go:export, CGo
|
||||||
interrupt bool // go:interrupt
|
interrupt bool // go:interrupt
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
|
noescape bool // go:noescape
|
||||||
variadic bool // go:variadic (CGo only)
|
variadic bool // go:variadic (CGo only)
|
||||||
inline inlineType // go:inline
|
inline inlineType // go:inline
|
||||||
}
|
}
|
||||||
@@ -127,11 +128,20 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
c.addStandardDeclaredAttributes(llvmFn)
|
c.addStandardDeclaredAttributes(llvmFn)
|
||||||
|
|
||||||
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
||||||
for i, info := range paramInfos {
|
for i, paramInfo := range paramInfos {
|
||||||
if info.elemSize != 0 {
|
if paramInfo.elemSize != 0 {
|
||||||
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, info.elemSize)
|
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, paramInfo.elemSize)
|
||||||
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
|
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
|
||||||
}
|
}
|
||||||
|
if info.noescape && paramInfo.flags¶mIsGoParam != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||||
|
// Parameters to functions with a //go:noescape parameter should get
|
||||||
|
// the nocapture attribute. However, the context parameter should
|
||||||
|
// not.
|
||||||
|
// (It may be safe to add the nocapture parameter to the context
|
||||||
|
// parameter, but I'd like to stay on the safe side here).
|
||||||
|
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
||||||
|
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a number of function or parameter attributes, depending on the
|
// Set a number of function or parameter attributes, depending on the
|
||||||
@@ -198,6 +208,8 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
// > circumstances, and should not be exposed to source languages.
|
// > circumstances, and should not be exposed to source languages.
|
||||||
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
|
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
|
||||||
}
|
}
|
||||||
|
case "tinygo_exitTask", "tinygo_schedulerUnlock":
|
||||||
|
llvmutil.AppendToGlobal(c.mod, "llvm.used", llvmFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// External/exported functions may not retain pointer values.
|
// External/exported functions may not retain pointer values.
|
||||||
@@ -221,6 +233,15 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the function if needed.
|
||||||
|
c.maybeCreateSyntheticFunction(fn, llvmFn)
|
||||||
|
|
||||||
|
return fnType, llvmFn
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a synthetic function (such as a generic function or a wrapper),
|
||||||
|
// create it now.
|
||||||
|
func (c *compilerContext) maybeCreateSyntheticFunction(fn *ssa.Function, llvmFn llvm.Value) {
|
||||||
// Synthetic functions are functions that do not appear in the source code,
|
// Synthetic functions are functions that do not appear in the source code,
|
||||||
// they are artificially constructed. Usually they are wrapper functions
|
// they are artificially constructed. Usually they are wrapper functions
|
||||||
// that are not referenced anywhere except in a SSA call instruction so
|
// that are not referenced anywhere except in a SSA call instruction so
|
||||||
@@ -228,6 +249,10 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
// The exception is the package initializer, which does appear in the
|
// The exception is the package initializer, which does appear in the
|
||||||
// *ssa.Package members and so shouldn't be created here.
|
// *ssa.Package members and so shouldn't be created here.
|
||||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" && fn.Synthetic != "range-over-func yield" {
|
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" && fn.Synthetic != "range-over-func yield" {
|
||||||
|
if len(fn.Blocks) == 0 {
|
||||||
|
c.addError(fn.Pos(), "missing function body")
|
||||||
|
return
|
||||||
|
}
|
||||||
irbuilder := c.ctx.NewBuilder()
|
irbuilder := c.ctx.NewBuilder()
|
||||||
b := newBuilder(c, irbuilder, fn)
|
b := newBuilder(c, irbuilder, fn)
|
||||||
b.createFunction()
|
b.createFunction()
|
||||||
@@ -235,8 +260,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
|
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
llvmFn.SetUnnamedAddr(true)
|
llvmFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fnType, llvmFn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFunctionInfo returns information about a function that is not directly
|
// getFunctionInfo returns information about a function that is not directly
|
||||||
@@ -345,7 +368,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
c.addError(f.Pos(), fmt.Sprintf("expected one parameter to //go:wasmimport, not %d", len(parts)-1))
|
c.addError(f.Pos(), fmt.Sprintf("expected one parameter to //go:wasmexport, not %d", len(parts)-1))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name := parts[1]
|
name := parts[1]
|
||||||
@@ -394,6 +417,13 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||||
info.nobounds = true
|
info.nobounds = true
|
||||||
}
|
}
|
||||||
|
case "//go:noescape":
|
||||||
|
// Don't let pointer parameters escape.
|
||||||
|
// Following the upstream Go implementation, we only do this for
|
||||||
|
// declarations, not definitions.
|
||||||
|
if len(f.Blocks) == 0 {
|
||||||
|
info.noescape = true
|
||||||
|
}
|
||||||
case "//go:variadic":
|
case "//go:variadic":
|
||||||
// The //go:variadic pragma is emitted by the CGo preprocessing
|
// The //go:variadic pragma is emitted by the CGo preprocessing
|
||||||
// pass for C variadic functions. This includes both explicit
|
// pass for C variadic functions. This includes both explicit
|
||||||
|
|||||||
Vendored
+3
-3
@@ -206,7 +206,7 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+32
-32
@@ -3,7 +3,7 @@ source_filename = "channel.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.channelBlockedList = type { ptr, ptr, ptr, { ptr, i32, i32 } }
|
%runtime.channelOp = type { ptr, ptr, i32, ptr }
|
||||||
%runtime.chanSelectState = type { ptr, ptr }
|
%runtime.chanSelectState = type { ptr, ptr }
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
@@ -18,15 +18,15 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanIntSend(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||||
store i32 3, ptr %chan.value, align 4
|
store i32 3, ptr %chan.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||||
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #4
|
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
@@ -34,61 +34,61 @@ entry:
|
|||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
||||||
|
|
||||||
declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1
|
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
%chan.value = alloca i32, align 4
|
%chan.value = alloca i32, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.blockedList, ptr undef) #4
|
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %chan.value)
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1
|
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #1
|
||||||
|
|
||||||
; 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(36) %ch, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||||
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.op, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
%chan.op = alloca %runtime.channelOp, align 8
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %chan.op)
|
||||||
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #4
|
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr null, ptr nonnull %chan.op, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
|
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %chan.op)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(32) %ch1, ptr dereferenceable_or_null(32) %ch2, ptr %context) unnamed_addr #2 {
|
define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(36) %ch1, ptr dereferenceable_or_null(36) %ch2, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||||
%select.send.value = alloca i32, align 4
|
%select.send.value = alloca i32, align 4
|
||||||
store i32 1, ptr %select.send.value, align 4
|
store i32 1, ptr %select.send.value, align 4
|
||||||
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
|
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||||
store ptr %ch1, ptr %select.states.alloca, align 4
|
store ptr %ch1, ptr %select.states.alloca, align 4
|
||||||
%select.states.alloca.repack1 = getelementptr inbounds %runtime.chanSelectState, ptr %select.states.alloca, i32 0, i32 1
|
%select.states.alloca.repack1 = getelementptr inbounds i8, ptr %select.states.alloca, i32 4
|
||||||
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
|
store ptr %select.send.value, ptr %select.states.alloca.repack1, align 4
|
||||||
%0 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1
|
%0 = getelementptr inbounds i8, ptr %select.states.alloca, i32 8
|
||||||
store ptr %ch2, ptr %0, align 4
|
store ptr %ch2, ptr %0, align 4
|
||||||
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], ptr %select.states.alloca, i32 0, i32 1, i32 1
|
%.repack3 = getelementptr inbounds i8, ptr %select.states.alloca, i32 12
|
||||||
store ptr null, ptr %.repack3, align 4
|
store ptr null, ptr %.repack3, align 4
|
||||||
%select.result = call { i32, i1 } @runtime.tryChanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr undef) #4
|
%select.result = call { i32, i1 } @runtime.chanSelect(ptr undef, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr null, i32 0, i32 0, ptr undef) #4
|
||||||
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
|
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %select.states.alloca)
|
||||||
%1 = extractvalue { i32, i1 } %select.result, 0
|
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||||
%2 = icmp eq i32 %1, 0
|
%2 = icmp eq i32 %1, 0
|
||||||
@@ -105,10 +105,10 @@ select.body: ; preds = %select.next
|
|||||||
br label %select.done
|
br label %select.done
|
||||||
}
|
}
|
||||||
|
|
||||||
declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #1
|
declare { i32, i1 } @runtime.chanSelect(ptr, ptr, i32, i32, ptr, i32, i32, ptr) #1
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #4 = { nounwind }
|
attributes #4 = { nounwind }
|
||||||
|
|||||||
+21
-12
@@ -3,9 +3,8 @@ source_filename = "defer.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||||
|
|
||||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
|
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
|
||||||
%runtime._interface = type { ptr, ptr }
|
%runtime._interface = type { ptr, ptr }
|
||||||
%runtime._defer = type { i32, ptr }
|
|
||||||
|
|
||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||||
@@ -28,7 +27,7 @@ entry:
|
|||||||
%0 = call ptr @llvm.stacksave.p0()
|
%0 = call ptr @llvm.stacksave.p0()
|
||||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||||
store i32 0, ptr %defer.alloca, align 4
|
store i32 0, ptr %defer.alloca, align 4
|
||||||
%defer.alloca.repack15 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
|
%defer.alloca.repack15 = getelementptr inbounds i8, ptr %defer.alloca, i32 4
|
||||||
store ptr null, ptr %defer.alloca.repack15, align 4
|
store ptr null, ptr %defer.alloca.repack15, align 4
|
||||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||||
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
||||||
@@ -52,7 +51,7 @@ rundefers.loophead: ; preds = %3, %rundefers.block
|
|||||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
rundefers.loop: ; preds = %rundefers.loophead
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
|
%stack.next.gep = getelementptr inbounds i8, ptr %2, i32 4
|
||||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||||
store ptr %stack.next, ptr %deferPtr, align 4
|
store ptr %stack.next, ptr %deferPtr, align 4
|
||||||
%callback = load i32, ptr %2, align 4
|
%callback = load i32, ptr %2, align 4
|
||||||
@@ -88,7 +87,7 @@ rundefers.loophead6: ; preds = %5, %lpad
|
|||||||
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
|
br i1 %stackIsNil7, label %rundefers.end3, label %rundefers.loop5
|
||||||
|
|
||||||
rundefers.loop5: ; preds = %rundefers.loophead6
|
rundefers.loop5: ; preds = %rundefers.loophead6
|
||||||
%stack.next.gep8 = getelementptr inbounds %runtime._defer, ptr %4, i32 0, i32 1
|
%stack.next.gep8 = getelementptr inbounds i8, ptr %4, i32 4
|
||||||
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4
|
%stack.next9 = load ptr, ptr %stack.next.gep8, align 4
|
||||||
store ptr %stack.next9, ptr %deferPtr, align 4
|
store ptr %stack.next9, ptr %deferPtr, align 4
|
||||||
%callback11 = load i32, ptr %4, align 4
|
%callback11 = load i32, ptr %4, align 4
|
||||||
@@ -122,12 +121,18 @@ declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||||
|
call void @runtime.printunlock(ptr undef) #4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare void @runtime.printlock(ptr) #2
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #2
|
declare void @runtime.printint32(i32, ptr) #2
|
||||||
|
|
||||||
|
declare void @runtime.printunlock(ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
|
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
@@ -139,11 +144,11 @@ entry:
|
|||||||
%0 = call ptr @llvm.stacksave.p0()
|
%0 = call ptr @llvm.stacksave.p0()
|
||||||
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
|
||||||
store i32 0, ptr %defer.alloca, align 4
|
store i32 0, ptr %defer.alloca, align 4
|
||||||
%defer.alloca.repack22 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1
|
%defer.alloca.repack22 = getelementptr inbounds i8, ptr %defer.alloca, i32 4
|
||||||
store ptr null, ptr %defer.alloca.repack22, align 4
|
store ptr null, ptr %defer.alloca.repack22, align 4
|
||||||
store ptr %defer.alloca, ptr %deferPtr, align 4
|
store ptr %defer.alloca, ptr %deferPtr, align 4
|
||||||
store i32 1, ptr %defer.alloca2, align 4
|
store i32 1, ptr %defer.alloca2, align 4
|
||||||
%defer.alloca2.repack23 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca2, i32 0, i32 1
|
%defer.alloca2.repack23 = getelementptr inbounds i8, ptr %defer.alloca2, i32 4
|
||||||
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4
|
store ptr %defer.alloca, ptr %defer.alloca2.repack23, align 4
|
||||||
store ptr %defer.alloca2, ptr %deferPtr, align 4
|
store ptr %defer.alloca2, ptr %deferPtr, align 4
|
||||||
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
%setjmp = call i32 asm "\0Amovs r0, #0\0Amov r2, pc\0Astr r2, [r1, #4]", "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"(ptr nonnull %deferframe.buf) #5
|
||||||
@@ -167,7 +172,7 @@ rundefers.loophead: ; preds = %4, %3, %rundefers.b
|
|||||||
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
|
||||||
|
|
||||||
rundefers.loop: ; preds = %rundefers.loophead
|
rundefers.loop: ; preds = %rundefers.loophead
|
||||||
%stack.next.gep = getelementptr inbounds %runtime._defer, ptr %2, i32 0, i32 1
|
%stack.next.gep = getelementptr inbounds i8, ptr %2, i32 4
|
||||||
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
%stack.next = load ptr, ptr %stack.next.gep, align 4
|
||||||
store ptr %stack.next, ptr %deferPtr, align 4
|
store ptr %stack.next, ptr %deferPtr, align 4
|
||||||
%callback = load i32, ptr %2, align 4
|
%callback = load i32, ptr %2, align 4
|
||||||
@@ -213,7 +218,7 @@ rundefers.loophead10: ; preds = %7, %6, %lpad
|
|||||||
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9
|
br i1 %stackIsNil11, label %rundefers.end7, label %rundefers.loop9
|
||||||
|
|
||||||
rundefers.loop9: ; preds = %rundefers.loophead10
|
rundefers.loop9: ; preds = %rundefers.loophead10
|
||||||
%stack.next.gep12 = getelementptr inbounds %runtime._defer, ptr %5, i32 0, i32 1
|
%stack.next.gep12 = getelementptr inbounds i8, ptr %5, i32 4
|
||||||
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4
|
%stack.next13 = load ptr, ptr %stack.next.gep12, align 4
|
||||||
store ptr %stack.next13, ptr %deferPtr, align 4
|
store ptr %stack.next13, ptr %deferPtr, align 4
|
||||||
%callback15 = load i32, ptr %5, align 4
|
%callback15 = load i32, ptr %5, align 4
|
||||||
@@ -250,20 +255,24 @@ rundefers.end7: ; preds = %rundefers.loophead1
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 3, ptr undef) #4
|
call void @runtime.printint32(i32 3, ptr undef) #4
|
||||||
|
call void @runtime.printunlock(ptr undef) #4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
|
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
call void @runtime.printlock(ptr undef) #4
|
||||||
call void @runtime.printint32(i32 5, ptr undef) #4
|
call void @runtime.printint32(i32 5, ptr undef) #4
|
||||||
|
call void @runtime.printunlock(ptr undef) #4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #3 = { nocallback nofree nosync nounwind willreturn }
|
attributes #3 = { nocallback nofree nosync nounwind willreturn }
|
||||||
attributes #4 = { nounwind }
|
attributes #4 = { nounwind }
|
||||||
attributes #5 = { nounwind returns_twice }
|
attributes #5 = { nounwind returns_twice }
|
||||||
|
|||||||
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,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
|||||||
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,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+10
-10
@@ -105,18 +105,18 @@ entry:
|
|||||||
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %makeslice, ptr @main.slice1, align 4
|
store ptr %makeslice, ptr @main.slice1, align 4
|
||||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 1), align 4
|
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice1, i32 4), align 4
|
||||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 4
|
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice1, i32 8), align 4
|
||||||
%makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
|
%makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %makeslice1, ptr @main.slice2, align 4
|
store ptr %makeslice1, ptr @main.slice2, align 4
|
||||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 1), align 4
|
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice2, i32 4), align 4
|
||||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 4
|
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice2, i32 8), align 4
|
||||||
%makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3
|
%makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %makeslice3, ptr @main.slice3, align 4
|
store ptr %makeslice3, ptr @main.slice3, align 4
|
||||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 1), align 4
|
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice3, i32 4), align 4
|
||||||
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 4
|
store i32 5, ptr getelementptr inbounds (i8, ptr @main.slice3, i32 8), align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ entry:
|
|||||||
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
|
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store double %v.r, ptr %0, align 8
|
store double %v.r, ptr %0, align 8
|
||||||
%.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
|
%.repack1 = getelementptr inbounds i8, ptr %0, i32 8
|
||||||
store double %v.i, ptr %.repack1, align 8
|
store double %v.i, ptr %.repack1, align 8
|
||||||
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
|
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1
|
||||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #3
|
||||||
@@ -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,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+4
-4
@@ -36,7 +36,7 @@ entry:
|
|||||||
br i1 %4, label %unsafe.String.throw, label %unsafe.String.next
|
br i1 %4, label %unsafe.String.throw, label %unsafe.String.next
|
||||||
|
|
||||||
unsafe.String.next: ; preds = %entry
|
unsafe.String.next: ; preds = %entry
|
||||||
%5 = zext i16 %len to i32
|
%5 = zext nneg i16 %len to i32
|
||||||
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
%6 = insertvalue %runtime._string undef, ptr %ptr, 0
|
||||||
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
%7 = insertvalue %runtime._string %6, i32 %5, 1
|
||||||
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
@@ -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,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+3
-3
@@ -171,9 +171,9 @@ declare i32 @llvm.smax.i32(i32, i32) #4
|
|||||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||||
declare i32 @llvm.umax.i32(i32, i32) #4
|
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 #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||||
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||||
attributes #5 = { nounwind }
|
attributes #5 = { nounwind }
|
||||||
|
|||||||
+29
-23
@@ -65,12 +65,14 @@ entry:
|
|||||||
store i32 3, ptr %n, align 4
|
store i32 3, ptr %n, align 4
|
||||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
store ptr %n, ptr %1, align 4
|
store ptr %n, ptr %1, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
||||||
%2 = load i32, ptr %n, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
|
call void @runtime.printlock(ptr undef) #9
|
||||||
call void @runtime.printint32(i32 %2, ptr undef) #9
|
call void @runtime.printint32(i32 %2, ptr undef) #9
|
||||||
|
call void @runtime.printunlock(ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,22 +87,26 @@ entry:
|
|||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load i32, ptr %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare void @runtime.printlock(ptr) #2
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #2
|
declare void @runtime.printint32(i32, ptr) #2
|
||||||
|
|
||||||
|
declare void @runtime.printunlock(ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
|
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
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 i8, ptr %0, i32 8
|
||||||
store ptr %fn.funcptr, ptr %2, align 4
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #9
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #9
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
||||||
@@ -111,9 +117,9 @@ entry:
|
|||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
|
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load i32, ptr %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
%4 = getelementptr inbounds i8, ptr %0, i32 8
|
||||||
%5 = load ptr, ptr %4, align 4
|
%5 = load ptr, ptr %4, align 4
|
||||||
call void %5(i32 %1, ptr %3) #9
|
call void %5(i32 %1, ptr %3) #9
|
||||||
ret void
|
ret void
|
||||||
@@ -135,24 +141,24 @@ entry:
|
|||||||
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2
|
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #2
|
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
|
||||||
store ptr %itf.value, ptr %0, align 4
|
store ptr %itf.value, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
|
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
store ptr @"main$string", ptr %1, align 4
|
store ptr @"main$string", ptr %1, align 4
|
||||||
%2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
|
%2 = getelementptr inbounds i8, ptr %0, i32 8
|
||||||
store i32 4, ptr %2, align 4
|
store i32 4, ptr %2, align 4
|
||||||
%3 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
|
%3 = getelementptr inbounds i8, ptr %0, i32 12
|
||||||
store ptr %itf.typecode, ptr %3, align 4
|
store ptr %itf.typecode, ptr %3, align 4
|
||||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #9
|
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #9
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
|
||||||
@@ -165,23 +171,23 @@ declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i
|
|||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load ptr, ptr %0, align 4
|
%1 = load ptr, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
|
%4 = getelementptr inbounds i8, ptr %0, i32 8
|
||||||
%5 = load i32, ptr %4, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
|
%6 = getelementptr inbounds i8, ptr %0, i32 12
|
||||||
%7 = load ptr, ptr %6, align 4
|
%7 = load ptr, ptr %6, align 4
|
||||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
|
||||||
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
|
||||||
attributes #7 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
attributes #7 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
attributes #8 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+strict-align,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
attributes #8 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
attributes #9 = { nounwind }
|
attributes #9 = { nounwind }
|
||||||
|
|||||||
+29
-23
@@ -72,11 +72,13 @@ entry:
|
|||||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
store ptr %n, ptr %1, align 4
|
store ptr %n, ptr %1, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||||
%2 = load i32, ptr %n, align 4
|
%2 = load i32, ptr %n, align 4
|
||||||
|
call void @runtime.printlock(ptr undef) #9
|
||||||
call void @runtime.printint32(i32 %2, ptr undef) #9
|
call void @runtime.printint32(i32 %2, ptr undef) #9
|
||||||
|
call void @runtime.printunlock(ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,15 +93,19 @@ entry:
|
|||||||
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
|
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(ptr %0) unnamed_addr #5 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load i32, ptr %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
||||||
call void @runtime.deadlock(ptr undef) #9
|
call void @runtime.deadlock(ptr undef) #9
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare void @runtime.printlock(ptr) #1
|
||||||
|
|
||||||
declare void @runtime.printint32(i32, ptr) #1
|
declare void @runtime.printint32(i32, ptr) #1
|
||||||
|
|
||||||
|
declare void @runtime.printunlock(ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
|
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
@@ -107,9 +113,9 @@ entry:
|
|||||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
||||||
store i32 5, ptr %0, align 4
|
store i32 5, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
|
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
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 i8, ptr %0, i32 8
|
||||||
store ptr %fn.funcptr, ptr %2, align 4
|
store ptr %fn.funcptr, ptr %2, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
@@ -119,9 +125,9 @@ entry:
|
|||||||
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
|
define linkonce_odr void @main.funcGoroutine.gowrapper(ptr %0) unnamed_addr #6 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load i32, ptr %0, align 4
|
%1 = load i32, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
%4 = getelementptr inbounds i8, ptr %0, i32 8
|
||||||
%5 = load ptr, ptr %4, align 4
|
%5 = load ptr, ptr %4, align 4
|
||||||
call void %5(i32 %1, ptr %3) #9
|
call void %5(i32 %1, ptr %3) #9
|
||||||
call void @runtime.deadlock(ptr undef) #9
|
call void @runtime.deadlock(ptr undef) #9
|
||||||
@@ -144,13 +150,13 @@ entry:
|
|||||||
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
|
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
|
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #1
|
declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||||
@@ -159,11 +165,11 @@ entry:
|
|||||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
|
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
|
||||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
|
||||||
store ptr %itf.value, ptr %0, align 4
|
store ptr %itf.value, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
|
%1 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
store ptr @"main$string", ptr %1, align 4
|
store ptr @"main$string", ptr %1, align 4
|
||||||
%2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
|
%2 = getelementptr inbounds i8, ptr %0, i32 8
|
||||||
store i32 4, ptr %2, align 4
|
store i32 4, ptr %2, align 4
|
||||||
%3 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
|
%3 = getelementptr inbounds i8, ptr %0, i32 12
|
||||||
store ptr %itf.typecode, ptr %3, align 4
|
store ptr %itf.typecode, ptr %3, align 4
|
||||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||||
ret void
|
ret void
|
||||||
@@ -175,24 +181,24 @@ declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i
|
|||||||
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
|
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
|
||||||
entry:
|
entry:
|
||||||
%1 = load ptr, ptr %0, align 4
|
%1 = load ptr, ptr %0, align 4
|
||||||
%2 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds i8, ptr %0, i32 4
|
||||||
%3 = load ptr, ptr %2, align 4
|
%3 = load ptr, ptr %2, align 4
|
||||||
%4 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 2
|
%4 = getelementptr inbounds i8, ptr %0, i32 8
|
||||||
%5 = load i32, ptr %4, align 4
|
%5 = load i32, ptr %4, align 4
|
||||||
%6 = getelementptr inbounds { ptr, ptr, i32, ptr }, ptr %0, i32 0, i32 3
|
%6 = getelementptr inbounds i8, ptr %0, i32 12
|
||||||
%7 = load ptr, ptr %6, align 4
|
%7 = load ptr, ptr %6, align 4
|
||||||
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
|
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
|
||||||
call void @runtime.deadlock(ptr undef) #9
|
call void @runtime.deadlock(ptr undef) #9
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
|
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.regularFunction" }
|
||||||
attributes #4 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
attributes #4 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
|
||||||
attributes #5 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
attributes #5 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
|
||||||
attributes #6 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
|
attributes #6 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
|
||||||
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 #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
|
||||||
attributes #8 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+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,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
|
||||||
attributes #9 = { nounwind }
|
attributes #9 = { nounwind }
|
||||||
|
|||||||
Vendored
+7
-7
@@ -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,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
|
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
|
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-methods"="reflect/methods.String() string" }
|
||||||
attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||||
attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+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,-multivalue,-reference-types" "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,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+9
@@ -106,3 +106,12 @@ var undefinedGlobalNotInSection uint32
|
|||||||
//go:align 1024
|
//go:align 1024
|
||||||
//go:section .global_section
|
//go:section .global_section
|
||||||
var multipleGlobalPragmas uint32
|
var multipleGlobalPragmas uint32
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func doesNotEscapeParam(a *int, b []int, c chan int, d *[0]byte)
|
||||||
|
|
||||||
|
// The //go:noescape pragma only works on declarations, not definitions.
|
||||||
|
//
|
||||||
|
//go:noescape
|
||||||
|
func stillEscapes(a *int, b []int, c chan int, d *[0]byte) {
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+18
-10
@@ -85,13 +85,21 @@ entry:
|
|||||||
|
|
||||||
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,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
declare void @main.doesNotEscapeParam(ptr nocapture dereferenceable_or_null(4), ptr nocapture, i32, i32, ptr nocapture dereferenceable_or_null(36), ptr nocapture, ptr) #1
|
||||||
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" }
|
; Function Attrs: nounwind
|
||||||
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" }
|
define hidden void @main.stillEscapes(ptr dereferenceable_or_null(4) %a, ptr %b.data, i32 %b.len, i32 %b.cap, ptr dereferenceable_or_null(36) %c, ptr %d, ptr %context) unnamed_addr #2 {
|
||||||
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
entry:
|
||||||
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
ret void
|
||||||
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" }
|
}
|
||||||
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 #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #9 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exported" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
|
||||||
|
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exportedFunctionInSection" }
|
||||||
|
attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
|
||||||
|
attributes #8 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
|
||||||
|
attributes #9 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="exported" }
|
||||||
|
|||||||
Vendored
+7
-7
@@ -51,9 +51,9 @@ entry:
|
|||||||
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store i32 1, ptr %varargs, align 4
|
store i32 1, ptr %varargs, align 4
|
||||||
%0 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 1
|
%0 = getelementptr inbounds i8, ptr %varargs, i32 4
|
||||||
store i32 2, ptr %0, align 4
|
store i32 2, ptr %0, align 4
|
||||||
%1 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 2
|
%1 = getelementptr inbounds i8, ptr %varargs, i32 8
|
||||||
store i32 3, ptr %1, align 4
|
store i32 3, ptr %1, align 4
|
||||||
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr undef) #3
|
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr undef) #3
|
||||||
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
|
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
|
||||||
@@ -286,7 +286,7 @@ entry:
|
|||||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
unsafe.Slice.next: ; preds = %entry
|
||||||
%5 = trunc i64 %len to i32
|
%5 = trunc nuw i64 %len to i32
|
||||||
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
||||||
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
||||||
@@ -310,7 +310,7 @@ entry:
|
|||||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
unsafe.Slice.next: ; preds = %entry
|
unsafe.Slice.next: ; preds = %entry
|
||||||
%5 = trunc i64 %len to i32
|
%5 = trunc nuw i64 %len to i32
|
||||||
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||||
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
||||||
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
|
||||||
@@ -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,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
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,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { nounwind }
|
attributes #3 = { nounwind }
|
||||||
|
|||||||
Vendored
+6
-6
@@ -81,7 +81,7 @@ entry:
|
|||||||
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.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
||||||
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
||||||
%hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
|
%hashmap.key.repack1 = getelementptr inbounds i8, ptr %hashmap.key, i32 12
|
||||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, 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
|
||||||
@@ -109,7 +109,7 @@ entry:
|
|||||||
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.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
||||||
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
store %main.hasPadding %s.elt, ptr %hashmap.key, align 4
|
||||||
%hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
|
%hashmap.key.repack1 = getelementptr inbounds i8, ptr %hashmap.key, i32 12
|
||||||
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
|
||||||
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, 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
|
||||||
@@ -132,9 +132,9 @@ entry:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+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,-multivalue,-reference-types" }
|
||||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||||
attributes #5 = { nounwind }
|
attributes #5 = { nounwind }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module github.com/tinygo-org/tinygo
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aykevl/go-wasm v0.0.2-0.20240312204833-50275154210c
|
github.com/aykevl/go-wasm v0.0.2-0.20240825160117-b76c3f9f0982
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||||
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee
|
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee
|
||||||
github.com/chromedp/chromedp v0.7.6
|
github.com/chromedp/chromedp v0.7.6
|
||||||
@@ -20,7 +20,7 @@ require (
|
|||||||
golang.org/x/sys v0.21.0
|
golang.org/x/sys v0.21.0
|
||||||
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504
|
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8
|
tinygo.org/x/go-llvm v0.0.0-20250119132755-9dca92dfb4f9
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
github.com/aykevl/go-wasm v0.0.2-0.20240312204833-50275154210c h1:4T0Vj1UkGgcpkRrmn7SbokebnlfxJcMZPgWtOYACAAA=
|
github.com/aykevl/go-wasm v0.0.2-0.20240825160117-b76c3f9f0982 h1:cD7QfvrJdYmBw2tFP/VyKPT8ZESlcrwSwo7SvH9Y4dc=
|
||||||
github.com/aykevl/go-wasm v0.0.2-0.20240312204833-50275154210c/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
github.com/aykevl/go-wasm v0.0.2-0.20240825160117-b76c3f9f0982/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||||
github.com/chromedp/cdproto v0.0.0-20211126220118-81fa0469ad77/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
github.com/chromedp/cdproto v0.0.0-20211126220118-81fa0469ad77/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
||||||
@@ -74,5 +74,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8 h1:bLsZXRUBavt++CJlMN7sppNziqu3LyamESLhFJcpqFQ=
|
tinygo.org/x/go-llvm v0.0.0-20250119132755-9dca92dfb4f9 h1:rMvEzuCYjyiR+pmdiCVWTQw3L6VqiSIXoL19I3lYufE=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
tinygo.org/x/go-llvm v0.0.0-20250119132755-9dca92dfb4f9/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
// Version of TinyGo.
|
// Version of TinyGo.
|
||||||
// Update this value before release of new version of software.
|
// Update this value before release of new version of software.
|
||||||
const version = "0.35.0-dev"
|
const version = "0.36.0-dev"
|
||||||
|
|
||||||
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
||||||
// (like 0.30.0-dev-abcd012).
|
// (like 0.30.0-dev-abcd012).
|
||||||
|
|||||||
@@ -287,9 +287,17 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// Get the object layout, if it is available.
|
// Get the object layout, if it is available.
|
||||||
llvmLayoutType := r.getLLVMTypeFromLayout(operands[2])
|
llvmLayoutType := r.getLLVMTypeFromLayout(operands[2])
|
||||||
|
|
||||||
|
// Get the alignment of the memory to be allocated.
|
||||||
|
alignment := 0 // use default alignment if unset
|
||||||
|
alignAttr := inst.llvmInst.GetCallSiteEnumAttribute(0, llvm.AttributeKindID("align"))
|
||||||
|
if !alignAttr.IsNil() {
|
||||||
|
alignment = int(alignAttr.GetEnumValue())
|
||||||
|
}
|
||||||
|
|
||||||
// Create the object.
|
// Create the object.
|
||||||
alloc := object{
|
alloc := object{
|
||||||
globalName: r.pkgName + "$alloc",
|
globalName: r.pkgName + "$alloc",
|
||||||
|
align: alignment,
|
||||||
llvmLayoutType: llvmLayoutType,
|
llvmLayoutType: llvmLayoutType,
|
||||||
buffer: newRawValue(uint32(size)),
|
buffer: newRawValue(uint32(size)),
|
||||||
size: uint32(size),
|
size: uint32(size),
|
||||||
@@ -646,6 +654,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
globalName: r.pkgName + "$alloca",
|
globalName: r.pkgName + "$alloca",
|
||||||
buffer: newRawValue(uint32(size)),
|
buffer: newRawValue(uint32(size)),
|
||||||
size: uint32(size),
|
size: uint32(size),
|
||||||
|
align: inst.llvmInst.Alignment(),
|
||||||
}
|
}
|
||||||
index := len(r.objects)
|
index := len(r.objects)
|
||||||
r.objects = append(r.objects, alloca)
|
r.objects = append(r.objects, alloca)
|
||||||
|
|||||||
+11
-6
@@ -42,6 +42,7 @@ type object struct {
|
|||||||
globalName string // name, if not yet created (not guaranteed to be the final name)
|
globalName string // name, if not yet created (not guaranteed to be the final name)
|
||||||
buffer value // buffer with value as given by interp, nil if external
|
buffer value // buffer with value as given by interp, nil if external
|
||||||
size uint32 // must match buffer.len(), if available
|
size uint32 // must match buffer.len(), if available
|
||||||
|
align int // alignment of the object (may be 0 if unknown)
|
||||||
constant bool // true if this is a constant global
|
constant bool // true if this is a constant global
|
||||||
marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
|
marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
|
||||||
}
|
}
|
||||||
@@ -593,6 +594,12 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
// runtime.alloc.
|
// runtime.alloc.
|
||||||
// First allocate a new global for this object.
|
// First allocate a new global for this object.
|
||||||
obj := mem.get(v.index())
|
obj := mem.get(v.index())
|
||||||
|
alignment := obj.align
|
||||||
|
if alignment == 0 {
|
||||||
|
// Unknown alignment, perhaps from a direct call to runtime.alloc in
|
||||||
|
// the runtime. Use a conservative default instead.
|
||||||
|
alignment = mem.r.maxAlign
|
||||||
|
}
|
||||||
if obj.llvmType.IsNil() && obj.llvmLayoutType.IsNil() {
|
if obj.llvmType.IsNil() && obj.llvmLayoutType.IsNil() {
|
||||||
// Create an initializer without knowing the global type.
|
// Create an initializer without knowing the global type.
|
||||||
// This is probably the result of a runtime.alloc call.
|
// This is probably the result of a runtime.alloc call.
|
||||||
@@ -603,7 +610,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
globalType := initializer.Type()
|
globalType := initializer.Type()
|
||||||
llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
|
llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
|
||||||
llvmValue.SetInitializer(initializer)
|
llvmValue.SetInitializer(initializer)
|
||||||
llvmValue.SetAlignment(mem.r.maxAlign)
|
llvmValue.SetAlignment(alignment)
|
||||||
obj.llvmGlobal = llvmValue
|
obj.llvmGlobal = llvmValue
|
||||||
mem.put(v.index(), obj)
|
mem.put(v.index(), obj)
|
||||||
} else {
|
} else {
|
||||||
@@ -642,11 +649,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
return llvm.Value{}, errors.New("interp: allocated value does not match allocated type")
|
return llvm.Value{}, errors.New("interp: allocated value does not match allocated type")
|
||||||
}
|
}
|
||||||
llvmValue.SetInitializer(initializer)
|
llvmValue.SetInitializer(initializer)
|
||||||
if obj.llvmType.IsNil() {
|
llvmValue.SetAlignment(alignment)
|
||||||
// The exact type isn't known (only the layout), so use the
|
|
||||||
// alignment that would normally be expected from runtime.alloc.
|
|
||||||
llvmValue.SetAlignment(mem.r.maxAlign)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// It should be included in r.globals because otherwise markExternal
|
// It should be included in r.globals because otherwise markExternal
|
||||||
@@ -1043,6 +1046,8 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
|
|||||||
v.buf[i] = ptrValue.pointer
|
v.buf[i] = ptrValue.pointer
|
||||||
}
|
}
|
||||||
case llvm.ICmp:
|
case llvm.ICmp:
|
||||||
|
// Note: constant icmp isn't supported anymore in LLVM 19.
|
||||||
|
// Once we drop support for LLVM 18, this can be removed.
|
||||||
size := r.targetData.TypeAllocSize(llvmValue.Operand(0).Type())
|
size := r.targetData.TypeAllocSize(llvmValue.Operand(0).Type())
|
||||||
lhs := newRawValue(uint32(size))
|
lhs := newRawValue(uint32(size))
|
||||||
rhs := newRawValue(uint32(size))
|
rhs := newRawValue(uint32(size))
|
||||||
|
|||||||
Vendored
-15
@@ -3,7 +3,6 @@ target triple = "x86_64--linux"
|
|||||||
|
|
||||||
@intToPtrResult = global i8 0
|
@intToPtrResult = global i8 0
|
||||||
@ptrToIntResult = global i8 0
|
@ptrToIntResult = global i8 0
|
||||||
@icmpResult = global i8 0
|
|
||||||
@pointerTagResult = global i64 0
|
@pointerTagResult = global i64 0
|
||||||
@someArray = internal global {i16, i8, i8} zeroinitializer
|
@someArray = internal global {i16, i8, i8} zeroinitializer
|
||||||
@someArrayPointer = global ptr zeroinitializer
|
@someArrayPointer = global ptr zeroinitializer
|
||||||
@@ -17,7 +16,6 @@ define internal void @main.init() {
|
|||||||
call void @testIntToPtr()
|
call void @testIntToPtr()
|
||||||
call void @testPtrToInt()
|
call void @testPtrToInt()
|
||||||
call void @testConstGEP()
|
call void @testConstGEP()
|
||||||
call void @testICmp()
|
|
||||||
call void @testPointerTag()
|
call void @testPointerTag()
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
@@ -53,19 +51,6 @@ define internal void @testConstGEP() {
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal void @testICmp() {
|
|
||||||
br i1 icmp eq (i64 ptrtoint (ptr @ptrToIntResult to i64), i64 0), label %equal, label %unequal
|
|
||||||
equal:
|
|
||||||
; should not be reached
|
|
||||||
store i8 1, ptr @icmpResult
|
|
||||||
ret void
|
|
||||||
unequal:
|
|
||||||
; should be reached
|
|
||||||
store i8 2, ptr @icmpResult
|
|
||||||
ret void
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
define internal void @testPointerTag() {
|
define internal void @testPointerTag() {
|
||||||
%val = and i64 ptrtoint (ptr getelementptr inbounds (i8, ptr @someArray, i32 2) to i64), 3
|
%val = and i64 ptrtoint (ptr getelementptr inbounds (i8, ptr @someArray, i32 2) to i64), 3
|
||||||
store i64 %val, ptr @pointerTagResult
|
store i64 %val, ptr @pointerTagResult
|
||||||
|
|||||||
Vendored
+1
-2
@@ -3,10 +3,9 @@ target triple = "x86_64--linux"
|
|||||||
|
|
||||||
@intToPtrResult = local_unnamed_addr global i8 2
|
@intToPtrResult = local_unnamed_addr global i8 2
|
||||||
@ptrToIntResult = local_unnamed_addr global i8 2
|
@ptrToIntResult = local_unnamed_addr global i8 2
|
||||||
@icmpResult = local_unnamed_addr global i8 2
|
|
||||||
@pointerTagResult = local_unnamed_addr global i64 2
|
@pointerTagResult = local_unnamed_addr global i64 2
|
||||||
@someArray = internal global { i16, i8, i8 } zeroinitializer
|
@someArray = internal global { i16, i8, i8 } zeroinitializer
|
||||||
@someArrayPointer = local_unnamed_addr global ptr getelementptr inbounds ({ i16, i8, i8 }, ptr @someArray, i64 0, i32 1)
|
@someArrayPointer = local_unnamed_addr global ptr getelementptr inbounds (i8, ptr @someArray, i64 2)
|
||||||
|
|
||||||
define void @runtime.initAll() local_unnamed_addr {
|
define void @runtime.initAll() local_unnamed_addr {
|
||||||
ret void
|
ret void
|
||||||
|
|||||||
+1
-1
Submodule lib/cmsis-svd updated: 40327a4d2d...05a9562ec5
+1
-1
Submodule lib/macos-minimal-sdk updated: 4e4113e3b1...9b69407cb5
@@ -243,6 +243,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
|||||||
"internal/binary/": false,
|
"internal/binary/": false,
|
||||||
"internal/bytealg/": false,
|
"internal/bytealg/": false,
|
||||||
"internal/cm/": false,
|
"internal/cm/": false,
|
||||||
|
"internal/futex/": false,
|
||||||
"internal/fuzz/": false,
|
"internal/fuzz/": false,
|
||||||
"internal/reflectlite/": false,
|
"internal/reflectlite/": false,
|
||||||
"internal/gclayout": false,
|
"internal/gclayout": false,
|
||||||
|
|||||||
+1
-1
@@ -485,7 +485,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
var initialCFlags []string
|
var initialCFlags []string
|
||||||
initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...)
|
initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...)
|
||||||
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||||
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags)
|
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.config.GOOS())
|
||||||
p.CFlags = append(initialCFlags, cflags...)
|
p.CFlags = append(initialCFlags, cflags...)
|
||||||
p.CGoHeaders = headerCode
|
p.CGoHeaders = headerCode
|
||||||
for path, hash := range accessedFiles {
|
for path, hash := range accessedFiles {
|
||||||
|
|||||||
@@ -1062,9 +1062,8 @@ func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
|
|||||||
return points, nil
|
return points, nil
|
||||||
case "windows":
|
case "windows":
|
||||||
// Obtain a list of all currently mounted volumes.
|
// Obtain a list of all currently mounted volumes.
|
||||||
cmd := executeCommand(options, "wmic",
|
cmd := executeCommand(options, "powershell", "-c",
|
||||||
"PATH", "Win32_LogicalDisk",
|
"Get-CimInstance -ClassName Win32_LogicalDisk | Select-Object DeviceID, DriveType, FileSystem, VolumeName")
|
||||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
@@ -1509,7 +1508,7 @@ func main() {
|
|||||||
stackSize = uint64(size)
|
stackSize = uint64(size)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
printSize := flag.String("size", "", "print sizes (none, short, full, html)")
|
||||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||||
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
||||||
printCommands := flag.Bool("x", false, "Print commands")
|
printCommands := flag.Bool("x", false, "Print commands")
|
||||||
|
|||||||
+17
-68
@@ -7,7 +7,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"io"
|
"io"
|
||||||
@@ -16,7 +15,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -29,6 +27,7 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/sys"
|
"github.com/tetratelabs/wazero/sys"
|
||||||
"github.com/tinygo-org/tinygo/builder"
|
"github.com/tinygo-org/tinygo/builder"
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/diagnostics"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -115,15 +114,10 @@ func TestBuild(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Debugging", func(t *testing.T) {
|
t.Run("Host", func(t *testing.T) {
|
||||||
for i := 0; i < 5; i++ {
|
t.Parallel()
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
runPlatTests(optionsFromTarget("", sema), tests, t)
|
||||||
options := optionsFromTarget("", sema)
|
|
||||||
runTest("alias.go", options, t, nil, nil)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return
|
|
||||||
|
|
||||||
// Test a few build options.
|
// Test a few build options.
|
||||||
t.Run("build-options", func(t *testing.T) {
|
t.Run("build-options", func(t *testing.T) {
|
||||||
@@ -426,67 +420,22 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
|
|
||||||
// Build the test binary.
|
// Build the test binary.
|
||||||
stdout := &bytes.Buffer{}
|
stdout := &bytes.Buffer{}
|
||||||
_, fileExt := config.EmulatorFormat()
|
_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||||
tmpdir := t.TempDir()
|
return cmd.Run()
|
||||||
result, err := builder.Build(pkgName, fileExt, tmpdir, config)
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("build failed:", err)
|
w := &bytes.Buffer{}
|
||||||
|
diagnostics.CreateDiagnostics(err).WriteTo(w, "")
|
||||||
|
for _, line := range strings.Split(strings.TrimRight(w.String(), "\n"), "\n") {
|
||||||
|
t.Log(line)
|
||||||
|
}
|
||||||
|
if stdout.Len() != 0 {
|
||||||
|
t.Logf("output:\n%s", stdout.String())
|
||||||
|
}
|
||||||
|
t.Fail()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := os.ReadFile(result.Executable)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("failed to read executable:", err)
|
|
||||||
}
|
|
||||||
hash := sha256.Sum256(data)
|
|
||||||
t.Logf("executable hash and size: %d %x", len(data), hash)
|
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
i := i
|
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
stdout := &bytes.Buffer{}
|
|
||||||
cmd := exec.Command(result.Executable)
|
|
||||||
cmd.Stdout = stdout
|
|
||||||
cmd.Stderr = stdout
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
t.Log("run error:", err)
|
|
||||||
}
|
|
||||||
checkOutput(t, expectedOutputPath, stdout.Bytes())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
|
|
||||||
//_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
|
||||||
// data, err := os.ReadFile(result.Executable)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatal("failed to read executable:", err)
|
|
||||||
// }
|
|
||||||
// hash := sha256.Sum256(data)
|
|
||||||
// t.Logf("executable hash and size: %d %x", len(data), hash)
|
|
||||||
|
|
||||||
// t.Log("command:", cmd)
|
|
||||||
// err = cmd.Run()
|
|
||||||
// if err == nil {
|
|
||||||
// t.Log(" error is nil!")
|
|
||||||
// } else {
|
|
||||||
// t.Log(" error:", err)
|
|
||||||
// }
|
|
||||||
// return err
|
|
||||||
//})
|
|
||||||
//if err != nil {
|
|
||||||
// w := &bytes.Buffer{}
|
|
||||||
// diagnostics.CreateDiagnostics(err).WriteTo(w, "")
|
|
||||||
// for _, line := range strings.Split(strings.TrimRight(w.String(), "\n"), "\n") {
|
|
||||||
// t.Log(line)
|
|
||||||
// }
|
|
||||||
// if stdout.Len() != 0 {
|
|
||||||
// t.Logf("output:\n%s", stdout.String())
|
|
||||||
// }
|
|
||||||
// t.Fail()
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
|
|
||||||
actual := stdout.Bytes()
|
actual := stdout.Bytes()
|
||||||
if config.EmulatorName() == "simavr" {
|
if config.EmulatorName() == "simavr" {
|
||||||
// Strip simavr log formatting.
|
// Strip simavr log formatting.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3
|
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey
|
||||||
|
|
||||||
// If you update the above build constraint, you'll probably also need to update
|
// If you update the above build constraint, you'll probably also need to update
|
||||||
// src/runtime/rand_hwrng.go.
|
// src/runtime/rand_hwrng.go.
|
||||||
|
|||||||
@@ -101,7 +101,13 @@ type Dialer struct {
|
|||||||
//
|
//
|
||||||
// The returned Conn, if any, will always be of type *Conn.
|
// The returned Conn, if any, will always be of type *Conn.
|
||||||
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
return nil, errors.New("tls:DialContext not implemented")
|
switch network {
|
||||||
|
case "tcp", "tcp4":
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Network %s not supported", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.DialTLS(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadX509KeyPair reads and parses a public/private key pair from a pair
|
// LoadX509KeyPair reads and parses a public/private key pair from a pair
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
//go:build tkey
|
||||||
|
|
||||||
|
// Hand written file based on https://github.com/tillitis/tkey-libs/blob/main/include/tkey/tk1_mem.h
|
||||||
|
|
||||||
|
package tkey
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Peripherals
|
||||||
|
var (
|
||||||
|
TRNG = (*TRNG_Type)(unsafe.Pointer(TK1_MMIO_TRNG_BASE))
|
||||||
|
|
||||||
|
TIMER = (*TIMER_Type)(unsafe.Pointer(TK1_MMIO_TIMER_BASE))
|
||||||
|
|
||||||
|
UDS = (*UDS_Type)(unsafe.Pointer(TK1_MMIO_UDS_BASE))
|
||||||
|
|
||||||
|
UART = (*UART_Type)(unsafe.Pointer(TK1_MMIO_UART_BASE))
|
||||||
|
|
||||||
|
TOUCH = (*TOUCH_Type)(unsafe.Pointer(TK1_MMIO_TOUCH_BASE))
|
||||||
|
|
||||||
|
TK1 = (*TK1_Type)(unsafe.Pointer(TK1_MMIO_TK1_BASE))
|
||||||
|
)
|
||||||
|
|
||||||
|
// Memory sections
|
||||||
|
const (
|
||||||
|
TK1_ROM_BASE uintptr = 0x00000000
|
||||||
|
|
||||||
|
TK1_RAM_BASE uintptr = 0x40000000
|
||||||
|
|
||||||
|
TK1_MMIO_BASE uintptr = 0xc0000000
|
||||||
|
|
||||||
|
TK1_MMIO_TRNG_BASE uintptr = 0xc0000000
|
||||||
|
|
||||||
|
TK1_MMIO_TIMER_BASE uintptr = 0xc1000000
|
||||||
|
|
||||||
|
TK1_MMIO_UDS_BASE uintptr = 0xc2000000
|
||||||
|
|
||||||
|
TK1_MMIO_UART_BASE uintptr = 0xc3000000
|
||||||
|
|
||||||
|
TK1_MMIO_TOUCH_BASE uintptr = 0xc4000000
|
||||||
|
|
||||||
|
TK1_MMIO_FW_RAM_BASE uintptr = 0xd0000000
|
||||||
|
|
||||||
|
TK1_MMIO_TK1_BASE uintptr = 0xff000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// Memory section sizes
|
||||||
|
const (
|
||||||
|
TK1_RAM_SIZE uintptr = 0x20000
|
||||||
|
|
||||||
|
TK1_MMIO_SIZE uintptr = 0x3fffffff
|
||||||
|
)
|
||||||
|
|
||||||
|
type TRNG_Type struct {
|
||||||
|
_ [36]byte
|
||||||
|
STATUS volatile.Register32
|
||||||
|
_ [88]byte
|
||||||
|
ENTROPY volatile.Register32
|
||||||
|
}
|
||||||
|
|
||||||
|
type TIMER_Type struct {
|
||||||
|
_ [32]byte
|
||||||
|
CTRL volatile.Register32
|
||||||
|
STATUS volatile.Register32
|
||||||
|
PRESCALER volatile.Register32
|
||||||
|
TIMER volatile.Register32
|
||||||
|
}
|
||||||
|
|
||||||
|
type UDS_Type struct {
|
||||||
|
_ [64]byte
|
||||||
|
DATA [8]volatile.Register32
|
||||||
|
}
|
||||||
|
|
||||||
|
type UART_Type struct {
|
||||||
|
_ [128]byte
|
||||||
|
RX_STATUS volatile.Register32
|
||||||
|
RX_DATA volatile.Register32
|
||||||
|
RX_BYTES volatile.Register32
|
||||||
|
_ [116]byte
|
||||||
|
TX_STATUS volatile.Register32
|
||||||
|
TX_DATA volatile.Register32
|
||||||
|
}
|
||||||
|
|
||||||
|
type TOUCH_Type struct {
|
||||||
|
_ [36]byte
|
||||||
|
STATUS volatile.Register32
|
||||||
|
}
|
||||||
|
|
||||||
|
type TK1_Type struct {
|
||||||
|
NAME0 volatile.Register32
|
||||||
|
NAME1 volatile.Register32
|
||||||
|
VERSION volatile.Register32
|
||||||
|
_ [16]byte
|
||||||
|
SWITCH_APP volatile.Register32
|
||||||
|
_ [4]byte
|
||||||
|
LED volatile.Register32
|
||||||
|
GPIO volatile.Register16
|
||||||
|
APP_ADDR volatile.Register32
|
||||||
|
APP_SIZE volatile.Register32
|
||||||
|
BLAKE2S volatile.Register32
|
||||||
|
_ [72]byte
|
||||||
|
CDI_FIRST [8]volatile.Register32
|
||||||
|
_ [32]byte
|
||||||
|
UDI_FIRST [2]volatile.Register32
|
||||||
|
_ [62]byte
|
||||||
|
RAM_ADDR_RAND volatile.Register16
|
||||||
|
_ [2]byte
|
||||||
|
RAM_DATA_RAND volatile.Register16
|
||||||
|
_ [126]byte
|
||||||
|
CPU_MON_CTRL volatile.Register16
|
||||||
|
_ [2]byte
|
||||||
|
CPU_MON_FIRST volatile.Register32
|
||||||
|
CPU_MON_LAST volatile.Register32
|
||||||
|
_ [60]byte
|
||||||
|
SYSTEM_RESET volatile.Register16
|
||||||
|
_ [66]byte
|
||||||
|
SPI_EN volatile.Register32
|
||||||
|
SPI_XFER volatile.Register32
|
||||||
|
SPI_DATA volatile.Register32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TK1_MMIO_TIMER_CTRL_START_BIT = 0
|
||||||
|
TK1_MMIO_TIMER_CTRL_STOP_BIT = 1
|
||||||
|
TK1_MMIO_TIMER_CTRL_START = 1 << TK1_MMIO_TIMER_CTRL_START_BIT
|
||||||
|
TK1_MMIO_TIMER_CTRL_STOP = 1 << TK1_MMIO_TIMER_CTRL_STOP_BIT
|
||||||
|
|
||||||
|
TK1_MMIO_TK1_LED_R_BIT = 2
|
||||||
|
TK1_MMIO_TK1_LED_G_BIT = 1
|
||||||
|
TK1_MMIO_TK1_LED_B_BIT = 0
|
||||||
|
|
||||||
|
TK1_MMIO_TK1_GPIO1_BIT = 0
|
||||||
|
TK1_MMIO_TK1_GPIO2_BIT = 1
|
||||||
|
TK1_MMIO_TK1_GPIO3_BIT = 2
|
||||||
|
TK1_MMIO_TK1_GPIO4_BIT = 3
|
||||||
|
)
|
||||||
@@ -7,15 +7,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// change these to test a different UART or pins if available
|
|
||||||
var (
|
var (
|
||||||
uart = machine.Serial
|
uart = machine.Serial
|
||||||
tx = machine.UART_TX_PIN
|
|
||||||
rx = machine.UART_RX_PIN
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
// use default settings for UART
|
||||||
|
uart.Configure(machine.UARTConfig{})
|
||||||
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
|
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
|
||||||
|
|
||||||
input := make([]byte, 64)
|
input := make([]byte, 64)
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ func main() {
|
|||||||
Stereo: true,
|
Stereo: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
data := make([]uint32, 64)
|
data := make([]uint16, 64)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// get the next group of samples
|
// get the next group of samples
|
||||||
machine.I2S0.Read(data)
|
machine.I2S0.ReadMono(data)
|
||||||
|
|
||||||
println("data", data[0], data[1], data[2], data[4], "...")
|
println("data", data[0], data[1], data[2], data[4], "...")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
// This example demonstrates how to use pin change interrupts.
|
// This example demonstrates how to use pin change interrupts.
|
||||||
//
|
//
|
||||||
// This is only an example and should not be copied directly in any serious
|
// This is only an example and should not be copied directly in any serious
|
||||||
// circuit, because it lacks an important feature: debouncing.
|
// circuit, because it only naively implements an important feature: debouncing.
|
||||||
// See: https://en.wikipedia.org/wiki/Switch#Contact_bounce
|
// See: https://en.wikipedia.org/wiki/Switch#Contact_bounce
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -15,6 +15,8 @@ const (
|
|||||||
led = machine.LED
|
led = machine.LED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var lastPress time.Time
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// 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
|
||||||
@@ -29,6 +31,13 @@ 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) {
|
||||||
|
|
||||||
|
// Ignore events that are too close to the last registered press (debouncing)
|
||||||
|
if time.Since(lastPress) < 100*time.Millisecond {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastPress = time.Now()
|
||||||
|
|
||||||
led.Set(!led.Get())
|
led.Set(!led.Get())
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package futex
|
||||||
|
|
||||||
|
// Cross platform futex implementation.
|
||||||
|
// Futexes are supported on all major operating systems and on WebAssembly.
|
||||||
|
//
|
||||||
|
// For more information, see: https://outerproduct.net/futex-dictionary.html
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A futex is a way for userspace to wait with the pointer as the key, and for
|
||||||
|
// another thread to wake one or all waiting threads keyed on the same pointer.
|
||||||
|
//
|
||||||
|
// A futex does not change the underlying value, it only reads it before going
|
||||||
|
// to sleep (atomically) to prevent lost wake-ups.
|
||||||
|
type Futex struct {
|
||||||
|
atomic.Uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomically check for cmp to still be equal to the futex value and if so, go
|
||||||
|
// to sleep. Return true if we were definitely awoken by a call to Wake or
|
||||||
|
// WakeAll, and false if we can't be sure of that.
|
||||||
|
func (f *Futex) Wait(cmp uint32) bool {
|
||||||
|
tinygo_futex_wait((*uint32)(unsafe.Pointer(&f.Uint32)), cmp)
|
||||||
|
|
||||||
|
// We *could* detect a zero return value from the futex system call which
|
||||||
|
// would indicate we got awoken by a Wake or WakeAll call. However, this is
|
||||||
|
// what the manual page has to say:
|
||||||
|
//
|
||||||
|
// > Note that a wake-up can also be caused by common futex usage patterns
|
||||||
|
// > in unrelated code that happened to have previously used the futex
|
||||||
|
// > word's memory location (e.g., typical futex-based implementations of
|
||||||
|
// > Pthreads mutexes can cause this under some conditions). Therefore,
|
||||||
|
// > callers should always conservatively assume that a return value of 0
|
||||||
|
// > can mean a spurious wake-up, and use the futex word's value (i.e., the
|
||||||
|
// > user-space synchronization scheme) to decide whether to continue to
|
||||||
|
// > block or not.
|
||||||
|
//
|
||||||
|
// I'm not sure whether we do anything like pthread does, so to be on the
|
||||||
|
// safe side we say we don't know whether the wakeup was spurious or not and
|
||||||
|
// return false.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like Wait, but times out after the number of nanoseconds in timeout.
|
||||||
|
func (f *Futex) WaitUntil(cmp uint32, timeout uint64) {
|
||||||
|
tinygo_futex_wait_timeout((*uint32)(unsafe.Pointer(&f.Uint32)), cmp, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake a single waiter.
|
||||||
|
func (f *Futex) Wake() {
|
||||||
|
tinygo_futex_wake((*uint32)(unsafe.Pointer(&f.Uint32)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake all waiters.
|
||||||
|
func (f *Futex) WakeAll() {
|
||||||
|
tinygo_futex_wake_all((*uint32)(unsafe.Pointer(&f.Uint32)))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export tinygo_futex_wait
|
||||||
|
func tinygo_futex_wait(addr *uint32, cmp uint32)
|
||||||
|
|
||||||
|
//export tinygo_futex_wait_timeout
|
||||||
|
func tinygo_futex_wait_timeout(addr *uint32, cmp uint32, timeout uint64)
|
||||||
|
|
||||||
|
//export tinygo_futex_wake
|
||||||
|
func tinygo_futex_wake(addr *uint32)
|
||||||
|
|
||||||
|
//export tinygo_futex_wake_all
|
||||||
|
func tinygo_futex_wake_all(addr *uint32)
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
//go:build none
|
||||||
|
|
||||||
|
// This file is manually included, to avoid CGo which would cause a circular
|
||||||
|
// import.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// This API isn't documented by Apple, but it is used by LLVM libc++ (so should
|
||||||
|
// be stable) and has been documented extensively here:
|
||||||
|
// https://outerproduct.net/futex-dictionary.html
|
||||||
|
|
||||||
|
int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout_us);
|
||||||
|
int __ulock_wait2(uint32_t operation, void *addr, uint64_t value, uint64_t timeout_ns, uint64_t value2);
|
||||||
|
int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
|
||||||
|
|
||||||
|
// Operation code.
|
||||||
|
#define UL_COMPARE_AND_WAIT 1
|
||||||
|
|
||||||
|
// Flags to the operation value.
|
||||||
|
#define ULF_WAKE_ALL 0x00000100
|
||||||
|
#define ULF_NO_ERRNO 0x01000000
|
||||||
|
|
||||||
|
void tinygo_futex_wait(uint32_t *addr, uint32_t cmp) {
|
||||||
|
__ulock_wait(UL_COMPARE_AND_WAIT|ULF_NO_ERRNO, addr, (uint64_t)cmp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tinygo_futex_wait_timeout(uint32_t *addr, uint32_t cmp, uint64_t timeout) {
|
||||||
|
// Make sure that an accidental use of a zero timeout is not treated as an
|
||||||
|
// infinite timeout. Return if it's zero since it wouldn't be waiting for
|
||||||
|
// any significant time anyway.
|
||||||
|
// Probably unnecessary, but guards against potential bugs.
|
||||||
|
if (timeout == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: __ulock_wait2 is available since MacOS 11.
|
||||||
|
// I think that's fine, since the version before that (MacOS 10.15) is EOL
|
||||||
|
// since 2022. Though if needed, we could certainly use __ulock_wait instead
|
||||||
|
// and deal with the smaller timeout value.
|
||||||
|
__ulock_wait2(UL_COMPARE_AND_WAIT|ULF_NO_ERRNO, addr, (uint64_t)cmp, timeout, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tinygo_futex_wake(uint32_t *addr) {
|
||||||
|
__ulock_wake(UL_COMPARE_AND_WAIT|ULF_NO_ERRNO, addr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tinygo_futex_wake_all(uint32_t *addr) {
|
||||||
|
__ulock_wake(UL_COMPARE_AND_WAIT|ULF_NO_ERRNO|ULF_WAKE_ALL, addr, 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
//go:build none
|
||||||
|
|
||||||
|
// This file is manually included, to avoid CGo which would cause a circular
|
||||||
|
// import.
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define FUTEX_WAIT 0
|
||||||
|
#define FUTEX_WAKE 1
|
||||||
|
#define FUTEX_PRIVATE_FLAG 128
|
||||||
|
|
||||||
|
void tinygo_futex_wait(uint32_t *addr, uint32_t cmp) {
|
||||||
|
syscall(SYS_futex, addr, FUTEX_WAIT|FUTEX_PRIVATE_FLAG, cmp, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tinygo_futex_wait_timeout(uint32_t *addr, uint32_t cmp, uint64_t timeout) {
|
||||||
|
struct timespec ts = {0};
|
||||||
|
ts.tv_sec = timeout / 1000000000;
|
||||||
|
ts.tv_nsec = timeout % 1000000000;
|
||||||
|
syscall(SYS_futex, addr, FUTEX_WAIT|FUTEX_PRIVATE_FLAG, cmp, &ts, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tinygo_futex_wake(uint32_t *addr) {
|
||||||
|
syscall(SYS_futex, addr, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tinygo_futex_wake_all(uint32_t *addr) {
|
||||||
|
syscall(SYS_futex, addr, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, INT_MAX, NULL, NULL, 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//go:build tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// Atomics implementation for cooperative systems. The atomic types here aren't
|
||||||
|
// actually atomic, they assume that accesses cannot be interrupted by a
|
||||||
|
// different goroutine or interrupt happening at the same time.
|
||||||
|
|
||||||
|
type atomicIntegerType interface {
|
||||||
|
uintptr | uint32 | uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type pseudoAtomic[T atomicIntegerType] struct {
|
||||||
|
v T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *pseudoAtomic[T]) Add(delta T) T { x.v += delta; return x.v }
|
||||||
|
func (x *pseudoAtomic[T]) Load() T { return x.v }
|
||||||
|
func (x *pseudoAtomic[T]) Store(val T) { x.v = val }
|
||||||
|
func (x *pseudoAtomic[T]) CompareAndSwap(old, new T) (swapped bool) {
|
||||||
|
if x.v != old {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
x.v = new
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func (x *pseudoAtomic[T]) Swap(new T) (old T) {
|
||||||
|
old = x.v
|
||||||
|
x.v = new
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uintptr is an atomic uintptr when multithreading is enabled, and a plain old
|
||||||
|
// uintptr otherwise.
|
||||||
|
type Uintptr = pseudoAtomic[uintptr]
|
||||||
|
|
||||||
|
// Uint32 is an atomic uint32 when multithreading is enabled, and a plain old
|
||||||
|
// uint32 otherwise.
|
||||||
|
type Uint32 = pseudoAtomic[uint32]
|
||||||
|
|
||||||
|
// Uint64 is an atomic uint64 when multithreading is enabled, and a plain old
|
||||||
|
// uint64 otherwise.
|
||||||
|
type Uint64 = pseudoAtomic[uint64]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//go:build !tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// Atomics implementation for non-cooperative systems (multithreaded, etc).
|
||||||
|
// These atomic types use real atomic instructions.
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
type (
|
||||||
|
Uintptr = atomic.Uintptr
|
||||||
|
Uint32 = atomic.Uint32
|
||||||
|
Uint64 = atomic.Uint64
|
||||||
|
)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
//go:build tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// A futex is a way for userspace to wait with the pointer as the key, and for
|
||||||
|
// another thread to wake one or all waiting threads keyed on the same pointer.
|
||||||
|
//
|
||||||
|
// A futex does not change the underlying value, it only reads it before to prevent
|
||||||
|
// lost wake-ups.
|
||||||
|
type Futex struct {
|
||||||
|
Uint32
|
||||||
|
waiters Stack
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomically check for cmp to still be equal to the futex value and if so, go
|
||||||
|
// to sleep. Return true if we were definitely awoken by a call to Wake or
|
||||||
|
// WakeAll, and false if we can't be sure of that.
|
||||||
|
func (f *Futex) Wait(cmp uint32) (awoken bool) {
|
||||||
|
if f.Uint32.v != cmp {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the current goroutine onto the waiter stack.
|
||||||
|
f.waiters.Push(Current())
|
||||||
|
|
||||||
|
// Pause until the waiters are awoken by Wake/WakeAll.
|
||||||
|
Pause()
|
||||||
|
|
||||||
|
// We were awoken by a call to Wake or WakeAll. There is no chance for
|
||||||
|
// spurious wakeups.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake a single waiter.
|
||||||
|
func (f *Futex) Wake() {
|
||||||
|
if t := f.waiters.Pop(); t != nil {
|
||||||
|
scheduleTask(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake all waiters.
|
||||||
|
func (f *Futex) WakeAll() {
|
||||||
|
for t := f.waiters.Pop(); t != nil; t = f.waiters.Pop() {
|
||||||
|
scheduleTask(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
//go:build scheduler.cores
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "runtime/interrupt"
|
||||||
|
|
||||||
|
// A futex is a way for userspace to wait with the pointer as the key, and for
|
||||||
|
// another thread to wake one or all waiting threads keyed on the same pointer.
|
||||||
|
//
|
||||||
|
// A futex does not change the underlying value, it only reads it before to prevent
|
||||||
|
// lost wake-ups.
|
||||||
|
type Futex struct {
|
||||||
|
Uint32
|
||||||
|
|
||||||
|
waiters Stack
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomically check for cmp to still be equal to the futex value and if so, go
|
||||||
|
// to sleep. Return true if we were definitely awoken by a call to Wake or
|
||||||
|
// WakeAll, and false if we can't be sure of that.
|
||||||
|
func (f *Futex) Wait(cmp uint32) (awoken bool) {
|
||||||
|
mask := futexLock()
|
||||||
|
|
||||||
|
if f.Uint32.Load() != cmp {
|
||||||
|
futexUnlock(mask)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the current goroutine onto the waiter stack.
|
||||||
|
f.waiters.Push(Current())
|
||||||
|
|
||||||
|
futexUnlock(mask)
|
||||||
|
|
||||||
|
// Pause until this task is awoken by Wake/WakeAll.
|
||||||
|
Pause()
|
||||||
|
|
||||||
|
// We were awoken by a call to Wake or WakeAll. There is no chance for
|
||||||
|
// spurious wakeups.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake a single waiter.
|
||||||
|
func (f *Futex) Wake() {
|
||||||
|
mask := futexLock()
|
||||||
|
if t := f.waiters.Pop(); t != nil {
|
||||||
|
scheduleTask(t)
|
||||||
|
}
|
||||||
|
futexUnlock(mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake all waiters.
|
||||||
|
func (f *Futex) WakeAll() {
|
||||||
|
mask := futexLock()
|
||||||
|
for t := f.waiters.Pop(); t != nil; t = f.waiters.Pop() {
|
||||||
|
scheduleTask(t)
|
||||||
|
}
|
||||||
|
futexUnlock(mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname futexLock runtime.futexLock
|
||||||
|
func futexLock() interrupt.State
|
||||||
|
|
||||||
|
//go:linkname futexUnlock runtime.futexUnlock
|
||||||
|
func futexUnlock(interrupt.State)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build scheduler.threads
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "internal/futex"
|
||||||
|
|
||||||
|
type Futex = futex.Futex
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build linux && !baremetal
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// Musl uses a pointer (or unsigned long for C++) so unsafe.Pointer should be
|
||||||
|
// fine.
|
||||||
|
type threadID unsafe.Pointer
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//go:build tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
type Mutex struct {
|
||||||
|
locked bool
|
||||||
|
blocked Stack
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mutex) Lock() {
|
||||||
|
if m.locked {
|
||||||
|
// Push self onto stack of blocked tasks, and wait to be resumed.
|
||||||
|
m.blocked.Push(Current())
|
||||||
|
Pause()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.locked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mutex) Unlock() {
|
||||||
|
if !m.locked {
|
||||||
|
panic("sync: unlock of unlocked Mutex")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake up a blocked task, if applicable.
|
||||||
|
if t := m.blocked.Pop(); t != nil {
|
||||||
|
scheduleTask(t)
|
||||||
|
} else {
|
||||||
|
m.locked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock tries to lock m and reports whether it succeeded.
|
||||||
|
//
|
||||||
|
// Note that while correct uses of TryLock do exist, they are rare,
|
||||||
|
// and use of TryLock is often a sign of a deeper problem
|
||||||
|
// in a particular use of mutexes.
|
||||||
|
func (m *Mutex) TryLock() bool {
|
||||||
|
if m.locked {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
m.Lock()
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
//go:build !tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// Futex-based mutex.
|
||||||
|
// This is largely based on the paper "Futexes are Tricky" by Ulrich Drepper.
|
||||||
|
// It describes a few ways to implement mutexes using a futex, and how some
|
||||||
|
// seemingly-obvious implementations don't exactly work as intended.
|
||||||
|
// Unfortunately, Go atomic operations work slightly differently so we can't
|
||||||
|
// copy the algorithm verbatim.
|
||||||
|
//
|
||||||
|
// The implementation works like this. The futex can have 3 different values,
|
||||||
|
// depending on the state:
|
||||||
|
//
|
||||||
|
// - 0: the futex is currently unlocked.
|
||||||
|
// - 1: the futex is locked, but is uncontended. There is one special case: if
|
||||||
|
// a contended futex is unlocked, it is set to 0. It is possible for another
|
||||||
|
// thread to lock the futex before the next waiter is woken. But because a
|
||||||
|
// waiter will be woken (if there is one), it will always change to 2
|
||||||
|
// regardless. So this is not a problem.
|
||||||
|
// - 2: the futex is locked, and is contended. At least one thread is trying
|
||||||
|
// to obtain the lock (and is in the contended loop, see below).
|
||||||
|
//
|
||||||
|
// For the paper, see:
|
||||||
|
// https://dept-info.labri.fr/~denis/Enseignement/2008-IR/Articles/01-futex.pdf)
|
||||||
|
|
||||||
|
type Mutex struct {
|
||||||
|
futex Futex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mutex) Lock() {
|
||||||
|
// Fast path: try to take an uncontended lock.
|
||||||
|
if m.futex.CompareAndSwap(0, 1) {
|
||||||
|
// We obtained the mutex.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The futex is contended, so we enter the contended loop.
|
||||||
|
// If we manage to change the futex from 0 to 2, we managed to take the
|
||||||
|
// lock. Else, we have to wait until a call to Unlock unlocks this mutex.
|
||||||
|
// (Unlock will wake one waiter when it finds the futex is set to 2 when
|
||||||
|
// unlocking).
|
||||||
|
for m.futex.Swap(2) != 0 {
|
||||||
|
// Wait until we get resumed in Unlock.
|
||||||
|
m.futex.Wait(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mutex) Unlock() {
|
||||||
|
if old := m.futex.Swap(0); old == 0 {
|
||||||
|
// Mutex wasn't locked before.
|
||||||
|
panic("sync: unlock of unlocked Mutex")
|
||||||
|
} else if old == 2 {
|
||||||
|
// Mutex was a contended lock, so we need to wake the next waiter.
|
||||||
|
m.futex.Wake()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock tries to lock m and reports whether it succeeded.
|
||||||
|
//
|
||||||
|
// Note that while correct uses of TryLock do exist, they are rare,
|
||||||
|
// and use of TryLock is often a sign of a deeper problem
|
||||||
|
// in a particular use of mutexes.
|
||||||
|
func (m *Mutex) TryLock() bool {
|
||||||
|
// Fast path: try to take an uncontended lock.
|
||||||
|
if m.futex.CompareAndSwap(0, 1) {
|
||||||
|
// We obtained the mutex.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// PMutex is a real mutex on systems that can be either preemptive or threaded,
|
||||||
|
// and a dummy lock on other (purely cooperative) systems.
|
||||||
|
//
|
||||||
|
// It is mainly useful for short operations that need a lock when threading may
|
||||||
|
// be involved, but which do not need a lock with a purely cooperative
|
||||||
|
// scheduler.
|
||||||
|
type PMutex struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PMutex) Lock() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PMutex) Unlock() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build !tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// PMutex is a real mutex on systems that can be either preemptive or threaded,
|
||||||
|
// and a dummy lock on other (purely cooperative) systems.
|
||||||
|
//
|
||||||
|
// It is mainly useful for short operations that need a lock when threading may
|
||||||
|
// be involved, but which do not need a lock with a purely cooperative
|
||||||
|
// scheduler.
|
||||||
|
type PMutex = Mutex
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
// Barebones semaphore implementation.
|
||||||
|
// The main limitation is that if there are multiple waiters, a single Post()
|
||||||
|
// call won't do anything. Only when Post() has been called to awaken all
|
||||||
|
// waiters will the waiters proceed.
|
||||||
|
// This limitation is not a problem when there will only be a single waiter.
|
||||||
|
type Semaphore struct {
|
||||||
|
futex Futex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post (unlock) the semaphore, incrementing the value in the semaphore.
|
||||||
|
func (s *Semaphore) Post() {
|
||||||
|
newValue := s.futex.Add(1)
|
||||||
|
if newValue == 0 {
|
||||||
|
s.futex.WakeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait (lock) the semaphore, decrementing the value in the semaphore.
|
||||||
|
func (s *Semaphore) Wait() {
|
||||||
|
delta := int32(-1)
|
||||||
|
value := s.futex.Add(uint32(delta))
|
||||||
|
for {
|
||||||
|
if int32(value) >= 0 {
|
||||||
|
// Semaphore unlocked!
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.futex.Wait(value)
|
||||||
|
value = s.futex.Load()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,11 +21,36 @@ type Task struct {
|
|||||||
// state is the underlying running state of the task.
|
// state is the underlying running state of the task.
|
||||||
state state
|
state state
|
||||||
|
|
||||||
|
RunState uint8
|
||||||
|
|
||||||
// DeferFrame stores a pointer to the (stack allocated) defer frame of the
|
// DeferFrame stores a pointer to the (stack allocated) defer frame of the
|
||||||
// goroutine that is used for the recover builtin.
|
// goroutine that is used for the recover builtin.
|
||||||
DeferFrame unsafe.Pointer
|
DeferFrame unsafe.Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RunStatePaused = iota
|
||||||
|
RunStateRunning
|
||||||
|
RunStateResuming
|
||||||
|
)
|
||||||
|
|
||||||
|
// DataUint32 returns the Data field as a uint32. The value is only valid after
|
||||||
|
// setting it through SetDataUint32 or by storing to it using DataAtomicUint32.
|
||||||
|
func (t *Task) DataUint32() uint32 {
|
||||||
|
return *(*uint32)(unsafe.Pointer(&t.Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDataUint32 updates the uint32 portion of the Data field (which could be
|
||||||
|
// the first 4 or last 4 bytes depending on the architecture endianness).
|
||||||
|
func (t *Task) SetDataUint32(val uint32) {
|
||||||
|
*(*uint32)(unsafe.Pointer(&t.Data)) = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataAtomicUint32 returns the Data field as an atomic-if-needed Uint32 value.
|
||||||
|
func (t *Task) DataAtomicUint32() *Uint32 {
|
||||||
|
return (*Uint32)(unsafe.Pointer(&t.Data))
|
||||||
|
}
|
||||||
|
|
||||||
// getGoroutineStackSize is a compiler intrinsic that returns the stack size for
|
// getGoroutineStackSize is a compiler intrinsic that returns the stack size for
|
||||||
// the given function and falls back to the default stack size. It is replaced
|
// the given function and falls back to the default stack size. It is replaced
|
||||||
// with a load from a special section just before codegen.
|
// with a load from a special section just before codegen.
|
||||||
@@ -33,3 +58,14 @@ func getGoroutineStackSize(fn uintptr) uintptr
|
|||||||
|
|
||||||
//go:linkname runtime_alloc runtime.alloc
|
//go:linkname runtime_alloc runtime.alloc
|
||||||
func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
|
func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname scheduleTask runtime.scheduleTask
|
||||||
|
func scheduleTask(*Task)
|
||||||
|
|
||||||
|
//go:linkname runtimePanic runtime.runtimePanic
|
||||||
|
func runtimePanic(str string)
|
||||||
|
|
||||||
|
// Stack canary, to detect a stack overflow. The number is a random number
|
||||||
|
// generated by random.org. The bit fiddling dance is necessary because
|
||||||
|
// otherwise Go wouldn't allow the cast to a smaller integer size.
|
||||||
|
const stackCanary = uintptr(uint64(0x670c1333b83bf575) & uint64(^uintptr(0)))
|
||||||
|
|||||||
@@ -6,14 +6,6 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stack canary, to detect a stack overflow. The number is a random number
|
|
||||||
// generated by random.org. The bit fiddling dance is necessary because
|
|
||||||
// otherwise Go wouldn't allow the cast to a smaller integer size.
|
|
||||||
const stackCanary = uintptr(uint64(0x670c1333b83bf575) & uint64(^uintptr(0)))
|
|
||||||
|
|
||||||
//go:linkname runtimePanic runtime.runtimePanic
|
|
||||||
func runtimePanic(str string)
|
|
||||||
|
|
||||||
// state is a structure which holds a reference to the state of the task.
|
// state is a structure which holds a reference to the state of the task.
|
||||||
// When the task is suspended, the stack pointers are saved here.
|
// When the task is suspended, the stack pointers are saved here.
|
||||||
type state struct {
|
type state struct {
|
||||||
@@ -52,7 +44,7 @@ type stackState struct {
|
|||||||
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
t := &Task{}
|
t := &Task{}
|
||||||
t.state.initialize(fn, args, stackSize)
|
t.state.initialize(fn, args, stackSize)
|
||||||
runqueuePushBack(t)
|
scheduleTask(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_launch
|
//export tinygo_launch
|
||||||
@@ -82,9 +74,6 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
|||||||
s.csp = unsafe.Add(stack, stackSize)
|
s.csp = unsafe.Add(stack, stackSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname runqueuePushBack runtime.runqueuePushBack
|
|
||||||
func runqueuePushBack(*Task)
|
|
||||||
|
|
||||||
// currentTask is the current running task, or nil if currently in the scheduler.
|
// currentTask is the current running task, or nil if currently in the scheduler.
|
||||||
var currentTask *Task
|
var currentTask *Task
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ import "unsafe"
|
|||||||
// There is only one goroutine so the task struct can be a global.
|
// There is only one goroutine so the task struct can be a global.
|
||||||
var mainTask Task
|
var mainTask Task
|
||||||
|
|
||||||
//go:linkname runtimePanic runtime.runtimePanic
|
|
||||||
func runtimePanic(str string)
|
|
||||||
|
|
||||||
func Pause() {
|
func Pause() {
|
||||||
runtimePanic("scheduler is disabled")
|
runtimePanic("scheduler is disabled")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build scheduler.tasks
|
//go:build scheduler.tasks || scheduler.cores
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
@@ -7,14 +7,6 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:linkname runtimePanic runtime.runtimePanic
|
|
||||||
func runtimePanic(str string)
|
|
||||||
|
|
||||||
// Stack canary, to detect a stack overflow. The number is a random number
|
|
||||||
// generated by random.org. The bit fiddling dance is necessary because
|
|
||||||
// otherwise Go wouldn't allow the cast to a smaller integer size.
|
|
||||||
const stackCanary = uintptr(uint64(0x670c1333b83bf575) & uint64(^uintptr(0)))
|
|
||||||
|
|
||||||
// state is a structure which holds a reference to the state of the task.
|
// state is a structure which holds a reference to the state of the task.
|
||||||
// When the task is suspended, the registers are stored onto the stack and the stack pointer is stored into sp.
|
// When the task is suspended, the registers are stored onto the stack and the stack pointer is stored into sp.
|
||||||
type state struct {
|
type state struct {
|
||||||
@@ -33,40 +25,54 @@ type state struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// currentTask is the current running task, or nil if currently in the scheduler.
|
// currentTask is the current running task, or nil if currently in the scheduler.
|
||||||
var currentTask *Task
|
//var currentTask *Task
|
||||||
|
|
||||||
// Current returns the current active task.
|
// Current returns the current active task.
|
||||||
func Current() *Task {
|
//func Current() *Task {
|
||||||
return currentTask
|
// return currentTask
|
||||||
|
//}
|
||||||
|
|
||||||
|
//go:linkname Current runtime.currentTask
|
||||||
|
func Current() *Task
|
||||||
|
|
||||||
|
//go:linkname schedulerLock runtime.schedulerLock
|
||||||
|
func schedulerLock()
|
||||||
|
|
||||||
|
func Pause() {
|
||||||
|
schedulerLock()
|
||||||
|
PauseLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause suspends the current task and returns to the scheduler.
|
// Pause suspends the current task and returns to the scheduler.
|
||||||
// This function may only be called when running on a goroutine stack, not when running on the system stack or in an interrupt.
|
// This function may only be called when running on a goroutine stack, not when running on the system stack or in an interrupt.
|
||||||
func Pause() {
|
func PauseLocked() {
|
||||||
// Check whether the canary (the lowest address of the stack) is still
|
// Check whether the canary (the lowest address of the stack) is still
|
||||||
// valid. If it is not, a stack overflow has occurred.
|
// valid. If it is not, a stack overflow has occurred.
|
||||||
if *currentTask.state.canaryPtr != stackCanary {
|
if *Current().state.canaryPtr != stackCanary {
|
||||||
runtimePanic("goroutine stack overflow")
|
runtimePanic("goroutine stack overflow")
|
||||||
}
|
}
|
||||||
if interrupt.In() {
|
if interrupt.In() {
|
||||||
runtimePanic("blocked inside interrupt")
|
runtimePanic("blocked inside interrupt")
|
||||||
}
|
}
|
||||||
currentTask.state.pause()
|
current := Current()
|
||||||
|
current.RunState = RunStatePaused
|
||||||
|
current.state.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_pause
|
//export tinygo_task_exit
|
||||||
func pause() {
|
func taskExit() {
|
||||||
|
println("-- exiting task")
|
||||||
Pause()
|
Pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resume the task until it pauses or completes.
|
// Resume the task until it pauses or completes.
|
||||||
// This may only be called from the scheduler.
|
// This may only be called from the scheduler.
|
||||||
func (t *Task) Resume() {
|
func (t *Task) Resume() {
|
||||||
currentTask = t
|
//currentTask = t
|
||||||
t.gcData.swap()
|
//t.gcData.swap()
|
||||||
t.state.resume()
|
t.state.resume()
|
||||||
t.gcData.swap()
|
//t.gcData.swap()
|
||||||
currentTask = nil
|
//currentTask = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
||||||
@@ -101,15 +107,12 @@ func swapTask(oldStack uintptr, newStack *uintptr)
|
|||||||
//go:extern tinygo_startTask
|
//go:extern tinygo_startTask
|
||||||
var startTask [0]uint8
|
var startTask [0]uint8
|
||||||
|
|
||||||
//go:linkname runqueuePushBack runtime.runqueuePushBack
|
|
||||||
func runqueuePushBack(*Task)
|
|
||||||
|
|
||||||
// start creates and starts a new goroutine with the given function and arguments.
|
// start creates and starts a new goroutine with the given function and arguments.
|
||||||
// The new goroutine is scheduled to run later.
|
// The new goroutine is scheduled to run later.
|
||||||
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
t := &Task{}
|
t := &Task{}
|
||||||
t.state.initialize(fn, args, stackSize)
|
t.state.initialize(fn, args, stackSize)
|
||||||
runqueuePushBack(t)
|
scheduleTask(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSystemStack returns whether the caller is running on the system stack.
|
// OnSystemStack returns whether the caller is running on the system stack.
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ tinygo_startTask:
|
|||||||
blx r4
|
blx r4
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
bl tinygo_pause
|
bl tinygo_task_exit
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
.size tinygo_startTask, .-tinygo_startTask
|
.size tinygo_startTask, .-tinygo_startTask
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build scheduler.tasks && cortexm
|
//go:build (scheduler.tasks || scheduler.cores) && cortexm
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user