Compare commits

..

1 Commits

Author SHA1 Message Date
BCG 45f3110b47 Added mstats fields 2023-02-01 22:11:44 -05:00
119 changed files with 2066 additions and 3844 deletions
+8
View File
@@ -105,6 +105,13 @@ jobs:
- test-linux: - test-linux:
llvm: "14" llvm: "14"
resource_class: large resource_class: large
test-llvm15-go120:
docker:
- image: golang:1.20-rc-buster
steps:
- test-linux:
llvm: "15"
resource_class: large
workflows: workflows:
test-all: test-all:
@@ -112,3 +119,4 @@ 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-llvm14-go118 - test-llvm14-go118
- test-llvm15-go120
-2
View File
@@ -1,5 +1,3 @@
build/ build/
llvm-*/ llvm-*/
.github
.circleci
+10 -27
View File
@@ -27,13 +27,13 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Restore LLVM source cache - name: Cache LLVM source
uses: actions/cache/restore@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-macos-v3 key: llvm-source-15-macos-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -43,22 +43,11 @@ jobs:
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Cache LLVM build
uses: actions/cache/save@v3 uses: actions/cache@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-macos-v4 key: llvm-build-15-macos-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'
@@ -72,12 +61,6 @@ jobs:
# build! # build!
make llvm-build make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache wasi-libc sysroot - name: Cache wasi-libc sysroot
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-wasi-libc id: cache-wasi-libc
@@ -89,7 +72,7 @@ jobs:
run: make wasi-libc run: make wasi-libc
- name: Test TinyGo - name: Test TinyGo
shell: bash shell: bash
run: make test GOTESTFLAGS="-short" run: make test GOTESTFLAGS="-v -short"
- name: Build TinyGo release tarball - name: Build TinyGo release tarball
run: make release -j3 run: make release -j3
- name: Test stdlib packages - name: Test stdlib packages
@@ -120,11 +103,11 @@ jobs:
run: | run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15 HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Build TinyGo - name: Build TinyGo
run: go install run: go install
+15 -26
View File
@@ -53,7 +53,6 @@ jobs:
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-15
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
- name: Trigger Drivers repo build on Github Actions - name: Trigger Drivers repo build on Github Actions
@@ -70,31 +69,21 @@ jobs:
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \ https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
-d '{"ref": "dev"}' -d '{"ref": "dev"}'
- name: Trigger TinyFS repo build on Github Actions - name: Trigger TinyFS repo build on CircleCI
run: | run: |
curl -X POST \ curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfs/pipeline' \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \ --header 'Content-Type: application/json' \
-H "Accept: application/vnd.github.v3+json" \ -d '{"branch": "dev"}' \
https://api.github.com/repos/tinygo-org/tinyfs/actions/workflows/build.yml/dispatches \ -u "${{ secrets.CIRCLECI_API_TOKEN }}"
-d '{"ref": "dev"}' - name: Trigger TinyFont repo build on CircleCI
- name: Trigger TinyFont repo build on Github Actions
run: | run: |
curl -X POST \ curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfont/pipeline' \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \ --header 'Content-Type: application/json' \
-H "Accept: application/vnd.github.v3+json" \ -d '{"branch": "dev"}' \
https://api.github.com/repos/tinygo-org/tinyfont/actions/workflows/build.yml/dispatches \ -u "${{ secrets.CIRCLECI_API_TOKEN }}"
-d '{"ref": "dev"}' - name: Trigger TinyDraw repo build on CircleCI
- name: Trigger TinyDraw repo build on Github Actions
run: | run: |
curl -X POST \ curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinydraw/pipeline' \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \ --header 'Content-Type: application/json' \
-H "Accept: application/vnd.github.v3+json" \ -d '{"branch": "dev"}' \
https://api.github.com/repos/tinygo-org/tinydraw/actions/workflows/build.yml/dispatches \ -u "${{ secrets.CIRCLECI_API_TOKEN }}"
-d '{"ref": "dev"}'
- name: Trigger TinyTerm repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyterm/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
+31 -103
View File
@@ -18,7 +18,7 @@ jobs:
# statically linked binary. # statically linked binary.
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: golang:1.20-alpine image: golang:1.19-alpine
steps: steps:
- name: Install apk dependencies - name: Install apk dependencies
# tar: needed for actions/cache@v3 # tar: needed for actions/cache@v3
@@ -39,11 +39,11 @@ jobs:
path: | path: |
~/.cache/go-build ~/.cache/go-build
~/go/pkg/mod ~/go/pkg/mod
- name: Restore LLVM source cache - name: Cache LLVM source
uses: actions/cache/restore@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-alpine-v3 key: llvm-source-15-linux-alpine-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -53,22 +53,11 @@ jobs:
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Cache LLVM build
uses: actions/cache/save@v3 uses: actions/cache@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-alpine-v4 key: llvm-build-15-linux-alpine-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'
@@ -82,12 +71,6 @@ jobs:
make llvm-build make llvm-build
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-binaryen id: cache-binaryen
@@ -135,13 +118,11 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Install wasmtime - name: Install wasmtime
run: | run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin curl https://wasmtime.dev/install.sh -sSf | bash
curl https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Download release artifact - name: Download release artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
@@ -177,7 +158,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
@@ -185,15 +166,13 @@ jobs:
node-version: '14' node-version: '14'
- name: Install wasmtime - name: Install wasmtime
run: | run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin curl https://wasmtime.dev/install.sh -sSf | bash
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Restore LLVM source cache - name: Cache LLVM source
uses: actions/cache/restore@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-asserts-v3 key: llvm-source-15-linux-asserts-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -203,22 +182,11 @@ jobs:
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Cache LLVM build
uses: actions/cache/save@v3 uses: actions/cache@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-asserts-v4 key: llvm-build-15-linux-asserts-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'
@@ -230,12 +198,6 @@ jobs:
make llvm-build ASSERT=1 make llvm-build ASSERT=1
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-binaryen id: cache-binaryen
@@ -290,13 +252,13 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Restore LLVM source cache - name: Cache LLVM source
uses: actions/cache/restore@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-v3 key: llvm-source-15-linux-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -306,22 +268,11 @@ jobs:
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Cache LLVM build
uses: actions/cache/save@v3 uses: actions/cache@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-arm-v4 key: llvm-build-15-linux-arm-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'
@@ -335,12 +286,6 @@ jobs:
make llvm-build CROSS=arm-linux-gnueabihf make llvm-build CROSS=arm-linux-gnueabihf
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-binaryen id: cache-binaryen
@@ -407,13 +352,13 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Restore LLVM source cache - name: Cache LLVM source
uses: actions/cache/restore@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-linux-v3 key: llvm-source-15-linux-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -423,22 +368,11 @@ jobs:
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save LLVM source cache - name: Cache LLVM build
uses: actions/cache/save@v3 uses: actions/cache@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore LLVM build cache
uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-linux-arm64-v4 key: llvm-build-15-linux-arm64-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'
@@ -450,12 +384,6 @@ jobs:
make llvm-build CROSS=aarch64-linux-gnu make llvm-build CROSS=aarch64-linux-gnu
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save LLVM build cache
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache Binaryen - name: Cache Binaryen
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-binaryen id: cache-binaryen
+14 -55
View File
@@ -15,12 +15,6 @@ jobs:
build-windows: build-windows:
runs-on: windows-2022 runs-on: windows-2022
steps: steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- uses: brechtm/setup-scoop@v2 - uses: brechtm/setup-scoop@v2
with: with:
scoop_update: 'false' scoop_update: 'false'
@@ -35,13 +29,13 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Restore cached LLVM source - name: Cache LLVM source
uses: actions/cache/restore@v3 uses: actions/cache@v3
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-15-windows-v4 key: llvm-source-15-windows-v2
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
@@ -51,22 +45,11 @@ jobs:
- name: Download LLVM source - name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true' if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source run: make llvm-source
- name: Save cached LLVM source - name: Cache LLVM build
uses: actions/cache/save@v3 uses: actions/cache@v3
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Restore cached LLVM build
uses: actions/cache/restore@v3
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-15-windows-v5 key: llvm-build-15-windows-v2
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -79,12 +62,6 @@ jobs:
make llvm-build CCACHE=OFF make llvm-build CCACHE=OFF
# Remove unnecessary object files (to reduce cache size). # Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Save cached LLVM build
uses: actions/cache/save@v3
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
with:
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
path: llvm-build
- name: Cache wasi-libc sysroot - name: Cache wasi-libc sysroot
uses: actions/cache@v3 uses: actions/cache@v3
id: cache-wasi-libc id: cache-wasi-libc
@@ -99,7 +76,7 @@ jobs:
scoop install wasmtime scoop install wasmtime
- name: Test TinyGo - name: Test TinyGo
shell: bash shell: bash
run: make test GOTESTFLAGS="-short" run: make test GOTESTFLAGS="-v -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
@@ -123,12 +100,6 @@ jobs:
runs-on: windows-2022 runs-on: windows-2022
needs: build-windows needs: build-windows
steps: steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- uses: brechtm/setup-scoop@v2 - uses: brechtm/setup-scoop@v2
with: with:
scoop_update: 'false' scoop_update: 'false'
@@ -137,11 +108,11 @@ jobs:
run: | run: |
scoop install binaryen scoop install binaryen
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
@@ -160,18 +131,12 @@ jobs:
runs-on: windows-2022 runs-on: windows-2022
needs: build-windows needs: build-windows
steps: steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
@@ -189,12 +154,6 @@ jobs:
runs-on: windows-2022 runs-on: windows-2022
needs: build-windows needs: build-windows
steps: steps:
- name: Configure pagefile
uses: al-cheb/configure-pagefile-action@v1.3
with:
minimum-size: 8GB
maximum-size: 24GB
disk-root: "C:"
- uses: brechtm/setup-scoop@v2 - uses: brechtm/setup-scoop@v2
with: with:
scoop_update: 'false' scoop_update: 'false'
@@ -203,11 +162,11 @@ jobs:
run: | run: |
scoop install binaryen wasmtime scoop install binaryen wasmtime
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Install Go - name: Install Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: '1.20' go-version: '1.19'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
-97
View File
@@ -1,100 +1,3 @@
0.27.0
---
* **general**
- all: update musl
- all: remove "acm:"` prefix for USB vid/pid pair
- all: add support for LLVM 15
- all: use DWARF version 4
- all: add initial (incomplete) support for Go 1.20
- all: add `-gc=custom` option
- `main`: print ldflags including ThinLTO flags with -x
- `main`: fix error message when a serial port can't be accessed
- `main`: add `-timeout` flag to allow setting how long TinyGo will try looking for a MSD volume for flashing
- `test`: print PASS on pass when running standalone test binaries
- `test`: fix printing of benchmark output
- `test`: print package name when compilation failed (not just when the test failed)
* **compiler**
- refactor to support LLVM 15
- `builder`: print compiler commands while building a library
- `compiler`: fix stack overflow when creating recursive pointer types (fix for LLVM 15+ only)
- `compiler`: allow map keys and values of ≥256 bytes
- `cgo`: add support for `C.float` and `C.double`
- `cgo`: support anonymous enums included in multiple Go files
- `cgo`: add support for bitwise operators
- `interp`: add support for constant icmp instructions
- `transform`: fix memory corruption issues
* **standard library**
- `machine/usb`: remove allocs in USB ISR
- `machine/usb`: add `Port()` and deprecate `New()` to have the API better match the singleton that is actually being returned
- `machine/usb`: change HID usage-maximum to 0xFF
- `machine/usb`: add USB HID joystick support
- `machine/usb`: change to not send before endpoint initialization
- `net`: implement `Pipe`
- `os`: add stub for `os.Chtimes`
- `reflect`: stub out `Type.FieldByIndex`
- `reflect`: add `Value.IsZero` method
- `reflect`: fix bug in `.Field` method when the field fits in a pointer but the parent doesn't
- `runtime`: switch some `panic()` calls in the gc to `runtimePanic()` for consistency
- `runtime`: add xorshift-based fastrand64
- `runtime`: fix alignment for arm64, arm, xtensa, riscv
- `runtime`: implement precise GC
- `runtime/debug`: stub `PrintStack`
- `sync`: implement simple pooling in `sync.Pool`
- `syscall`: stubbed `Setuid`, Exec and friends
- `syscall`: add more stubs as needed for Go 1.20 support
- `testing`: implement `t.Setenv`
- `unsafe`: add support for Go 1.20 slice/string functions
* **targets**
- `all`: do not set stack size per board
- `all`: update picolibc to v1.7.9
- `atsame5x`: fix CAN extendedID handling
- `atsame5x`: reduce heap allocation
- `avr`: drop GNU toolchain dependency
- `avr`: fix .data initialization for binaries over 64kB
- `avr`: support ThinLTO
- `baremetal`: implements calloc
- `darwin`: fix `syscall.Open` on darwin/arm64
- `darwin`: fix error with `tinygo lldb`
- `esp`: use LLVM Xtensa linker instead of Espressif toolchain
- `esp`: use ThinLTO for Xtensa
- `esp32c3`: add SPI support
- `linux`: include musl `getpagesize` function in release
- `nrf51`: add ADC implementation
- `nrf52840`: add PDM support
- `riscv`: add "target-abi" metadata flag
- `rp2040`: remove mem allocation in GPIO ISR
- `rp2040`: avoid allocating clock on heap
- `rp2040`: add basic GPIO support for PIO
- `rp2040`: fix USB interrupt issue
- `rp2040`: fix RP2040-E5 USB errata
- `stm32`: always set ADC pins to pullups floating
- `stm32f1`, `stm32f4`: fix ADC by clearing the correct bit for rank after each read
- `stm32wl`: Fix incomplete RNG initialisation
- `stm32wlx`: change order for init so clock speeds are set before peripheral start
- `wasi`: makes wasmtime "run" explicit
- `wasm`: fix GC scanning of allocas
- `wasm`: allow custom malloc implementation
- `wasm`: remove `-wasm-abi=` flag (use `-target` instead)
- `wasm`: fix scanning of the stack
- `wasm`: fix panic when allocating 0 bytes using malloc
- `wasm`: always run wasm-opt even with `-scheduler=none`
- `wasm`: avoid miscompile with ThinLTO
- `wasm`: allow the emulator to expand `{tmpDir}`
- `wasm`: support ThinLTO
- `windows`: update mingw-w64 version to avoid linker warning
- `windows`: add ARM64 support
* **boards**
- Add Waveshare RP2040 Zero
- Add Arduino Leonardo support
- Add Adafruit KB2040
- Add Adafruit Feather M0 Express
- Add Makerfabs ESP32C3SPI35 TFT Touchscreen board
- Add Espressif ESP32-C3-DevKit-RUST-1 board
- `lgt92`: fix OpenOCD configuration
- `xiao-rp2040`: fix D9 and D10 constants
- `xiao-rp2040`: add pin definitions
0.26.0 0.26.0
--- ---
+1 -1
View File
@@ -1,5 +1,5 @@
# tinygo-llvm stage obtains the llvm source for TinyGo # tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.20 AS tinygo-llvm FROM golang:1.19 AS tinygo-llvm
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-11 ninja-build apt-get install -y apt-utils make cmake clang-11 ninja-build
+2 -13
View File
@@ -30,7 +30,7 @@ GO ?= go
export GOROOT = $(shell $(GO) env GOROOT) export GOROOT = $(shell $(GO) env GOROOT)
# Flags to pass to go test. # Flags to pass to go test.
GOTESTFLAGS ?= GOTESTFLAGS ?= -v
# md5sum binary # md5sum binary
MD5SUM = md5sum MD5SUM = md5sum
@@ -242,7 +242,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM. # Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd) TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: $(LLVM_BUILDDIR)/build.ninja:
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION) mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
# Build LLVM. # Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
@@ -296,7 +296,6 @@ TEST_PACKAGES_FAST = \
encoding \ encoding \
encoding/ascii85 \ encoding/ascii85 \
encoding/base32 \ encoding/base32 \
encoding/base64 \
encoding/csv \ encoding/csv \
encoding/hex \ encoding/hex \
go/scanner \ go/scanner \
@@ -332,34 +331,26 @@ TEST_PACKAGES_FAST += crypto/elliptic/internal/fiat
endif endif
# archive/zip requires os.ReadAt, which is not yet supported on windows # archive/zip requires os.ReadAt, which is not yet supported on windows
# bytes requires mmap
# compress/flate appears to hang on wasi # compress/flate appears to hang on wasi
# compress/lzw appears to hang on wasi # compress/lzw appears to hang on wasi
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic # crypto/hmac fails on wasi, it exits with a "slice out of range" panic
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows # debug/plan9obj requires os.ReadAt, which is not yet supported on windows
# image requires recover(), which is not yet supported on wasi
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi # io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
# mime/quotedprintable requires syscall.Faccessat
# strconv requires recover() which is not yet supported on wasi # strconv requires recover() which is not yet supported on wasi
# text/tabwriter requries recover(), which is not yet supported on wasi
# text/template/parse requires recover(), which is not yet supported on wasi # text/template/parse requires recover(), which is not yet supported on wasi
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi # testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
# Additional standard library packages that pass tests on individual platforms # Additional standard library packages that pass tests on individual platforms
TEST_PACKAGES_LINUX := \ TEST_PACKAGES_LINUX := \
archive/zip \ archive/zip \
bytes \
compress/flate \ compress/flate \
compress/lzw \ compress/lzw \
crypto/hmac \ crypto/hmac \
debug/dwarf \ debug/dwarf \
debug/plan9obj \ debug/plan9obj \
image \
io/ioutil \ io/ioutil \
mime/quotedprintable \
strconv \ strconv \
testing/fstest \ testing/fstest \
text/tabwriter \
text/template/parse text/template/parse
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX) TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
@@ -474,8 +465,6 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt $(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial $(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick $(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
+1 -1
View File
@@ -41,7 +41,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
## Supported boards/targets ## Supported boards/targets
You can compile TinyGo programs for microcontrollers, WebAssembly, Windows, and Linux. You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 94 microcontroller boards are currently supported: The following 94 microcontroller boards are currently supported:
+56 -36
View File
@@ -169,7 +169,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
CodeModel: config.CodeModel(), CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(), RelocationModel: config.RelocationModel(),
SizeLevel: sizeLevel, SizeLevel: sizeLevel,
TinyGoVersion: goenv.Version,
Scheduler: config.Scheduler(), Scheduler: config.Scheduler(),
AutomaticStackSize: config.AutomaticStackSize(), AutomaticStackSize: config.AutomaticStackSize(),
@@ -191,9 +190,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{ lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine), Sizes: compiler.Sizes(machine),
}) })
if err != nil {
return BuildResult{}, err
}
result := BuildResult{ result := BuildResult{
ModuleRoot: lprogram.MainPkg().Module.Dir, ModuleRoot: lprogram.MainPkg().Module.Dir,
MainDir: lprogram.MainPkg().Dir, MainDir: lprogram.MainPkg().Dir,
@@ -203,6 +199,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// If there is no module root, just the regular root. // If there is no module root, just the regular root.
result.ModuleRoot = lprogram.MainPkg().Root result.ModuleRoot = lprogram.MainPkg().Root
} }
if err != nil { // failed to load AST
return result, err
}
err = lprogram.Parse() err = lprogram.Parse()
if err != nil { if err != nil {
return result, err return result, err
@@ -306,6 +305,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
actionID := packageAction{ actionID := packageAction{
ImportPath: pkg.ImportPath, ImportPath: pkg.ImportPath,
CompilerBuildID: string(compilerBuildID), CompilerBuildID: string(compilerBuildID),
TinyGoVersion: goenv.Version,
LLVMVersion: llvm.Version, LLVMVersion: llvm.Version,
Config: compilerConfig, Config: compilerConfig,
CFlags: pkg.CFlags, CFlags: pkg.CFlags,
@@ -594,7 +594,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
defer llvmBuf.Dispose() defer llvmBuf.Dispose()
return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666) return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc": case ".bc":
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod) var buf llvm.MemoryBuffer
if config.UseThinLTO() {
buf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
} else {
buf = llvm.WriteBitcodeToMemoryBuffer(mod)
}
defer buf.Dispose() defer buf.Dispose()
return result, os.WriteFile(outpath, buf.Bytes(), 0666) return result, os.WriteFile(outpath, buf.Bytes(), 0666)
case ".ll": case ".ll":
@@ -616,7 +621,16 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
dependencies: []*compileJob{programJob}, dependencies: []*compileJob{programJob},
result: objfile, result: objfile,
run: func(*compileJob) error { run: func(*compileJob) error {
llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod) var llvmBuf llvm.MemoryBuffer
if config.UseThinLTO() {
llvmBuf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
} else {
var err error
llvmBuf, err = machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
}
}
defer llvmBuf.Dispose() defer llvmBuf.Dispose()
return os.WriteFile(objfile, llvmBuf.Bytes(), 0666) return os.WriteFile(objfile, llvmBuf.Bytes(), 0666)
}, },
@@ -650,7 +664,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{ job := &compileJob{
description: "compile extra file " + path, description: "compile extra file " + path,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands) result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands)
job.result = result job.result = result
return err return err
}, },
@@ -668,7 +682,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{ job := &compileJob{
description: "compile CGo file " + abspath, description: "compile CGo file " + abspath,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.Options.PrintCommands) result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands)
job.result = result job.result = result
return err return err
}, },
@@ -727,34 +741,36 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
} }
ldflags = append(ldflags, dependency.result) ldflags = append(ldflags, dependency.result)
} }
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU()) if config.UseThinLTO() {
if config.GOOS() == "windows" { ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
// Options for the MinGW wrapper for the lld COFF linker. if config.GOOS() == "windows" {
ldflags = append(ldflags, // Options for the MinGW wrapper for the lld COFF linker.
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel), ldflags = append(ldflags,
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto")) "-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
} else if config.GOOS() == "darwin" { "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
// Options for the ld64-compatible lld linker. } else if config.GOOS() == "darwin" {
ldflags = append(ldflags, // Options for the ld64-compatible lld linker.
"--lto-O"+strconv.Itoa(optLevel), ldflags = append(ldflags,
"-cache_path_lto", filepath.Join(cacheDir, "thinlto")) "--lto-O"+strconv.Itoa(optLevel),
} else { "-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
// Options for the ELF linker. } else {
ldflags = append(ldflags, // Options for the ELF linker.
"--lto-O"+strconv.Itoa(optLevel), ldflags = append(ldflags,
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"), "--lto-O"+strconv.Itoa(optLevel),
) "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
} )
if config.CodeModel() != "default" { }
ldflags = append(ldflags, if config.CodeModel() != "default" {
"-mllvm", "-code-model="+config.CodeModel()) ldflags = append(ldflags,
} "-mllvm", "-code-model="+config.CodeModel())
if sizeLevel >= 2 { }
// Workaround with roughly the same effect as if sizeLevel >= 2 {
// https://reviews.llvm.org/D119342. // Workaround with roughly the same effect as
// Can hopefully be removed in LLVM 15. // https://reviews.llvm.org/D119342.
ldflags = append(ldflags, // Can hopefully be removed in LLVM 15.
"-mllvm", "--rotation-max-header-size=0") ldflags = append(ldflags,
"-mllvm", "--rotation-max-header-size=0")
}
} }
if config.Options.PrintCommands != nil { if config.Options.PrintCommands != nil {
config.Options.PrintCommands(config.Target.Linker, ldflags...) config.Options.PrintCommands(config.Target.Linker, ldflags...)
@@ -1053,6 +1069,10 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
} }
} }
if config.GOOS() != "darwin" && !config.UseThinLTO() {
transform.ApplyFunctionSections(mod) // -ffunction-sections
}
// Insert values from -ldflags="-X ..." into the IR. // Insert values from -ldflags="-X ..." into the IR.
err = setGlobalValues(mod, config.Options.GlobalValues) err = setGlobalValues(mod, config.Options.GlobalValues)
if err != nil { if err != nil {
+16 -8
View File
@@ -56,7 +56,7 @@ import (
// depfile but without invalidating its name. For this reason, the depfile is // depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it // written on each new compilation (even when it seems unnecessary). However, it
// could in rare cases lead to a stale file fetched from the cache. // could in rare cases lead to a stale file fetched from the cache.
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) { func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) {
// Hash input file. // Hash input file.
fileHash, err := hashFile(abspath) fileHash, err := hashFile(abspath)
if err != nil { if err != nil {
@@ -67,6 +67,11 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock")) unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock"))
defer unlock() defer unlock()
ext := ".o"
if thinlto {
ext = ".bc"
}
// Create cache key for the dependencies file. // Create cache key for the dependencies file.
buf, err := json.Marshal(struct { buf, err := json.Marshal(struct {
Path string Path string
@@ -99,7 +104,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
} }
// Obtain hashes of all the files listed as a dependency. // Obtain hashes of all the files listed as a dependency.
outpath, err := makeCFileCachePath(dependencies, depfileNameHash) outpath, err := makeCFileCachePath(dependencies, depfileNameHash, ext)
if err == nil { if err == nil {
if _, err := os.Stat(outpath); err == nil { if _, err := os.Stat(outpath); err == nil {
return outpath, nil return outpath, nil
@@ -112,7 +117,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
return "", err return "", err
} }
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*.bc") objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*"+ext)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -122,8 +127,11 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
return "", err return "", err
} }
depTmpFile.Close() depTmpFile.Close()
flags := append([]string{}, cflags...) // copy cflags flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name(), "-flto=thin") // autogenerate dependencies flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
if thinlto {
flags = append(flags, "-flto=thin")
}
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath) flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
if strings.ToLower(filepath.Ext(abspath)) == ".s" { if strings.ToLower(filepath.Ext(abspath)) == ".s" {
// If this is an assembly file (.s or .S, lowercase or uppercase), then // If this is an assembly file (.s or .S, lowercase or uppercase), then
@@ -181,7 +189,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
} }
// Move temporary object file to final location. // Move temporary object file to final location.
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash) outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash, ext)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -196,7 +204,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
// Create a cache path (a path in GOCACHE) to store the output of a compiler // Create a cache path (a path in GOCACHE) to store the output of a compiler
// job. This path is based on the dep file name (which is a hash of metadata // job. This path is based on the dep file name (which is a hash of metadata
// including compiler flags) and the hash of all input files in the paths slice. // including compiler flags) and the hash of all input files in the paths slice.
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) { func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, error) {
// Hash all input files. // Hash all input files.
fileHashes := make(map[string]string, len(paths)) fileHashes := make(map[string]string, len(paths))
for _, path := range paths { for _, path := range paths {
@@ -221,7 +229,7 @@ func makeCFileCachePath(paths []string, depfileNameHash string) (string, error)
outFileNameBuf := sha512.Sum512_224(buf) outFileNameBuf := sha512.Sum512_224(buf)
cacheKey := hex.EncodeToString(outFileNameBuf[:]) cacheKey := hex.EncodeToString(outFileNameBuf[:])
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".bc") outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+ext)
return outpath, nil return outpath, nil
} }
+8 -3
View File
@@ -118,6 +118,10 @@ var (
// pack: data created when storing a constant in an interface for example // pack: data created when storing a constant in an interface for example
// string: buffer behind strings // string: buffer behind strings
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`) packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
// Reflect sidetables. Created by the reflect lowering pass.
// See src/reflect/sidetables.go.
reflectDataRegexp = regexp.MustCompile(`^reflect\.[a-zA-Z]+Sidetable$`)
) )
// readProgramSizeFromDWARF reads the source location for each line of code and // readProgramSizeFromDWARF reads the source location for each line of code and
@@ -371,7 +375,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
if section.Flags&elf.SHF_ALLOC == 0 { if section.Flags&elf.SHF_ALLOC == 0 {
continue continue
} }
if packageSymbolRegexp.MatchString(symbol.Name) { if packageSymbolRegexp.MatchString(symbol.Name) || reflectDataRegexp.MatchString(symbol.Name) {
addresses = append(addresses, addressLine{ addresses = append(addresses, addressLine{
Address: symbol.Value, Address: symbol.Value,
Length: symbol.Size, Length: symbol.Size,
@@ -832,8 +836,9 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
} 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, "$")]
} else if path == "<Go type>" { } else if reflectDataRegexp.MatchString(path) {
packagePath = "Go types" // Parse symbol names like reflect.structTypesSidetable.
packagePath = "Go reflect data"
} else if path == "<Go interface assert>" { } else if path == "<Go interface assert>" {
// Interface type assert, generated by the interface lowering pass. // Interface type assert, generated by the interface lowering pass.
packagePath = "Go interface assert" packagePath = "Go interface assert"
+9 -26
View File
@@ -75,8 +75,7 @@ func (c *Config) GOARM() string {
// BuildTags returns the complete list of build tags used during this build. // BuildTags returns the complete list of build tags used during this build.
func (c *Config) BuildTags() []string { func (c *Config) BuildTags() []string {
targetTags := filterTags(c.Target.BuildTags, c.Options.Tags) tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
tags := append(targetTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
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))
} }
@@ -191,6 +190,14 @@ func (c *Config) StackSize() uint64 {
return c.Target.DefaultStackSize return c.Target.DefaultStackSize
} }
// UseThinLTO returns whether ThinLTO should be used for the given target.
func (c *Config) UseThinLTO() bool {
// All architectures support ThinLTO now. However, this code is kept for the
// time being in case there are regressions. The non-ThinLTO code support
// should be removed when it is proven to work reliably.
return true
}
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that // RP2040BootPatch returns whether the RP2040 boot patch should be applied that
// calculates and patches in the checksum for the 2nd stage bootloader. // calculates and patches in the checksum for the 2nd stage bootloader.
func (c *Config) RP2040BootPatch() bool { func (c *Config) RP2040BootPatch() bool {
@@ -543,27 +550,3 @@ type TestConfig struct {
CompileTestBinary bool CompileTestBinary bool
// TODO: Filter the test functions to run, include verbose flag, etc // TODO: Filter the test functions to run, include verbose flag, etc
} }
// filterTags removes predefined build tags for a target if a conflicting option
// is provided by the user.
func filterTags(targetTags []string, userTags []string) []string {
var filtered []string
for _, t := range targetTags {
switch {
case strings.HasPrefix(t, "runtime_memhash_"):
overridden := false
for _, ut := range userTags {
if strings.HasPrefix(ut, "runtime_memhash_") {
overridden = true
break
}
}
if !overridden {
filtered = append(filtered, t)
}
default:
filtered = append(filtered, t)
}
}
return filtered
}
-132
View File
@@ -1,132 +0,0 @@
package compileopts
import (
"fmt"
"strings"
"testing"
)
func TestBuildTags(t *testing.T) {
tests := []struct {
targetTags []string
userTags []string
result []string
}{
{
targetTags: []string{},
userTags: []string{},
result: []string{
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
},
},
{
targetTags: []string{"bear"},
userTags: []string{},
result: []string{
"bear",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
},
},
{
targetTags: []string{},
userTags: []string{"cat"},
result: []string{
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
},
},
{
targetTags: []string{"bear"},
userTags: []string{"cat"},
result: []string{
"bear",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
},
},
{
targetTags: []string{"bear", "runtime_memhash_leveldb"},
userTags: []string{"cat"},
result: []string{
"bear",
"runtime_memhash_leveldb",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
},
},
{
targetTags: []string{"bear", "runtime_memhash_leveldb"},
userTags: []string{"cat", "runtime_memhash_leveldb"},
result: []string{
"bear",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
"runtime_memhash_leveldb",
},
},
{
targetTags: []string{"bear", "runtime_memhash_leveldb"},
userTags: []string{"cat", "runtime_memhash_sip"},
result: []string{
"bear",
"tinygo",
"math_big_pure_go",
"gc.conservative",
"scheduler.none",
"serial.none",
"cat",
"runtime_memhash_sip",
},
},
}
for _, tc := range tests {
tt := tc
t.Run(fmt.Sprintf("%s+%s", strings.Join(tt.targetTags, ","), strings.Join(tt.userTags, ",")), func(t *testing.T) {
c := &Config{
Target: &TargetSpec{
BuildTags: tt.targetTags,
},
Options: &Options{
Tags: tt.userTags,
},
}
res := c.BuildTags()
if len(res) != len(tt.result) {
t.Errorf("expected %d tags, got %d", len(tt.result), len(res))
}
for i, tag := range tt.result {
if tag != res[i] {
t.Errorf("tag %d: expected %s, got %s", i, tt.result[i], tag)
}
}
})
}
}
+3 -3
View File
@@ -326,9 +326,9 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
} }
if goarch != "wasm" { if goarch != "wasm" {
suffix := "" suffix := ""
if goos == "windows" && goarch == "amd64" { if goos == "windows" {
// Windows uses a different calling convention on amd64 from other // Windows uses a different calling convention from other operating
// operating systems so we need separate assembly files. // systems so we need separate assembly files.
suffix = "_windows" suffix = "_windows"
} }
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S") spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
+2 -14
View File
@@ -47,7 +47,6 @@ type Config struct {
CodeModel string CodeModel string
RelocationModel string RelocationModel string
SizeLevel int SizeLevel int
TinyGoVersion string // for llvm.ident
// Various compiler options that determine how code is generated. // Various compiler options that determine how code is generated.
Scheduler string Scheduler string
@@ -322,14 +321,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(), llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
}), }),
) )
if c.TinyGoVersion != "" {
// It is necessary to set llvm.ident, otherwise debugging on MacOS
// won't work.
c.mod.AddNamedMetadataOperand("llvm.ident",
c.ctx.MDNode(([]llvm.Metadata{
c.ctx.MDString("TinyGo version " + c.TinyGoVersion),
})))
}
c.dibuilder.Finalize() c.dibuilder.Finalize()
c.dibuilder.Destroy() c.dibuilder.Destroy()
} }
@@ -349,15 +340,12 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
return c.mod, c.diagnostics return c.mod, c.diagnostics
} }
func (c *compilerContext) getRuntimeType(name string) types.Type {
return c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type()
}
// getLLVMRuntimeType obtains a named type from the runtime package and returns // getLLVMRuntimeType obtains a named type from the runtime package and returns
// it as a LLVM type, creating it if necessary. It is a shorthand for // it as a LLVM type, creating it if necessary. It is a shorthand for
// getLLVMType(getRuntimeType(name)). // getLLVMType(getRuntimeType(name)).
func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type { func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
return c.getLLVMType(c.getRuntimeType(name)) typ := c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type()
return c.getLLVMType(typ)
} }
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already // getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
-1
View File
@@ -49,7 +49,6 @@ func TestCompiler(t *testing.T) {
{"goroutine.go", "cortex-m-qemu", "tasks"}, {"goroutine.go", "cortex-m-qemu", "tasks"},
{"channel.go", "", ""}, {"channel.go", "", ""},
{"gc.go", "", ""}, {"gc.go", "", ""},
{"zeromap.go", "", ""},
} }
if goMinor >= 20 { if goMinor >= 20 {
tests = append(tests, testCase{"go1.20.go", "", ""}) tests = append(tests, testCase{"go1.20.go", "", ""})
+2 -2
View File
@@ -271,7 +271,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode") typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver") receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
values = []llvm.Value{callback, next, typecode, receiverValue} values = []llvm.Value{callback, next, typecode, receiverValue}
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType) valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
for _, arg := range instr.Call.Args { for _, arg := range instr.Call.Args {
val := b.getValue(arg) val := b.getValue(arg)
values = append(values, val) values = append(values, val)
@@ -476,7 +476,7 @@ func (b *builder) createRunDefers() {
valueTypes = append(valueTypes, b.getFuncType(callback.Signature())) valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
} else { } else {
//Expect typecode //Expect typecode
valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType) valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
} }
for _, arg := range callback.Args { for _, arg := range callback.Args {
+166 -296
View File
@@ -6,7 +6,6 @@ package compiler
// interface-lowering.go for more details. // interface-lowering.go for more details.
import ( import (
"fmt"
"go/token" "go/token"
"go/types" "go/types"
"strconv" "strconv"
@@ -16,49 +15,6 @@ import (
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// Type kinds for basic types.
// They must match the constants for the Kind type in src/reflect/type.go.
var basicTypes = [...]uint8{
types.Bool: 1,
types.Int: 2,
types.Int8: 3,
types.Int16: 4,
types.Int32: 5,
types.Int64: 6,
types.Uint: 7,
types.Uint8: 8,
types.Uint16: 9,
types.Uint32: 10,
types.Uint64: 11,
types.Uintptr: 12,
types.Float32: 13,
types.Float64: 14,
types.Complex64: 15,
types.Complex128: 16,
types.String: 17,
types.UnsafePointer: 18,
}
// These must also match the constants for the Kind type in src/reflect/type.go.
const (
typeKindChan = 19
typeKindInterface = 20
typeKindPointer = 21
typeKindSlice = 22
typeKindArray = 23
typeKindSignature = 24
typeKindMap = 25
typeKindStruct = 26
)
// Flags stored in the first byte of the struct field byte array. Must be kept
// up to date with src/reflect/type.go.
const (
structFieldFlagAnonymous = 1 << iota
structFieldFlagHasTag
structFieldFlagIsExported
)
// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction. // createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
// It tries to put the type in the interface value, but if that's not possible, // It tries to put the type in the interface value, but if that's not possible,
// it will do an allocation of the right size and put that in the interface // it will do an allocation of the right size and put that in the interface
@@ -67,9 +23,10 @@ const (
// An interface value is a {typecode, value} tuple named runtime._interface. // An interface value is a {typecode, value} tuple named runtime._interface.
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value { func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
itfValue := b.emitPointerPack([]llvm.Value{val}) itfValue := b.emitPointerPack([]llvm.Value{val})
itfType := b.getTypeCode(typ) itfTypeCodeGlobal := b.getTypeCode(typ)
itfTypeCode := b.CreatePtrToInt(itfTypeCodeGlobal, b.uintptrType, "")
itf := llvm.Undef(b.getLLVMRuntimeType("_interface")) itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
itf = b.CreateInsertValue(itf, itfType, 0, "") itf = b.CreateInsertValue(itf, itfTypeCode, 0, "")
itf = b.CreateInsertValue(itf, itfValue, 1, "") itf = b.CreateInsertValue(itf, itfValue, 1, "")
return itf return itf
} }
@@ -84,240 +41,118 @@ func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type)
} }
// getTypeCode returns a reference to a type code. // getTypeCode returns a reference to a type code.
// A type code is a pointer to a constant global that describes the type. // It returns a pointer to an external global which should be replaced with the
// This function returns a pointer to the 'kind' field (which might not be the // real type in the interface lowering pass.
// first field in the struct).
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
ms := c.program.MethodSets.MethodSet(typ)
hasMethodSet := ms.Len() != 0
if _, ok := typ.Underlying().(*types.Interface); ok {
hasMethodSet = false
}
globalName := "reflect/types.type:" + getTypeCodeName(typ) globalName := "reflect/types.type:" + getTypeCodeName(typ)
global := c.mod.NamedGlobal(globalName) global := c.mod.NamedGlobal(globalName)
if global.IsNil() { if global.IsNil() {
var typeFields []llvm.Value // Create a new typecode global.
// Define the type fields. These must match the structs in global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName)
// src/reflect/type.go (ptrType, arrayType, etc). See the comment at the // Some type classes contain more information for underlying types or
// top of src/reflect/type.go for more information on the layout of these structs. // element types. Store it directly in the typecode global to make
typeFieldTypes := []*types.Var{ // reflect lowering simpler.
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]), var references llvm.Value
} var length int64
var methodSet llvm.Value
var ptrTo llvm.Value
var typeAssert llvm.Value
switch typ := typ.(type) { switch typ := typ.(type) {
case *types.Basic:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
)
case *types.Named: case *types.Named:
typeFieldTypes = append(typeFieldTypes, references = c.getTypeCode(typ.Underlying())
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
)
case *types.Chan, *types.Slice:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
)
case *types.Pointer:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
)
case *types.Array:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
)
case *types.Map:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "keyType", types.Typ[types.UnsafePointer]),
)
case *types.Struct:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
)
case *types.Interface:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
)
// TODO: methods
case *types.Signature:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
)
// TODO: signature params and return values
}
if hasMethodSet {
// This method set is appended at the start of the struct. It is
// removed in the interface lowering pass.
// TODO: don't remove these and instead do what upstream Go is doing
// instead. See: https://research.swtch.com/interfaces. This can
// likely be optimized in LLVM using
// https://llvm.org/docs/TypeMetadata.html.
typeFieldTypes = append([]*types.Var{
types.NewVar(token.NoPos, nil, "methodSet", types.Typ[types.UnsafePointer]),
}, typeFieldTypes...)
}
globalType := types.NewStruct(typeFieldTypes, nil)
global = llvm.AddGlobal(c.mod, c.getLLVMType(globalType), globalName)
metabyte := getTypeKind(typ)
switch typ := typ.(type) {
case *types.Basic:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
case *types.Named:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Underlying()), // underlying
}
metabyte |= 1 << 5 // "named" flag
case *types.Chan: case *types.Chan:
typeFields = []llvm.Value{ references = c.getTypeCode(typ.Elem())
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elementType
}
case *types.Slice:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elementType
}
case *types.Pointer: case *types.Pointer:
typeFields = []llvm.Value{c.getTypeCode(typ.Elem())} references = c.getTypeCode(typ.Elem())
case *types.Slice:
references = c.getTypeCode(typ.Elem())
case *types.Array: case *types.Array:
typeFields = []llvm.Value{ references = c.getTypeCode(typ.Elem())
c.getTypeCode(types.NewPointer(typ)), // ptrTo length = typ.Len()
c.getTypeCode(typ.Elem()), // elementType
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
}
case *types.Map:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elem
c.getTypeCode(typ.Key()), // key
}
case *types.Struct: case *types.Struct:
typeFields = []llvm.Value{ // Take a pointer to the typecodeID of the first field (if it exists).
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields structGlobal := c.makeStructTypeFields(typ)
c.getTypeCode(types.NewPointer(typ)), // ptrTo references = llvm.ConstBitCast(structGlobal, global.Type())
}
structFieldType := c.getLLVMRuntimeType("structField")
var fields []llvm.Value
for i := 0; i < typ.NumFields(); i++ {
field := typ.Field(i)
var flags uint8
if field.Anonymous() {
flags |= structFieldFlagAnonymous
}
if typ.Tag(i) != "" {
flags |= structFieldFlagHasTag
}
if token.IsExported(field.Name()) {
flags |= structFieldFlagIsExported
}
data := string(flags) + field.Name() + "\x00"
if typ.Tag(i) != "" {
if len(typ.Tag(i)) > 0xff {
c.addError(field.Pos(), fmt.Sprintf("struct tag is %d bytes which is too long, max is 255", len(typ.Tag(i))))
}
data += string([]byte{byte(len(typ.Tag(i)))}) + typ.Tag(i)
}
dataInitializer := c.ctx.ConstString(data, false)
dataGlobal := llvm.AddGlobal(c.mod, dataInitializer.Type(), globalName+"."+field.Name())
dataGlobal.SetInitializer(dataInitializer)
dataGlobal.SetAlignment(1)
dataGlobal.SetUnnamedAddr(true)
dataGlobal.SetLinkage(llvm.InternalLinkage)
dataGlobal.SetGlobalConstant(true)
fieldType := c.getTypeCode(field.Type())
fields = append(fields, llvm.ConstNamedStruct(structFieldType, []llvm.Value{
fieldType,
llvm.ConstGEP(dataGlobal.GlobalValueType(), dataGlobal, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
}),
}))
}
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
case *types.Interface: case *types.Interface:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))} methodSetGlobal := c.getInterfaceMethodSet(typ)
// TODO: methods references = llvm.ConstBitCast(methodSetGlobal, global.Type())
case *types.Signature:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: params, return values, etc
} }
// Prepend metadata byte. if _, ok := typ.Underlying().(*types.Interface); !ok {
typeFields = append([]llvm.Value{ methodSet = c.getTypeMethodSet(typ)
llvm.ConstInt(c.ctx.Int8Type(), uint64(metabyte), false), } else {
}, typeFields...) typeAssert = c.getInterfaceImplementsFunc(typ)
if hasMethodSet { typeAssert = llvm.ConstPtrToInt(typeAssert, c.uintptrType)
typeFields = append([]llvm.Value{ }
llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType), if _, ok := typ.Underlying().(*types.Pointer); !ok {
}, typeFields...) ptrTo = c.getTypeCode(types.NewPointer(typ))
}
globalValue := llvm.ConstNull(global.GlobalValueType())
if !references.IsNil() {
globalValue = c.builder.CreateInsertValue(globalValue, references, 0, "")
}
if length != 0 {
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
globalValue = c.builder.CreateInsertValue(globalValue, lengthValue, 1, "")
}
if !methodSet.IsNil() {
globalValue = c.builder.CreateInsertValue(globalValue, methodSet, 2, "")
}
if !ptrTo.IsNil() {
globalValue = c.builder.CreateInsertValue(globalValue, ptrTo, 3, "")
}
if !typeAssert.IsNil() {
globalValue = c.builder.CreateInsertValue(globalValue, typeAssert, 4, "")
} }
alignment := c.targetData.TypeAllocSize(c.i8ptrType)
globalValue := c.ctx.ConstStruct(typeFields, false)
global.SetInitializer(globalValue) global.SetInitializer(globalValue)
global.SetLinkage(llvm.LinkOnceODRLinkage) global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetAlignment(int(alignment)) }
if c.Debug { return global
file := c.getDIFile("<Go type>") }
diglobal := c.dibuilder.CreateGlobalVariableExpression(file, llvm.DIGlobalVariableExpression{
Name: "type " + typ.String(), // makeStructTypeFields creates a new global that stores all type information
File: file, // related to this struct type, and returns the resulting global. This global is
Line: 1, // actually an array of all the fields in the structs.
Type: c.getDIType(globalType), func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
LocalToUnit: false, // The global is an array of runtime.structField structs.
Expr: c.dibuilder.CreateExpression(nil), runtimeStructField := c.getLLVMRuntimeType("structField")
AlignInBits: uint32(alignment * 8), structGlobalType := llvm.ArrayType(runtimeStructField, typ.NumFields())
structGlobal := llvm.AddGlobal(c.mod, structGlobalType, "reflect/types.structFields")
structGlobalValue := llvm.ConstNull(structGlobalType)
for i := 0; i < typ.NumFields(); i++ {
fieldGlobalValue := llvm.ConstNull(runtimeStructField)
fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), 0, "")
fieldNameType, fieldName := c.makeGlobalArray([]byte(typ.Field(i).Name()), "reflect/types.structFieldName", c.ctx.Int8Type())
fieldName.SetLinkage(llvm.PrivateLinkage)
fieldName.SetUnnamedAddr(true)
fieldName = llvm.ConstGEP(fieldNameType, fieldName, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
})
fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldName, 1, "")
if typ.Tag(i) != "" {
fieldTagType, fieldTag := c.makeGlobalArray([]byte(typ.Tag(i)), "reflect/types.structFieldTag", c.ctx.Int8Type())
fieldTag.SetLinkage(llvm.PrivateLinkage)
fieldTag.SetUnnamedAddr(true)
fieldTag = llvm.ConstGEP(fieldTagType, fieldTag, []llvm.Value{
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
}) })
global.AddMetadata(0, diglobal) fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldTag, 2, "")
} }
if typ.Field(i).Embedded() {
fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false)
fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldEmbedded, 3, "")
}
structGlobalValue = c.builder.CreateInsertValue(structGlobalValue, fieldGlobalValue, i, "")
} }
offset := uint64(0) structGlobal.SetInitializer(structGlobalValue)
if hasMethodSet { structGlobal.SetUnnamedAddr(true)
// The pointer to the method set is always the first element of the structGlobal.SetLinkage(llvm.PrivateLinkage)
// global (if there is a method set). However, the pointer we return return structGlobal
// should point to the 'kind' field not the method set.
offset = 1
}
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
llvm.ConstInt(llvm.Int32Type(), 0, false),
llvm.ConstInt(llvm.Int32Type(), offset, false),
})
} }
// getTypeKind returns the type kind for the given type, as defined by var basicTypes = [...]string{
// reflect.Kind.
func getTypeKind(t types.Type) uint8 {
switch t := t.Underlying().(type) {
case *types.Basic:
return basicTypes[t.Kind()]
case *types.Chan:
return typeKindChan
case *types.Interface:
return typeKindInterface
case *types.Pointer:
return typeKindPointer
case *types.Slice:
return typeKindSlice
case *types.Array:
return typeKindArray
case *types.Signature:
return typeKindSignature
case *types.Map:
return typeKindMap
case *types.Struct:
return typeKindStruct
default:
panic("unknown type")
}
}
var basicTypeNames = [...]string{
types.Bool: "bool", types.Bool: "bool",
types.Int: "int", types.Int: "int",
types.Int8: "int8", types.Int8: "int8",
@@ -348,7 +183,7 @@ func getTypeCodeName(t types.Type) string {
case *types.Array: case *types.Array:
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem()) return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
case *types.Basic: case *types.Basic:
return "basic:" + basicTypeNames[t.Kind()] return "basic:" + basicTypes[t.Kind()]
case *types.Chan: case *types.Chan:
return "chan:" + getTypeCodeName(t.Elem()) return "chan:" + getTypeCodeName(t.Elem())
case *types.Interface: case *types.Interface:
@@ -400,40 +235,75 @@ func getTypeCodeName(t types.Type) string {
// getTypeMethodSet returns a reference (GEP) to a global method set. This // getTypeMethodSet returns a reference (GEP) to a global method set. This
// method set should be unreferenced after the interface lowering pass. // method set should be unreferenced after the interface lowering pass.
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value { func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
globalName := typ.String() + "$methodset" global := c.mod.NamedGlobal(typ.String() + "$methodset")
global := c.mod.NamedGlobal(globalName) zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
if global.IsNil() { if !global.IsNil() {
ms := c.program.MethodSets.MethodSet(typ) // the method set already exists
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{zero, zero})
// Create method set.
var signatures, wrappers []llvm.Value
for i := 0; i < ms.Len(); i++ {
method := ms.At(i)
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
signatures = append(signatures, signatureGlobal)
fn := c.program.MethodValue(method)
llvmFnType, llvmFn := c.getFunction(fn)
if llvmFn.IsNil() {
// compiler error, so panic
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
}
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn)
wrappers = append(wrappers, wrapper)
}
// Construct global value.
globalValue := c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false),
llvm.ConstArray(c.i8ptrType, signatures),
c.ctx.ConstStruct(wrappers, false),
}, false)
global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName)
global.SetInitializer(globalValue)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetLinkage(llvm.LinkOnceODRLinkage)
} }
return global
ms := c.program.MethodSets.MethodSet(typ)
if ms.Len() == 0 {
// no methods, so can leave that one out
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
}
methods := make([]llvm.Value, ms.Len())
interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo")
for i := 0; i < ms.Len(); i++ {
method := ms.At(i)
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
fn := c.program.MethodValue(method)
llvmFnType, llvmFn := c.getFunction(fn)
if llvmFn.IsNil() {
// compiler error, so panic
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
}
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFnType, llvmFn)
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
signatureGlobal,
llvm.ConstPtrToInt(wrapper, c.uintptrType),
})
methods[i] = methodInfo
}
arrayType := llvm.ArrayType(interfaceMethodInfoType, len(methods))
value := llvm.ConstArray(interfaceMethodInfoType, methods)
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
global.SetInitializer(value)
global.SetGlobalConstant(true)
global.SetLinkage(llvm.LinkOnceODRLinkage)
return llvm.ConstGEP(arrayType, global, []llvm.Value{zero, zero})
}
// getInterfaceMethodSet returns a global variable with the method set of the
// given named interface type. This method set is used by the interface lowering
// pass.
func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
name := typ.String()
if _, ok := typ.(*types.Named); !ok {
// Anonymous interface.
name = "reflect/types.interface:" + name
}
global := c.mod.NamedGlobal(name + "$interface")
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
if !global.IsNil() {
// method set already exist, return it
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{zero, zero})
}
// Every method is a *i8 reference indicating the signature of this method.
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
for i := range methods {
method := typ.Underlying().(*types.Interface).Method(i)
methods[i] = c.getMethodSignature(method)
}
value := llvm.ConstArray(c.i8ptrType, methods)
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
global.SetInitializer(value)
global.SetGlobalConstant(true)
global.SetLinkage(llvm.LinkOnceODRLinkage)
return llvm.ConstGEP(value.Type(), global, []llvm.Value{zero, zero})
} }
// getMethodSignatureName returns a unique name (that can be used as the name of // getMethodSignatureName returns a unique name (that can be used as the name of
@@ -573,7 +443,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll
fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert" fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
llvmFn := c.mod.NamedFunction(fnName) llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, false) llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.uintptrType}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn) c.addStandardDeclaredAttributes(llvmFn)
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface)) methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
@@ -594,7 +464,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
for i := 0; i < sig.Params().Len(); i++ { for i := 0; i < sig.Params().Len(); i++ {
paramTuple = append(paramTuple, sig.Params().At(i)) paramTuple = append(paramTuple, sig.Params().At(i))
} }
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer])) paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr]))
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)) llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn) c.addStandardDeclaredAttributes(llvmFn)
@@ -731,7 +601,7 @@ func typestring(t types.Type) string {
case *types.Array: case *types.Array:
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem()) return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
case *types.Basic: case *types.Basic:
return basicTypeNames[t.Kind()] return basicTypes[t.Kind()]
case *types.Chan: case *types.Chan:
switch t.Dir() { switch t.Dir() {
case types.SendRecv: case types.SendRecv:
-25
View File
@@ -24,8 +24,6 @@ func (b *builder) defineIntrinsicFunction() {
b.createMemoryCopyImpl() b.createMemoryCopyImpl()
case name == "runtime.memzero": case name == "runtime.memzero":
b.createMemoryZeroImpl() b.createMemoryZeroImpl()
case name == "runtime.KeepAlive":
b.createKeepAliveImpl()
case strings.HasPrefix(name, "runtime/volatile.Load"): case strings.HasPrefix(name, "runtime/volatile.Load"):
b.createVolatileLoad() b.createVolatileLoad()
case strings.HasPrefix(name, "runtime/volatile.Store"): case strings.HasPrefix(name, "runtime/volatile.Store"):
@@ -89,29 +87,6 @@ func (b *builder) createMemoryZeroImpl() {
b.CreateRetVoid() b.CreateRetVoid()
} }
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
// using inline assembly.
func (b *builder) createKeepAliveImpl() {
b.createFunctionStart(true)
// Get the underlying value of the interface value.
interfaceValue := b.getValue(b.fn.Params[0])
pointerValue := b.CreateExtractValue(interfaceValue, 1, "")
// Create an equivalent of the following C code, which is basically just a
// nop but ensures the pointerValue is kept alive:
//
// __asm__ __volatile__("" : : "r"(pointerValue))
//
// It should be portable to basically everything as the "r" register type
// exists basically everywhere.
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false)
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
b.CreateRetVoid()
}
var mathToLLVMMapping = map[string]string{ var mathToLLVMMapping = map[string]string{
"math.Ceil": "llvm.ceil.f64", "math.Ceil": "llvm.ceil.f64",
"math.Exp": "llvm.exp.f64", "math.Exp": "llvm.exp.f64",
+1 -78
View File
@@ -89,7 +89,6 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// growth. // growth.
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, mapKeyAlloca) b.CreateStore(key, mapKeyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
// Fetch the value from the hashmap. // Fetch the value from the hashmap.
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize} params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "") commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
@@ -134,7 +133,6 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
// key can be compared with runtime.memequal // key can be compared with runtime.memequal
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca) b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyPtr, valuePtr} params := []llvm.Value{m, keyPtr, valuePtr}
b.createRuntimeCall("hashmapBinarySet", params, "") b.createRuntimeCall("hashmapBinarySet", params, "")
b.emitLifetimeEnd(keyPtr, keySize) b.emitLifetimeEnd(keyPtr, keySize)
@@ -163,7 +161,6 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
} else if hashmapIsBinaryKey(keyType) { } else if hashmapIsBinaryKey(keyType) {
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca) b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyPtr} params := []llvm.Value{m, keyPtr}
b.createRuntimeCall("hashmapBinaryDelete", params, "") b.createRuntimeCall("hashmapBinaryDelete", params, "")
b.emitLifetimeEnd(keyPtr, keySize) b.emitLifetimeEnd(keyPtr, keySize)
@@ -243,8 +240,7 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
} }
// Returns true if this key type does not contain strings, interfaces etc., so // Returns true if this key type does not contain strings, interfaces etc., so
// can be compared with runtime.memequal. Note that padding bytes are undef // can be compared with runtime.memequal.
// and can alter two "equal" structs being equal when compared with memequal.
func hashmapIsBinaryKey(keyType types.Type) bool { func hashmapIsBinaryKey(keyType types.Type) bool {
switch keyType := keyType.(type) { switch keyType := keyType.(type) {
case *types.Basic: case *types.Basic:
@@ -267,76 +263,3 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
return false return false
} }
} }
func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
// We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
// To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
// offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
// we handle nested types. Next, we determine if there are any padding bytes before the next
// element and zero those as well.
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
switch llvmType.TypeKind() {
case llvm.IntegerTypeKind:
// no padding bytes
return nil
case llvm.PointerTypeKind:
// mo padding bytes
return nil
case llvm.ArrayTypeKind:
llvmArrayType := llvmType
llvmElemType := llvmType.ElementType()
for i := 0; i < llvmArrayType.ArrayLength(); i++ {
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
// zero any padding bytes in this element
b.zeroUndefBytes(llvmElemType, elemPtr)
}
case llvm.StructTypeKind:
llvmStructType := llvmType
numFields := llvmStructType.StructElementTypesCount()
llvmElementTypes := llvmStructType.StructElementTypes()
for i := 0; i < numFields; i++ {
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
// zero any padding bytes in this field
llvmElemType := llvmElementTypes[i]
b.zeroUndefBytes(llvmElemType, elemPtr)
// zero any padding bytes before the next field, if any
offset := b.targetData.ElementOffset(llvmStructType, i)
storeSize := b.targetData.TypeStoreSize(llvmElemType)
fieldEndOffset := offset + storeSize
var nextOffset uint64
if i < numFields-1 {
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
} else {
// Last field? Next offset is the total size of the allcoate struct.
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
}
if fieldEndOffset != nextOffset {
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
gepPtr := elemPtr
if gepPtr.Type() != b.i8ptrType {
gepPtr = b.CreateBitCast(gepPtr, b.i8ptrType, "") // LLVM 14
}
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), gepPtr, []llvm.Value{llvmStoreSize}, "")
if paddingStart.Type() != b.i8ptrType {
paddingStart = b.CreateBitCast(paddingStart, b.i8ptrType, "") // LLVM 14
}
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
}
}
}
return nil
}
+1 -1
View File
@@ -4,7 +4,7 @@ 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, i1, %runtime._interface }
%runtime._interface = type { ptr, ptr } %runtime._interface = type { i32, ptr }
%runtime._defer = type { i32, ptr } %runtime._defer = type { i32, ptr }
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
+5 -5
View File
@@ -3,7 +3,8 @@ source_filename = "gc.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._interface = type { ptr, ptr } %runtime.typecodeID = type { ptr, i32, ptr, ptr, i32 }
%runtime._interface = type { i32, ptr }
@main.scalar1 = hidden global ptr null, align 4 @main.scalar1 = hidden global ptr null, align 4
@main.scalar2 = hidden global ptr null, align 4 @main.scalar2 = hidden global ptr null, align 4
@@ -21,8 +22,8 @@ target triple = "wasm32-unknown-wasi"
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 8 @main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 8
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " } @"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" } @"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 16, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4 @"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { ptr null, i32 0, ptr null, ptr @"reflect/types.type:pointer:basic:complex128", i32 0 }
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:complex128" }, align 4 @"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:basic:complex128", i32 0, ptr null, ptr null, i32 0 }
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
@@ -128,8 +129,7 @@ entry:
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 { double, double }, ptr %0, i32 0, i32 1
store double %v.i, ptr %.repack1, align 8 store double %v.i, ptr %.repack1, align 8
%1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1 %1 = insertvalue %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:basic:complex128" to i32), ptr undef }, ptr %0, 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #2
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2
ret %runtime._interface %1 ret %runtime._interface %1
} }
+11 -11
View File
@@ -145,34 +145,34 @@ entry:
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0 declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
; 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(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8 %0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
store ptr %itf.value, ptr %0, align 4 store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1 %1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1
store ptr @"main$string", ptr %1, align 4 store ptr @"main$string", ptr %1, align 4
%.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1 %.repack1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1, i32 1
store i32 4, ptr %.repack1, align 4 store i32 4, ptr %.repack1, align 4
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2 %2 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 2
store ptr %itf.typecode, ptr %2, align 4 store i32 %itf.typecode, ptr %2, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #8 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #8
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) #8 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) #8
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #6 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, i32, ptr) #6
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 {
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 { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 1
%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 { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 2
%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 { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 3
%7 = load ptr, ptr %6, align 4 %7 = load i32, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #8 call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, i32 %7, ptr undef) #8
ret void ret void
} }
+11 -11
View File
@@ -154,35 +154,35 @@ entry:
declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0 declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0
; 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(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8 %0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8
store ptr %itf.value, ptr %0, align 4 store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1 %1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1
store ptr @"main$string", ptr %1, align 4 store ptr @"main$string", ptr %1, align 4
%.repack1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1, i32 1 %.repack1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1, i32 1
store i32 4, ptr %.repack1, align 4 store i32 4, ptr %.repack1, align 4
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2 %2 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 2
store ptr %itf.typecode, ptr %2, align 4 store i32 %itf.typecode, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8 call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #8
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #6 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, i32, ptr) #6
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #7 {
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 { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 1
%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 { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 2
%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 { ptr, ptr, i32, i32 }, ptr %0, i32 0, i32 3
%7 = load ptr, ptr %6, align 4 %7 = load i32, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #8 call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, i32 %7, ptr undef) #8
call void @runtime.deadlock(ptr undef) #8 call void @runtime.deadlock(ptr undef) #8
unreachable unreachable
} }
+35 -34
View File
@@ -3,17 +3,22 @@ source_filename = "interface.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._interface = type { ptr, ptr } %runtime.typecodeID = type { ptr, i32, ptr, ptr, i32 }
%runtime._interface = type { i32, ptr }
%runtime._string = type { ptr, i32 } %runtime._string = type { ptr, i32 }
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 2, ptr @"reflect/types.type:pointer:basic:int" }, align 4 @"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { ptr null, i32 0, ptr null, ptr @"reflect/types.type:pointer:basic:int", i32 0 }
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:int" }, align 4 @"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:basic:int", i32 0, ptr null, ptr null, i32 0 }
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:named:error" }, align 4 @"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:named:error", i32 0, ptr null, ptr null, i32 0 }
@"reflect/types.type:named:error" = linkonce_odr constant { i8, ptr, ptr } { i8 52, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, ptr null, ptr @"reflect/types.type:pointer:named:error", i32 ptrtoint (ptr @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.interface:interface{Error() string}$interface", i32 0, ptr null, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", i32 ptrtoint (ptr @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4 @"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x ptr] [ptr @"reflect/methods.Error() string"]
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4 @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, ptr null, ptr null, i32 0 }
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, ptr null, ptr null, i32 0 }
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { ptr @"reflect/types.interface:interface{String() string}$interface", i32 0, ptr null, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", i32 ptrtoint (ptr @"interface:{String:func:{}{basic:string}}.$typeassert" to i32) }
@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x ptr] [ptr @"reflect/methods.String() string"]
@"reflect/types.typeid:basic:int" = external constant i8 @"reflect/types.typeid:basic:int" = external constant i8
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
@@ -30,42 +35,42 @@ entry:
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null } ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:basic:int" to i32), ptr null }
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null } ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:basic:int" to i32), ptr null }
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null } ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:named:error" to i32), ptr null }
} }
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 { define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #6
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null } ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), ptr null }
} }
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i1 @main.isInt(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6 %typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #6
br i1 %typecode, label %typeassert.ok, label %typeassert.next br i1 %typecode, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry typeassert.next: ; preds = %typeassert.ok, %entry
@@ -75,12 +80,12 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next br label %typeassert.next
} }
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0 declare i1 @runtime.typeAssert(i32, ptr dereferenceable_or_null(1), ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i1 @main.isError(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6 %0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
br i1 %0, label %typeassert.ok, label %typeassert.next br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry typeassert.next: ; preds = %typeassert.ok, %entry
@@ -90,12 +95,10 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next br label %typeassert.next
} }
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i1 @main.isStringer(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #6 %0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #6
br i1 %0, label %typeassert.ok, label %typeassert.next br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry typeassert.next: ; preds = %typeassert.ok, %entry
@@ -105,28 +108,26 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next br label %typeassert.next
} }
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden i8 @main.callFooMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #6 %0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, i32 %itf.typecode, ptr undef) #6
ret i8 %0 ret i8 %0
} }
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #4 declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, i32, ptr) #4
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #6 %0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, i32 %itf.typecode, ptr undef) #6
%1 = extractvalue %runtime._string %0, 0 %1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6 call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6
ret %runtime._string %0 ret %runtime._string %0
} }
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #5 declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, i32, ptr) #5
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" } attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
-37
View File
@@ -1,37 +0,0 @@
package main
type hasPadding struct {
b1 bool
i int
b2 bool
}
type nestedPadding struct {
b bool
hasPadding
i int
}
//go:noinline
func testZeroGet(m map[hasPadding]int, s hasPadding) int {
return m[s]
}
//go:noinline
func testZeroSet(m map[hasPadding]int, s hasPadding) {
m[s] = 5
}
//go:noinline
func testZeroArrayGet(m map[[2]hasPadding]int, s [2]hasPadding) int {
return m[s]
}
//go:noinline
func testZeroArraySet(m map[[2]hasPadding]int, s [2]hasPadding) {
m[s] = 5
}
func main() {
}
-170
View File
@@ -1,170 +0,0 @@
; ModuleID = 'zeromap.go'
source_filename = "zeromap.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%main.hasPadding = type { i1, i32, i1 }
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4
%s = alloca %main.hasPadding, align 8
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s, align 8
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #4
store %main.hasPadding %2, ptr %s, align 8
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 8
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
%4 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #4
%5 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
%6 = load i32, ptr %hashmap.value, align 4
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret i32 %6
}
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
declare void @runtime.memzero(ptr, i32, ptr) #0
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #0
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
; Function Attrs: noinline nounwind
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4
%s = alloca %main.hasPadding, align 8
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s, align 8
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #4
store %main.hasPadding %2, ptr %s, align 8
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 8
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
%4 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #4
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret void
}
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #0
; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4
%s1 = alloca [2 x %main.hasPadding], align 8
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s1, align 8
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #4
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %s1, align 8
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #4
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #4
%2 = getelementptr inbounds i8, ptr %hashmap.key, i32 13
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #4
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 21
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
%4 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
%5 = load i32, ptr %hashmap.value, align 4
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret i32 %5
}
; Function Attrs: noinline nounwind
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4
%s1 = alloca [2 x %main.hasPadding], align 8
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s1, align 8
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #4
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %s1, align 8
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #4
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #4
%2 = getelementptr inbounds i8, ptr %hashmap.key, i32 13
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #4
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 21
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret void
}
; Function Attrs: nounwind
define hidden void @main.main(ptr %context) unnamed_addr #1 {
entry:
ret void
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { argmemonly nocallback nofree nosync nounwind willreturn }
attributes #4 = { nounwind }
+1 -1
View File
@@ -15,7 +15,7 @@ require (
github.com/mattn/go-tty v0.0.4 github.com/mattn/go-tty v0.0.4
go.bug.st/serial v1.3.5 go.bug.st/serial v1.3.5
golang.org/x/sys v0.4.0 golang.org/x/sys v0.4.0
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 golang.org/x/tools v0.5.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32 tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32
) )
+2 -2
View File
@@ -56,8 +56,8 @@ golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 h1:LHoToPgySGSr2NcUHbjENAidHz38RkKaNmmntwn9TjI= golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4=
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+1 -1
View File
@@ -12,7 +12,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.28.0-dev" const Version = "0.27.0-dev"
var ( var (
// This variable is set at build time using -ldflags parameters. // This variable is set at build time using -ldflags parameters.
+56 -30
View File
@@ -238,7 +238,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// which case this call won't even get to this point but will // which case this call won't even get to this point but will
// already be emitted in initAll. // already be emitted in initAll.
continue continue
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "runtime.hashmapInterfaceHash" || case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" ||
callFn.name == "os.runtime_args" || callFn.name == "internal/task.start" || callFn.name == "internal/task.Current": callFn.name == "os.runtime_args" || callFn.name == "internal/task.start" || callFn.name == "internal/task.Current":
// These functions should be run at runtime. Specifically: // These functions should be run at runtime. Specifically:
// * Print and panic functions are best emitted directly without // * Print and panic functions are best emitted directly without
@@ -378,6 +378,42 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():]) copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
dstObj.buffer = dstBuf dstObj.buffer = dstBuf
mem.put(dst.index(), dstObj) mem.put(dst.index(), dstObj)
case callFn.name == "(reflect.rawType).elem":
if r.debug {
fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
}
// Extract the type code global from the first parameter.
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
typecodeID := typecodeIDPtrToInt.Operand(0)
// Get the type class.
// See also: getClassAndValueFromTypeCode in transform/reflect.go.
typecodeName := typecodeID.Name()
const prefix = "reflect/types.type:"
if !strings.HasPrefix(typecodeName, prefix) {
panic("unexpected typecode name: " + typecodeName)
}
id := typecodeName[len(prefix):]
class := id[:strings.IndexByte(id, ':')]
value := id[len(class)+1:]
if class == "named" {
// Get the underlying type.
class = value[:strings.IndexByte(value, ':')]
value = value[len(class)+1:]
}
// Elem() is only valid for certain type classes.
switch class {
case "chan", "pointer", "slice", "array":
elementType := r.builder.CreateExtractValue(typecodeID.Initializer(), 0, "")
uintptrType := r.mod.Context().IntType(int(mem.r.pointerSize) * 8)
locals[inst.localIndex] = r.getValue(llvm.ConstPtrToInt(elementType, uintptrType))
default:
return nil, mem, r.errorAt(inst, fmt.Errorf("(reflect.Type).Elem() called on %s type", class))
}
case callFn.name == "runtime.typeAssert": case callFn.name == "runtime.typeAssert":
// This function must be implemented manually as it is normally // This function must be implemented manually as it is normally
// implemented by the interface lowering pass. // implemented by the interface lowering pass.
@@ -388,22 +424,15 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if err != nil { if err != nil {
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
actualType, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem) actualTypePtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
if err != nil { if err != nil {
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
if !actualType.IsAConstantInt().IsNil() && actualType.ZExtValue() == 0 { if !actualTypePtrToInt.IsAConstantInt().IsNil() && actualTypePtrToInt.ZExtValue() == 0 {
locals[inst.localIndex] = literalValue{uint8(0)} locals[inst.localIndex] = literalValue{uint8(0)}
break break
} }
// Strip pointer casts (bitcast, getelementptr). actualType := actualTypePtrToInt.Operand(0)
for !actualType.IsAConstantExpr().IsNil() {
opcode := actualType.Opcode()
if opcode != llvm.GetElementPtr && opcode != llvm.BitCast {
break
}
actualType = actualType.Operand(0)
}
if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") { if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") {
locals[inst.localIndex] = literalValue{uint8(1)} locals[inst.localIndex] = literalValue{uint8(1)}
} else { } else {
@@ -419,12 +448,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if err != nil { if err != nil {
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
methodSetPtr, err := mem.load(typecodePtr.addOffset(-int64(r.pointerSize)), r.pointerSize).asPointer(r) methodSetPtr, err := mem.load(typecodePtr.addOffset(r.pointerSize*2), r.pointerSize).asPointer(r)
if err != nil { if err != nil {
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer() methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
llvmFn := inst.llvmInst.CalledValue() llvmFn := inst.llvmInst.CalledValue()
methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods") methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
methodSetString := methodSetAttr.GetStringValue() methodSetString := methodSetAttr.GetStringValue()
@@ -432,9 +460,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// Make a set of all the methods on the concrete type, for // Make a set of all the methods on the concrete type, for
// easier checking in the next step. // easier checking in the next step.
concreteTypeMethods := map[string]struct{}{} concreteTypeMethods := map[string]struct{}{}
for i := 0; i < numMethods; i++ { for i := 0; i < methodSet.Type().ArrayLength(); i++ {
methodInfo := r.builder.CreateExtractValue(methodSet, 1, "") methodInfo := r.builder.CreateExtractValue(methodSet, i, "")
name := r.builder.CreateExtractValue(methodInfo, i, "").Name() name := r.builder.CreateExtractValue(methodInfo, 0, "").Name()
concreteTypeMethods[name] = struct{}{} concreteTypeMethods[name] = struct{}{}
} }
@@ -460,16 +488,15 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
fmt.Fprintln(os.Stderr, indent+"invoke method:", operands[1:]) fmt.Fprintln(os.Stderr, indent+"invoke method:", operands[1:])
} }
// Load the type code and method set of the interface value. // Load the type code of the interface value.
typecodePtr, err := operands[len(operands)-2].asPointer(r) typecodeIDBitCast, err := operands[len(operands)-2].toLLVMValue(inst.llvmInst.Operand(len(operands)-3).Type(), &mem)
if err != nil { if err != nil {
return nil, mem, r.errorAt(inst, err) return nil, mem, r.errorAt(inst, err)
} }
methodSetPtr, err := mem.load(typecodePtr.addOffset(-int64(r.pointerSize)), r.pointerSize).asPointer(r) typecodeID := typecodeIDBitCast.Operand(0).Initializer()
if err != nil {
return nil, mem, r.errorAt(inst, err) // Load the method set, which is part of the typecodeID object.
} methodSet := stripPointerCasts(r.builder.CreateExtractValue(typecodeID, 2, "")).Initializer()
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
// We don't need to load the interface method set. // We don't need to load the interface method set.
@@ -481,14 +508,13 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// Iterate through all methods, looking for the one method that // Iterate through all methods, looking for the one method that
// should be returned. // should be returned.
numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue()) numMethods := methodSet.Type().ArrayLength()
var method llvm.Value var method llvm.Value
for i := 0; i < numMethods; i++ { for i := 0; i < numMethods; i++ {
methodSignatureAgg := r.builder.CreateExtractValue(methodSet, 1, "") methodSignatureAgg := r.builder.CreateExtractValue(methodSet, i, "")
methodSignature := r.builder.CreateExtractValue(methodSignatureAgg, i, "") methodSignature := r.builder.CreateExtractValue(methodSignatureAgg, 0, "")
if methodSignature == signature { if methodSignature == signature {
methodAgg := r.builder.CreateExtractValue(methodSet, 2, "") method = r.builder.CreateExtractValue(methodSignatureAgg, 1, "").Operand(0)
method = r.builder.CreateExtractValue(methodAgg, i, "")
} }
} }
if method.IsNil() { if method.IsNil() {
@@ -659,7 +685,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
} }
continue continue
} }
ptr = ptr.addOffset(int64(offset)) ptr = ptr.addOffset(uint32(offset))
locals[inst.localIndex] = ptr locals[inst.localIndex] = ptr
if r.debug { if r.debug {
fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr) fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr)
@@ -758,7 +784,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
case llvm.Add: case llvm.Add:
// This likely means this is part of a // This likely means this is part of a
// unsafe.Pointer(uintptr(ptr) + offset) pattern. // unsafe.Pointer(uintptr(ptr) + offset) pattern.
lhsPtr = lhsPtr.addOffset(int64(rhs.Uint())) lhsPtr = lhsPtr.addOffset(uint32(rhs.Uint()))
locals[inst.localIndex] = lhsPtr locals[inst.localIndex] = lhsPtr
continue continue
case llvm.Xor: case llvm.Xor:
+2 -2
View File
@@ -501,7 +501,7 @@ func (v pointerValue) offset() uint32 {
// addOffset essentially does a GEP operation (pointer arithmetic): it adds the // addOffset essentially does a GEP operation (pointer arithmetic): it adds the
// offset to the pointer. It also checks that the offset doesn't overflow the // offset to the pointer. It also checks that the offset doesn't overflow the
// maximum offset size (which is 4GB). // maximum offset size (which is 4GB).
func (v pointerValue) addOffset(offset int64) pointerValue { func (v pointerValue) addOffset(offset uint32) pointerValue {
result := pointerValue{v.pointer + uint64(offset)} result := pointerValue{v.pointer + uint64(offset)}
if checks && v.index() != result.index() { if checks && v.index() != result.index() {
panic("interp: offset out of range") panic("interp: offset out of range")
@@ -815,7 +815,7 @@ func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
// as a ptrtoint, so that they can be used in certain // as a ptrtoint, so that they can be used in certain
// optimizations. // optimizations.
name := elementType.StructName() name := elementType.StructName()
if name == "runtime.funcValueWithSignature" { if name == "runtime.typecodeID" || name == "runtime.funcValueWithSignature" {
uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8) uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8)
field = llvm.ConstPtrToInt(field, uintptrType) field = llvm.ConstPtrToInt(field, uintptrType)
} }
+8 -7
View File
@@ -1,16 +1,17 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux" target triple = "x86_64--linux"
%runtime.typecodeID = type { %runtime.typecodeID*, i64, %runtime.interfaceMethodInfo* }
%runtime.interfaceMethodInfo = type { i8*, i64 }
@main.v1 = global i1 0 @main.v1 = global i1 0
@main.v2 = global i1 0 @main.v2 = global i1 0
@"reflect/types.type:named:main.foo" = private constant { i8, i8*, i8* } { i8 34, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:named:main.foo", i32 0, i32 0), i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:basic:int", i32 0, i32 0) }, align 4 @"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0, %runtime.interfaceMethodInfo* null }
@"reflect/types.type:pointer:named:main.foo" = external constant { i8, i8* }
@"reflect/types.typeid:named:main.foo" = external constant i8 @"reflect/types.typeid:named:main.foo" = external constant i8
@"reflect/types.type:basic:int" = private constant { i8, i8* } { i8 2, i8* getelementptr inbounds ({ i8, i8* }, { i8, i8* }* @"reflect/types.type:pointer:basic:int", i32 0, i32 0) }, align 4 @"reflect/types.type:basic:int" = external constant %runtime.typecodeID
@"reflect/types.type:pointer:basic:int" = external constant { i8, i8* }
declare i1 @runtime.typeAssert(i8*, i8*, i8*, i8*) declare i1 @runtime.typeAssert(i64, i8*, i8*, i8*)
define void @runtime.initAll() unnamed_addr { define void @runtime.initAll() unnamed_addr {
entry: entry:
@@ -21,9 +22,9 @@ entry:
define internal void @main.init() unnamed_addr { define internal void @main.init() unnamed_addr {
entry: entry:
; Test type asserts. ; Test type asserts.
%typecode = call i1 @runtime.typeAssert(i8* getelementptr inbounds ({ i8, i8*, i8* }, { i8, i8*, i8* }* @"reflect/types.type:named:main.foo", i32 0, i32 0), i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null) %typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null)
store i1 %typecode, i1* @main.v1 store i1 %typecode, i1* @main.v1
%typecode2 = call i1 @runtime.typeAssert(i8* null, i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null) %typecode2 = call i1 @runtime.typeAssert(i64 0, i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null)
store i1 %typecode2, i1* @main.v2 store i1 %typecode2, i1* @main.v2
ret void ret void
} }
+1 -1
View File
@@ -644,7 +644,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
case "qemu-user": case "qemu-user":
port = ":1234" port = ":1234"
// Run in an emulator. // Run in an emulator.
args := append([]string{"-g", "1234"}, emulator[1:]...) args := append(emulator[1:], "-g", "1234")
daemon = executeCommand(config.Options, emulator[0], args...) daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
+9 -6
View File
@@ -180,8 +180,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
// Skip the ones that aren't. // Skip the ones that aren't.
switch name { switch name {
case "reflect.go": case "reflect.go":
// Reflect tests do not run correctly, probably because of the // Reflect tests do not work due to type code issues.
// limited amount of memory.
continue continue
case "gc.go": case "gc.go":
@@ -189,16 +188,20 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
continue continue
case "json.go", "stdlib.go", "testing.go": case "json.go", "stdlib.go", "testing.go":
// Too big for AVR. Doesn't fit in flash/RAM. // Breaks interp.
continue continue
case "math.go": case "math.go":
// Needs newer picolibc version (for sqrt). // Stuck somewhere, not sure what's happening.
continue continue
case "cgo/": case "cgo/":
// CGo function pointers don't work on AVR (needs LLVM 16 and // CGo does not work on AVR.
// some compiler changes). continue
case "timers.go":
// Doesn't compile:
// panic: compiler: could not store type code number inside interface type code
continue continue
default: default:
-57
View File
@@ -1,57 +0,0 @@
package main
import (
"machine"
"time"
)
var (
err error
message = "1234567887654321123456788765432112345678876543211234567887654321"
)
func main() {
time.Sleep(3 * time.Second)
// Print out general information
println("Flash data start: ", machine.FlashDataStart())
println("Flash data end: ", machine.FlashDataEnd())
println("Flash data size, bytes:", machine.Flash.Size())
println("Flash write block size:", machine.Flash.WriteBlockSize())
println("Flash erase block size:", machine.Flash.EraseBlockSize())
println()
flash := machine.OpenFlashBuffer(machine.Flash, machine.FlashDataStart())
original := make([]byte, len(message))
saved := make([]byte, len(message))
// Read flash contents on start (data shall survive power off)
print("Reading data from flash: ")
_, err = flash.Read(original)
checkError(err)
println(string(original))
// Write the message to flash
print("Writing data to flash: ")
flash.Seek(0, 0) // rewind back to beginning
_, err = flash.Write([]byte(message))
checkError(err)
println(string(message))
// Read back flash contents after write (verify data is the same as written)
print("Reading data back from flash: ")
flash.Seek(0, 0) // rewind back to beginning
_, err = flash.Read(saved)
checkError(err)
println(string(saved))
println()
}
func checkError(err error) {
if err != nil {
for {
println(err.Error())
time.Sleep(time.Second)
}
}
}
-35
View File
@@ -1,35 +0,0 @@
//go:build rp2040
package main
// This example demonstrates scheduling a delayed interrupt by real time clock.
//
// An interrupt may execute user callback function or used for its side effects
// like waking up from sleep or dormant states.
//
// The interrupt can be configured to repeat.
//
// There is no separate method to disable interrupt, use 0 delay for that.
//
// Unfortunately, it is not possible to use time.Duration to work with RTC directly,
// that would introduce a circular dependency between "machine" and "time" packages.
import (
"fmt"
"machine"
"time"
)
func main() {
// Schedule and enable recurring interrupt.
// The callback function is executed in the context of an interrupt handler,
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
delay := time.Minute + 12*time.Second
machine.RTC.SetInterrupt(uint32(delay.Seconds()), true, func() { println("Peekaboo!") })
for {
fmt.Printf("%v\r\n", time.Now().Format(time.RFC3339))
time.Sleep(1 * time.Second)
}
}
+1 -7
View File
@@ -2,10 +2,7 @@
package task package task
import ( import "unsafe"
"runtime/interrupt"
"unsafe"
)
//go:linkname runtimePanic runtime.runtimePanic //go:linkname runtimePanic runtime.runtimePanic
func runtimePanic(str string) func runtimePanic(str string)
@@ -48,9 +45,6 @@ func Pause() {
if *currentTask.state.canaryPtr != stackCanary { if *currentTask.state.canaryPtr != stackCanary {
runtimePanic("goroutine stack overflow") runtimePanic("goroutine stack overflow")
} }
if interrupt.In() {
runtimePanic("blocked inside interrupt")
}
currentTask.state.pause() currentTask.state.pause()
} }
+5 -10
View File
@@ -4,6 +4,7 @@ _tinygo_startTask:
#else #else
.section .text.tinygo_startTask .section .text.tinygo_startTask
.global tinygo_startTask .global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask: tinygo_startTask:
#endif #endif
.cfi_startproc .cfi_startproc
@@ -34,6 +35,7 @@ tinygo_startTask:
#endif #endif
.cfi_endproc .cfi_endproc
#ifndef __MACH__ #ifndef __MACH__
.size tinygo_startTask, .-tinygo_startTask
#endif #endif
@@ -42,6 +44,7 @@ tinygo_startTask:
_tinygo_swapTask: _tinygo_swapTask:
#else #else
.global tinygo_swapTask .global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask: tinygo_swapTask:
#endif #endif
// This function gets the following parameters: // This function gets the following parameters:
@@ -49,16 +52,12 @@ tinygo_swapTask:
// x1 = oldStack *uintptr // x1 = oldStack *uintptr
// Save all callee-saved registers: // Save all callee-saved registers:
stp x19, x20, [sp, #-160]! stp x19, x20, [sp, #-96]!
stp x21, x22, [sp, #16] stp x21, x22, [sp, #16]
stp x23, x24, [sp, #32] stp x23, x24, [sp, #32]
stp x25, x26, [sp, #48] stp x25, x26, [sp, #48]
stp x27, x28, [sp, #64] stp x27, x28, [sp, #64]
stp x29, x30, [sp, #80] stp x29, x30, [sp, #80]
stp d8, d9, [sp, #96]
stp d10, d11, [sp, #112]
stp d12, d13, [sp, #128]
stp d14, d15, [sp, #144]
// Save the current stack pointer in oldStack. // Save the current stack pointer in oldStack.
mov x8, sp mov x8, sp
@@ -68,14 +67,10 @@ tinygo_swapTask:
mov sp, x0 mov sp, x0
// Restore stack state and return. // Restore stack state and return.
ldp d14, d15, [sp, #144]
ldp d12, d13, [sp, #128]
ldp d10, d11, [sp, #112]
ldp d8, d9, [sp, #96]
ldp x29, x30, [sp, #80] ldp x29, x30, [sp, #80]
ldp x27, x28, [sp, #64] ldp x27, x28, [sp, #64]
ldp x25, x26, [sp, #48] ldp x25, x26, [sp, #48]
ldp x23, x24, [sp, #32] ldp x23, x24, [sp, #32]
ldp x21, x22, [sp, #16] ldp x21, x22, [sp, #16]
ldp x19, x20, [sp], #160 ldp x19, x20, [sp], #96
ret ret
+2 -10
View File
@@ -1,4 +1,4 @@
//go:build scheduler.tasks && arm64 //go:build scheduler.tasks && arm64 && !windows
package task package task
@@ -21,16 +21,8 @@ type calleeSavedRegs struct {
x27 uintptr x27 uintptr
x28 uintptr x28 uintptr
x29 uintptr x29 uintptr
pc uintptr // aka x30 aka LR
d8 uintptr pc uintptr // aka x30 aka LR
d9 uintptr
d10 uintptr
d11 uintptr
d12 uintptr
d13 uintptr
d14 uintptr
d15 uintptr
} }
// archInit runs architecture-specific setup for the goroutine startup. // archInit runs architecture-specific setup for the goroutine startup.
@@ -0,0 +1,65 @@
.section .text.tinygo_startTask,"ax"
.global tinygo_startTask
tinygo_startTask:
.cfi_startproc
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, x19 contains the pc of the to-be-started function and
// x20 contains the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Indicate to the unwinder that there is nothing to unwind, this is the
// root frame. It avoids the following (bogus) error message in GDB:
// Backtrace stopped: previous frame identical to this frame (corrupt stack?)
.cfi_undefined lr
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
mov x0, x20
// Branch to the "goroutine start" function. By using blx instead of bx,
// we'll return here instead of tail calling.
blr x19
// After return, exit this goroutine. This is a tail call.
b tinygo_pause
.cfi_endproc
.global tinygo_swapTask
tinygo_swapTask:
// This function gets the following parameters:
// x0 = newStack uintptr
// x1 = oldStack *uintptr
// Save all callee-saved registers:
stp x19, x20, [sp, #-160]!
stp x21, x22, [sp, #16]
stp x23, x24, [sp, #32]
stp x25, x26, [sp, #48]
stp x27, x28, [sp, #64]
stp x29, x30, [sp, #80]
stp d8, d9, [sp, #96]
stp d10, d11, [sp, #112]
stp d12, d13, [sp, #128]
stp d14, d15, [sp, #144]
// Save the current stack pointer in oldStack.
mov x8, sp
str x8, [x1]
// Switch to the new stack pointer.
mov sp, x0
// Restore stack state and return.
ldp d14, d15, [sp, #144]
ldp d12, d13, [sp, #128]
ldp d10, d11, [sp, #112]
ldp d8, d9, [sp, #96]
ldp x29, x30, [sp, #80]
ldp x27, x28, [sp, #64]
ldp x25, x26, [sp, #48]
ldp x23, x24, [sp, #32]
ldp x21, x22, [sp, #16]
ldp x19, x20, [sp], #160
ret
@@ -0,0 +1,72 @@
//go:build scheduler.tasks && arm64 && windows
package task
import "unsafe"
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored
// when switching between tasks. Also see task_stack_arm64_windows.S that
// relies on the exact layout of this struct.
type calleeSavedRegs struct {
x19 uintptr
x20 uintptr
x21 uintptr
x22 uintptr
x23 uintptr
x24 uintptr
x25 uintptr
x26 uintptr
x27 uintptr
x28 uintptr
x29 uintptr
pc uintptr // aka x30 aka LR
d8 uintptr
d9 uintptr
d10 uintptr
d11 uintptr
d12 uintptr
d13 uintptr
d14 uintptr
d15 uintptr
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_arm64_windows.S).
// This assembly code calls a function (passed in x19) with a single argument
// (passed in x20). After the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in x19.
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
r.x19 = fn
// Pass the pointer to the arguments struct in x20.
r.x20 = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
-13
View File
@@ -56,18 +56,6 @@ var (
} }
DefaultUART = UART0 DefaultUART = UART0
// Since we treat UART1 as zero, let's also call it by the real name
UART1 = UART0
// UART2
UART2 = &_UART2
_UART2 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
TxAltFuncSelector: AF7_USART1_2,
RxAltFuncSelector: AF7_USART1_2,
}
// I2C Busses // I2C Busses
I2C1 = &I2C{ I2C1 = &I2C{
Bus: stm32.I2C1, Bus: stm32.I2C1,
@@ -84,5 +72,4 @@ var (
func init() { func init() {
// Enable UARTs Interrupts // Enable UARTs Interrupts
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART0.handleInterrupt) UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART0.handleInterrupt)
UART2.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART2.handleInterrupt)
} }
+1 -1
View File
@@ -29,7 +29,7 @@ const (
// Analog pins // Analog pins
const ( const (
A0 = D0 A0 = D1
A1 = D1 A1 = D1
A2 = D2 A2 = D2
A3 = D3 A3 = D3
+4 -4
View File
@@ -50,11 +50,11 @@ const (
// I2C pins // I2C pins
const ( const (
I2C0_SDA_PIN Pin = D2 I2C0_SDA_PIN Pin = D4
I2C0_SCL_PIN Pin = D3 I2C0_SCL_PIN Pin = D5
I2C1_SDA_PIN Pin = D4 I2C1_SDA_PIN Pin = NoPin
I2C1_SCL_PIN Pin = D5 I2C1_SCL_PIN Pin = NoPin
) )
// SPI pins // SPI pins
-144
View File
@@ -1,144 +0,0 @@
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx
package machine
import (
"errors"
"io"
"unsafe"
)
//go:extern __flash_data_start
var flashDataStart [0]byte
//go:extern __flash_data_end
var flashDataEnd [0]byte
// Return the start of the writable flash area, aligned on a page boundary. This
// is usually just after the program and static data.
func FlashDataStart() uintptr {
pagesize := uintptr(eraseBlockSize())
return (uintptr(unsafe.Pointer(&flashDataStart)) + pagesize - 1) &^ (pagesize - 1)
}
// Return the end of the writable flash area. Usually this is the address one
// past the end of the on-chip flash.
func FlashDataEnd() uintptr {
return uintptr(unsafe.Pointer(&flashDataEnd))
}
var (
errFlashCannotErasePage = errors.New("cannot erase flash page")
errFlashInvalidWriteLength = errors.New("write flash data must align to correct number of bits")
errFlashNotAllowedWriteData = errors.New("not allowed to write flash data")
errFlashCannotWriteData = errors.New("cannot write flash data")
errFlashCannotReadPastEOF = errors.New("cannot read beyond end of flash data")
errFlashCannotWritePastEOF = errors.New("cannot write beyond end of flash data")
)
// BlockDevice is the raw device that is meant to store flash data.
type BlockDevice interface {
// ReadAt reads the given number of bytes from the block device.
io.ReaderAt
// WriteAt writes the given number of bytes to the block device.
io.WriterAt
// Size returns the number of bytes in this block device.
Size() int64
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
WriteBlockSize() int64
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
EraseBlockSize() int64
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
EraseBlocks(start, len int64) error
}
// FlashBuffer implements the ReadWriteCloser interface using the BlockDevice interface.
type FlashBuffer struct {
b BlockDevice
start uintptr
current uintptr
}
// OpenFlashBuffer opens a FlashBuffer.
func OpenFlashBuffer(b BlockDevice, address uintptr) *FlashBuffer {
return &FlashBuffer{b: b, start: address, current: address}
}
// Read data from a FlashBuffer.
func (fl *FlashBuffer) Read(p []byte) (n int, err error) {
fl.b.ReadAt(p, int64(fl.current))
fl.current += uintptr(len(p))
return len(p), nil
}
// Write data to a FlashBuffer.
func (fl *FlashBuffer) Write(p []byte) (n int, err error) {
// any new pages needed?
// NOTE probably will not work as expected if you try to write over page boundary
// of pages with different sizes.
pagesize := uintptr(fl.b.EraseBlockSize())
currentPageCount := (fl.current - fl.start + pagesize - 1) / pagesize
totalPagesNeeded := (fl.current - fl.start + uintptr(len(p)) + pagesize - 1) / pagesize
if currentPageCount == totalPagesNeeded {
// just write the data
n, err := fl.b.WriteAt(p, int64(fl.current))
if err != nil {
return 0, err
}
fl.current += uintptr(n)
return n, nil
}
// erase enough blocks to hold the data
page := fl.flashPageFromAddress(fl.start + (currentPageCount * pagesize))
fl.b.EraseBlocks(page, int64(totalPagesNeeded-currentPageCount))
// write the data
for i := 0; i < len(p); i += int(pagesize) {
var last int = i + int(pagesize)
if i+int(pagesize) > len(p) {
last = len(p)
}
_, err := fl.b.WriteAt(p[i:last], int64(fl.current))
if err != nil {
return 0, err
}
fl.current += uintptr(last - i)
}
return len(p), nil
}
// Close the FlashBuffer.
func (fl *FlashBuffer) Close() error {
return nil
}
// Seek implements io.Seeker interface, but with limitations.
// You can only seek relative to the start.
// Also, you cannot use seek before write operations, only read.
func (fl *FlashBuffer) Seek(offset int64, whence int) (int64, error) {
fl.current = fl.start + uintptr(offset)
return offset, nil
}
// calculate page number from address
func (fl *FlashBuffer) flashPageFromAddress(address uintptr) int64 {
return int64(address-memoryStart) / fl.b.EraseBlockSize()
}
+28 -28
View File
@@ -406,61 +406,61 @@ func (p Pin) getPinCfg() uint8 {
return uint8(sam.PORT.PINCFG1_0.Get()>>16) & 0xff return uint8(sam.PORT.PINCFG1_0.Get()>>16) & 0xff
case 35: // PB03 case 35: // PB03
return uint8(sam.PORT.PINCFG1_0.Get()>>24) & 0xff return uint8(sam.PORT.PINCFG1_0.Get()>>24) & 0xff
case 36: // PB04 case 37: // PB04
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
case 37: // PB05 case 38: // PB05
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
case 38: // PB06 case 39: // PB06
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
case 39: // PB07 case 40: // PB07
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
case 40: // PB08 case 41: // PB08
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
case 41: // PB09 case 42: // PB09
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
case 42: // PB10 case 43: // PB10
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
case 43: // PB11 case 44: // PB11
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
case 44: // PB12 case 45: // PB12
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
case 45: // PB13 case 46: // PB13
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
case 46: // PB14 case 47: // PB14
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
case 47: // PB15 case 48: // PB15
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
case 48: // PB16 case 49: // PB16
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
case 49: // PB17 case 50: // PB17
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
case 50: // PB18 case 51: // PB18
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
case 51: // PB19 case 52: // PB19
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
case 52: // PB20 case 53: // PB20
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
case 53: // PB21 case 54: // PB21
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
case 54: // PB22 case 55: // PB22
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
case 55: // PB23 case 56: // PB23
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
case 56: // PB24 case 57: // PB24
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
case 57: // PB25 case 58: // PB25
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
case 58: // PB26 case 59: // PB26
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
case 59: // PB27 case 60: // PB27
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
case 60: // PB28 case 61: // PB28
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
case 61: // PB29 case 62: // PB29
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
case 62: // PB30 case 63: // PB30
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
case 63: // PB31 case 64: // PB31
return uint8(sam.PORT.PINCFG1_28.Get()>>24) & 0xff return uint8(sam.PORT.PINCFG1_28.Get()>>24) & 0xff
default: default:
return 0 return 0
-108
View File
@@ -3,9 +3,7 @@
package machine package machine
import ( import (
"bytes"
"device/nrf" "device/nrf"
"encoding/binary"
"runtime/interrupt" "runtime/interrupt"
"unsafe" "unsafe"
) )
@@ -384,109 +382,3 @@ func ReadTemperature() int32 {
nrf.TEMP.EVENTS_DATARDY.Set(0) nrf.TEMP.EVENTS_DATARDY.Set(0)
return temp return temp
} }
const memoryStart = 0x0
// compile-time check for ensuring we fulfill BlockDevice interface
var _ BlockDevice = flashBlockDevice{}
var Flash flashBlockDevice
type flashBlockDevice struct {
}
// ReadAt reads the given number of bytes from the block device.
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotReadPastEOF
}
data := unsafe.Slice((*byte)(unsafe.Pointer(FlashDataStart()+uintptr(off))), len(p))
copy(p, data)
return len(p), nil
}
// WriteAt writes the given number of bytes to the block device.
// Only double-word (64 bits) length data can be programmed. See rm0461 page 78.
// If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF
}
address := FlashDataStart() + uintptr(off)
padded := f.pad(p)
waitWhileFlashBusy()
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen)
defer nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Ren)
for j := 0; j < len(padded); j += int(f.WriteBlockSize()) {
// write word
*(*uint32)(unsafe.Pointer(address)) = binary.LittleEndian.Uint32(padded[j : j+int(f.WriteBlockSize())])
address += uintptr(f.WriteBlockSize())
waitWhileFlashBusy()
}
return len(padded), nil
}
// Size returns the number of bytes in this block device.
func (f flashBlockDevice) Size() int64 {
return int64(FlashDataEnd() - FlashDataStart())
}
const writeBlockSize = 4
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
func (f flashBlockDevice) WriteBlockSize() int64 {
return writeBlockSize
}
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
func (f flashBlockDevice) EraseBlockSize() int64 {
return eraseBlockSize()
}
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
waitWhileFlashBusy()
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Een)
defer nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Ren)
for i := start; i < start+len; i++ {
nrf.NVMC.ERASEPAGE.Set(uint32(address))
waitWhileFlashBusy()
address += uintptr(f.EraseBlockSize())
}
return nil
}
// pad data if needed so it is long enough for correct byte alignment on writes.
func (f flashBlockDevice) pad(p []byte) []byte {
paddingNeeded := f.WriteBlockSize() - (int64(len(p)) % f.WriteBlockSize())
if paddingNeeded == 0 {
return p
}
padding := bytes.Repeat([]byte{0xff}, int(paddingNeeded))
return append(p, padding...)
}
func waitWhileFlashBusy() {
for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready {
}
}
-6
View File
@@ -6,12 +6,6 @@ import (
"device/nrf" "device/nrf"
) )
const eraseBlockSizeValue = 1024
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
// Get peripheral and pin number for this GPIO pin. // Get peripheral and pin number for this GPIO pin.
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) { func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
return nrf.GPIO, uint32(p) return nrf.GPIO, uint32(p)
-6
View File
@@ -63,9 +63,3 @@ var (
PWM1 = &PWM{PWM: nrf.PWM1} PWM1 = &PWM{PWM: nrf.PWM1}
PWM2 = &PWM{PWM: nrf.PWM2} PWM2 = &PWM{PWM: nrf.PWM2}
) )
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
-6
View File
@@ -84,9 +84,3 @@ var (
PWM2 = &PWM{PWM: nrf.PWM2} PWM2 = &PWM{PWM: nrf.PWM2}
PWM3 = &PWM{PWM: nrf.PWM3} PWM3 = &PWM{PWM: nrf.PWM3}
) )
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
-6
View File
@@ -102,9 +102,3 @@ func (pdm *PDM) Read(buf []int16) (uint32, error) {
return uint32(len(buf)), nil return uint32(len(buf)), nil
} }
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
+1 -20
View File
@@ -57,8 +57,6 @@ var (
ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved") ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
ErrI2CGeneric = errors.New("i2c error") ErrI2CGeneric = errors.New("i2c error")
ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable") ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
errInvalidI2CSDA = errors.New("invalid I2C SDA pin")
errInvalidI2CSCL = errors.New("invalid I2C SCL pin")
) )
// Tx performs a write and then a read transfer placing the result in // Tx performs a write and then a read transfer placing the result in
@@ -92,7 +90,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
// SCL: 3, 7, 11, 15, 19, 27 // SCL: 3, 7, 11, 15, 19, 27
func (i2c *I2C) Configure(config I2CConfig) error { func (i2c *I2C) Configure(config I2CConfig) error {
const defaultBaud uint32 = 100_000 // 100kHz standard mode const defaultBaud uint32 = 100_000 // 100kHz standard mode
if config.SCL == 0 && config.SDA == 0 { if config.SCL == 0 {
// If config pins are zero valued or clock pin is invalid then we set default values. // If config pins are zero valued or clock pin is invalid then we set default values.
switch i2c.Bus { switch i2c.Bus {
case rp.I2C0: case rp.I2C0:
@@ -103,23 +101,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
config.SDA = I2C1_SDA_PIN config.SDA = I2C1_SDA_PIN
} }
} }
var okSCL, okSDA bool
switch i2c.Bus {
case rp.I2C0:
okSCL = (config.SCL+3)%4 == 0
okSDA = (config.SDA+4)%4 == 0
case rp.I2C1:
okSCL = (config.SCL+1)%4 == 0
okSDA = (config.SDA+2)%4 == 0
}
switch {
case !okSCL:
return errInvalidI2CSCL
case !okSDA:
return errInvalidI2CSDA
}
if config.Frequency == 0 { if config.Frequency == 0 {
config.Frequency = defaultBaud config.Frequency = defaultBaud
} }
-240
View File
@@ -1,240 +0,0 @@
//go:build rp2040
// Implementation based on code located here:
// https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_rtc/rtc.c
package machine
import (
"device/rp"
"errors"
"runtime/interrupt"
"unsafe"
)
type rtcType rp.RTC_Type
type rtcTime struct {
Year int16
Month int8
Day int8
Dotw int8
Hour int8
Min int8
Sec int8
}
var RTC = (*rtcType)(unsafe.Pointer(rp.RTC))
const (
second = 1
minute = 60 * second
hour = 60 * minute
day = 24 * hour
)
var (
rtcAlarmRepeats bool
rtcCallback func()
rtcEpoch = rtcTime{
Year: 1970, Month: 1, Day: 1, Dotw: 4, Hour: 0, Min: 0, Sec: 0,
}
)
var (
ErrRtcDelayTooSmall = errors.New("RTC interrupt deplay is too small, shall be at least 1 second")
ErrRtcDelayTooLarge = errors.New("RTC interrupt deplay is too large, shall be no more than 1 day")
)
// SetInterrupt configures delayed and optionally recurring interrupt by real time clock.
//
// Delay is specified in whole seconds, allowed range depends on platform.
// Zero delay disables previously configured interrupt, if any.
//
// RP2040 implementation allows delay to be up to 1 day, otherwise a respective error is emitted.
func (rtc *rtcType) SetInterrupt(delay uint32, repeat bool, callback func()) error {
// Verify delay range
if delay > day {
return ErrRtcDelayTooLarge
}
// De-configure delayed interrupt if delay is zero
if delay == 0 {
rtc.disableInterruptMatch()
return nil
}
// Configure delayed interrupt
rtc.setDivider()
rtcAlarmRepeats = repeat
rtcCallback = callback
err := rtc.setTime(rtcEpoch)
if err != nil {
return err
}
rtc.setAlarm(toAlarmTime(delay), callback)
return nil
}
func toAlarmTime(delay uint32) rtcTime {
result := rtcEpoch
remainder := delay + 1 // needed "+1", otherwise alarm fires one second too early
if remainder >= hour {
result.Hour = int8(remainder / hour)
remainder %= hour
}
if remainder >= minute {
result.Min = int8(remainder / minute)
remainder %= minute
}
result.Sec = int8(remainder)
return result
}
func (rtc *rtcType) setDivider() {
// Get clk_rtc freq and make sure it is running
rtcFreq := configuredFreq[clkRTC]
if rtcFreq == 0 {
panic("can not set RTC divider, clock is not running")
}
// Take rtc out of reset now that we know clk_rtc is running
resetBlock(rp.RESETS_RESET_RTC)
unresetBlockWait(rp.RESETS_RESET_RTC)
// Set up the 1 second divider.
// If rtc_freq is 400 then clkdiv_m1 should be 399
rtcFreq -= 1
// Check the freq is not too big to divide
if rtcFreq > rp.RTC_CLKDIV_M1_CLKDIV_M1_Msk {
panic("can not set RTC divider, clock frequency is too big to divide")
}
// Write divide value
rtc.CLKDIV_M1.Set(rtcFreq)
}
// setTime configures RTC with supplied time, initialises and activates it.
func (rtc *rtcType) setTime(t rtcTime) error {
// Disable RTC and wait while it is still running
rtc.CTRL.Set(0)
for rtc.isActive() {
}
rtc.SETUP_0.Set((uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos) |
(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos) |
(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos))
rtc.SETUP_1.Set((uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos) |
(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos) |
(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos) |
(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos))
// Load setup values into RTC clock domain
rtc.CTRL.SetBits(rp.RTC_CTRL_LOAD)
// Enable RTC and wait for it to be running
rtc.CTRL.SetBits(rp.RTC_CTRL_RTC_ENABLE)
for !rtc.isActive() {
}
return nil
}
func (rtc *rtcType) isActive() bool {
return rtc.CTRL.HasBits(rp.RTC_CTRL_RTC_ACTIVE)
}
// setAlarm configures alarm in RTC and arms it.
// The callback is executed in the context of an interrupt handler,
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
func (rtc *rtcType) setAlarm(t rtcTime, callback func()) {
rtc.disableInterruptMatch()
// Clear all match enable bits
rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA | rp.RTC_IRQ_SETUP_0_MONTH_ENA | rp.RTC_IRQ_SETUP_0_DAY_ENA)
rtc.IRQ_SETUP_1.ClearBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA | rp.RTC_IRQ_SETUP_1_HOUR_ENA | rp.RTC_IRQ_SETUP_1_MIN_ENA | rp.RTC_IRQ_SETUP_1_SEC_ENA)
// Only add to setup if it isn't -1 and set the match enable bits for things we care about
if t.Year >= 0 {
rtc.IRQ_SETUP_0.SetBits(uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos)
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA)
}
if t.Month >= 0 {
rtc.IRQ_SETUP_0.SetBits(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos)
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MONTH_ENA)
}
if t.Day >= 0 {
rtc.IRQ_SETUP_0.SetBits(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos)
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_DAY_ENA)
}
if t.Dotw >= 0 {
rtc.IRQ_SETUP_1.SetBits(uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos)
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA)
}
if t.Hour >= 0 {
rtc.IRQ_SETUP_1.SetBits(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos)
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_HOUR_ENA)
}
if t.Min >= 0 {
rtc.IRQ_SETUP_1.SetBits(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos)
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_MIN_ENA)
}
if t.Sec >= 0 {
rtc.IRQ_SETUP_1.SetBits(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos)
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_SEC_ENA)
}
// Enable the IRQ at the proc
interrupt.New(rp.IRQ_RTC_IRQ, rtcHandleInterrupt).Enable()
// Enable the IRQ at the peri
rtc.INTE.Set(rp.RTC_INTE_RTC)
rtc.enableInterruptMatch()
}
func (rtc *rtcType) enableInterruptMatch() {
// Set matching and wait for it to be enabled
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA)
for !rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) {
}
}
func (rtc *rtcType) disableInterruptMatch() {
// Disable matching and wait for it to stop being active
rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA)
for rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) {
}
}
func rtcHandleInterrupt(itr interrupt.Interrupt) {
// Always disable the alarm to clear the current IRQ.
// Even if it is a repeatable alarm, we don't want it to keep firing.
// If it matches on a second it can keep firing for that second.
RTC.disableInterruptMatch()
// Call user callback function
if rtcCallback != nil {
rtcCallback()
}
if rtcAlarmRepeats {
// If it is a repeatable alarm, reset time and re-enable the alarm.
RTC.setTime(rtcEpoch)
RTC.enableInterruptMatch()
}
}
+1 -25
View File
@@ -40,9 +40,6 @@ var (
ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022") ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022")
ErrSPITimeout = errors.New("SPI timeout") ErrSPITimeout = errors.New("SPI timeout")
ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz") ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz")
errSPIInvalidSDI = errors.New("invalid SPI SDI pin")
errSPIInvalidSDO = errors.New("invalid SPI SDO pin")
errSPIInvalidSCK = errors.New("invalid SPI SCK pin")
) )
type SPI struct { type SPI struct {
@@ -165,7 +162,7 @@ func (spi SPI) GetBaudRate() uint32 {
// No pin configuration is needed of SCK, SDO and SDI needed after calling Configure. // No pin configuration is needed of SCK, SDO and SDI needed after calling Configure.
func (spi SPI) Configure(config SPIConfig) error { func (spi SPI) Configure(config SPIConfig) error {
const defaultBaud uint32 = 115200 const defaultBaud uint32 = 115200
if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 { if config.SCK == 0 {
// set default pins if config zero valued or invalid clock pin supplied. // set default pins if config zero valued or invalid clock pin supplied.
switch spi.Bus { switch spi.Bus {
case rp.SPI0: case rp.SPI0:
@@ -178,27 +175,6 @@ func (spi SPI) Configure(config SPIConfig) error {
config.SDI = SPI1_SDI_PIN config.SDI = SPI1_SDI_PIN
} }
} }
var okSDI, okSDO, okSCK bool
switch spi.Bus {
case rp.SPI0:
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18
case rp.SPI1:
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 28
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
}
switch {
case !okSDI:
return errSPIInvalidSDI
case !okSDO:
return errSPIInvalidSDO
case !okSCK:
return errSPIInvalidSCK
}
if config.DataBits < 4 || config.DataBits > 16 { if config.DataBits < 4 || config.DataBits > 16 {
config.DataBits = 8 config.DataBits = 8
} }
+2 -2
View File
@@ -127,10 +127,10 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
// Bus is reset // Bus is reset
if (status & rp.USBCTRL_REGS_INTS_BUS_RESET) > 0 { if (status & rp.USBCTRL_REGS_INTS_BUS_RESET) > 0 {
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_BUS_RESET) rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_BUS_RESET)
fixRP2040UsbDeviceEnumeration()
rp.USBCTRL_REGS.ADDR_ENDP.Set(0) rp.USBCTRL_REGS.ADDR_ENDP.Set(0)
initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL) initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL)
fixRP2040UsbDeviceEnumeration()
} }
} }
-122
View File
@@ -1,122 +0,0 @@
//go:build stm32f4 || stm32l4 || stm32wlx
package machine
import (
"device/stm32"
"bytes"
"unsafe"
)
// compile-time check for ensuring we fulfill BlockDevice interface
var _ BlockDevice = flashBlockDevice{}
var Flash flashBlockDevice
type flashBlockDevice struct {
}
// ReadAt reads the given number of bytes from the block device.
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotReadPastEOF
}
data := unsafe.Slice((*byte)(unsafe.Pointer(FlashDataStart()+uintptr(off))), len(p))
copy(p, data)
return len(p), nil
}
// WriteAt writes the given number of bytes to the block device.
// Only double-word (64 bits) length data can be programmed. See rm0461 page 78.
// If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF
}
unlockFlash()
defer lockFlash()
return writeFlashData(FlashDataStart()+uintptr(off), f.pad(p))
}
// Size returns the number of bytes in this block device.
func (f flashBlockDevice) Size() int64 {
return int64(FlashDataEnd() - FlashDataStart())
}
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
func (f flashBlockDevice) WriteBlockSize() int64 {
return writeBlockSize
}
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
// TODO: correctly handle processors that have differently sized blocks
// in different areas of memory like the STM32F40x and STM32F1x.
func (f flashBlockDevice) EraseBlockSize() int64 {
return eraseBlockSize()
}
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
unlockFlash()
defer lockFlash()
for i := start; i < start+len; i++ {
if err := eraseBlock(uint32(i)); err != nil {
return err
}
}
return nil
}
// pad data if needed so it is long enough for correct byte alignment on writes.
func (f flashBlockDevice) pad(p []byte) []byte {
paddingNeeded := f.WriteBlockSize() - (int64(len(p)) % f.WriteBlockSize())
if paddingNeeded == 0 {
return p
}
padded := bytes.Repeat([]byte{0xff}, int(paddingNeeded))
return append(p, padded...)
}
const memoryStart = 0x08000000
func unlockFlash() {
// keys as described rm0461 page 76
var fkey1 uint32 = 0x45670123
var fkey2 uint32 = 0xCDEF89AB
// Wait for the flash memory not to be busy
for stm32.FLASH.GetSR_BSY() != 0 {
}
// Check if the controller is unlocked already
if stm32.FLASH.GetCR_LOCK() != 0 {
// Write the first key
stm32.FLASH.SetKEYR(fkey1)
// Write the second key
stm32.FLASH.SetKEYR(fkey2)
}
}
func lockFlash() {
stm32.FLASH.SetCR_LOCK(1)
}
-141
View File
@@ -6,8 +6,6 @@ package machine
import ( import (
"device/stm32" "device/stm32"
"encoding/binary"
"errors"
"math/bits" "math/bits"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile" "runtime/volatile"
@@ -793,142 +791,3 @@ func (i2c *I2C) getSpeed(config I2CConfig) uint32 {
} }
} }
} }
//---------- Flash related code
// the block size actually depends on the sector.
// TODO: handle this correctly for sectors > 3
const eraseBlockSizeValue = 16384
// see RM0090 page 75
func sectorNumber(address uintptr) uint32 {
switch {
// 0x0800 0000 - 0x0800 3FFF
case address >= 0x08000000 && address <= 0x08003FFF:
return 0
// 0x0800 4000 - 0x0800 7FFF
case address >= 0x08004000 && address <= 0x08007FFF:
return 1
// 0x0800 8000 - 0x0800 BFFF
case address >= 0x08008000 && address <= 0x0800BFFF:
return 2
// 0x0800 C000 - 0x0800 FFFF
case address >= 0x0800C000 && address <= 0x0800FFFF:
return 3
// 0x0801 0000 - 0x0801 FFFF
case address >= 0x08010000 && address <= 0x0801FFFF:
return 4
// 0x0802 0000 - 0x0803 FFFF
case address >= 0x08020000 && address <= 0x0803FFFF:
return 5
// 0x0804 0000 - 0x0805 FFFF
case address >= 0x08040000 && address <= 0x0805FFFF:
return 6
case address >= 0x08060000 && address <= 0x0807FFFF:
return 7
case address >= 0x08080000 && address <= 0x0809FFFF:
return 8
case address >= 0x080A0000 && address <= 0x080BFFFF:
return 9
case address >= 0x080C0000 && address <= 0x080DFFFF:
return 10
case address >= 0x080E0000 && address <= 0x080FFFFF:
return 11
default:
return 0
}
}
// calculate sector number from address
// var sector uint32 = sectorNumber(address)
// see RM0090 page 85
// eraseBlock at the passed in block number
func eraseBlock(block uint32) error {
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0xF0)
// set SER bit
stm32.FLASH.SetCR_SER(1)
defer stm32.FLASH.SetCR_SER(0)
// set the block (aka sector) to be erased
stm32.FLASH.SetCR_SNB(block)
defer stm32.FLASH.SetCR_SNB(0)
// start the page erase
stm32.FLASH.SetCR_STRT(1)
waitUntilFlashDone()
if err := checkError(); err != nil {
return err
}
return nil
}
const writeBlockSize = 2
// see RM0090 page 86
// must write data in word-length
func writeFlashData(address uintptr, data []byte) (int, error) {
if len(data)%writeBlockSize != 0 {
return 0, errFlashInvalidWriteLength
}
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0xF0)
// set parallelism to x32
stm32.FLASH.SetCR_PSIZE(2)
for i := 0; i < len(data); i += writeBlockSize {
// start write operation
stm32.FLASH.SetCR_PG(1)
*(*uint16)(unsafe.Pointer(address)) = binary.BigEndian.Uint16(data[i : i+writeBlockSize])
waitUntilFlashDone()
if err := checkError(); err != nil {
return i, err
}
// end write operation
stm32.FLASH.SetCR_PG(0)
}
return len(data), nil
}
func waitUntilFlashDone() {
for stm32.FLASH.GetSR_BSY() != 0 {
}
}
var (
errFlashPGS = errors.New("errFlashPGS")
errFlashPGP = errors.New("errFlashPGP")
errFlashPGA = errors.New("errFlashPGA")
errFlashWRP = errors.New("errFlashWRP")
)
func checkError() error {
switch {
case stm32.FLASH.GetSR_PGSERR() != 0:
return errFlashPGS
case stm32.FLASH.GetSR_PGPERR() != 0:
return errFlashPGP
case stm32.FLASH.GetSR_PGAERR() != 0:
return errFlashPGA
case stm32.FLASH.GetSR_WRPERR() != 0:
return errFlashWRP
}
return nil
}
-103
View File
@@ -4,8 +4,6 @@ package machine
import ( import (
"device/stm32" "device/stm32"
"encoding/binary"
"errors"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile" "runtime/volatile"
"unsafe" "unsafe"
@@ -545,104 +543,3 @@ func initRNG() {
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN) stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN)
stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN) stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN)
} }
//---------- Flash related code
const eraseBlockSizeValue = 2048
// see RM0394 page 83
// eraseBlock of the passed in block number
func eraseBlock(block uint32) error {
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0x3FA)
// page erase operation
stm32.FLASH.SetCR_PER(1)
defer stm32.FLASH.SetCR_PER(0)
// set the page to be erased
stm32.FLASH.SetCR_PNB(block)
// start the page erase
stm32.FLASH.SetCR_START(1)
waitUntilFlashDone()
if err := checkError(); err != nil {
return err
}
return nil
}
const writeBlockSize = 8
// see RM0394 page 84
// It is only possible to program double word (2 x 32-bit data).
func writeFlashData(address uintptr, data []byte) (int, error) {
if len(data)%writeBlockSize != 0 {
return 0, errFlashInvalidWriteLength
}
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0x3FA)
for j := 0; j < len(data); j += writeBlockSize {
// start page write operation
stm32.FLASH.SetCR_PG(1)
// write first word using double-word low order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
address += writeBlockSize / 2
// write second word using double-word high order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j : j+writeBlockSize/2])
waitUntilFlashDone()
if err := checkError(); err != nil {
return j, err
}
// end flash write
stm32.FLASH.SetCR_PG(0)
address += writeBlockSize / 2
}
return len(data), nil
}
func waitUntilFlashDone() {
for stm32.FLASH.GetSR_BSY() != 0 {
}
}
var (
errFlashPGS = errors.New("errFlashPGS")
errFlashSIZE = errors.New("errFlashSIZE")
errFlashPGA = errors.New("errFlashPGA")
errFlashWRP = errors.New("errFlashWRP")
errFlashPROG = errors.New("errFlashPROG")
)
func checkError() error {
switch {
case stm32.FLASH.GetSR_PGSERR() != 0:
return errFlashPGS
case stm32.FLASH.GetSR_SIZERR() != 0:
return errFlashSIZE
case stm32.FLASH.GetSR_PGAERR() != 0:
return errFlashPGA
case stm32.FLASH.GetSR_WRPERR() != 0:
return errFlashWRP
case stm32.FLASH.GetSR_PROGERR() != 0:
return errFlashPROG
}
return nil
}
-114
View File
@@ -6,8 +6,6 @@ package machine
import ( import (
"device/stm32" "device/stm32"
"encoding/binary"
"errors"
"math/bits" "math/bits"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile" "runtime/volatile"
@@ -426,115 +424,3 @@ const (
ARR_MAX = 0x10000 ARR_MAX = 0x10000
PSC_MAX = 0x10000 PSC_MAX = 0x10000
) )
//---------- Flash related code
const eraseBlockSizeValue = 2048
// eraseBlock of the passed in block number
func eraseBlock(block uint32) error {
waitUntilFlashDone()
// check if operation is allowed.
if stm32.FLASH.GetSR_PESD() != 0 {
return errFlashCannotErasePage
}
// clear any previous errors
stm32.FLASH.SR.SetBits(0x3FA)
// page erase operation
stm32.FLASH.SetCR_PER(1)
defer stm32.FLASH.SetCR_PER(0)
// set the address to the page to be written
stm32.FLASH.SetCR_PNB(block)
defer stm32.FLASH.SetCR_PNB(0)
// start the page erase
stm32.FLASH.SetCR_STRT(1)
waitUntilFlashDone()
if err := checkError(); err != nil {
return err
}
return nil
}
const writeBlockSize = 8
func writeFlashData(address uintptr, data []byte) (int, error) {
if len(data)%writeBlockSize != 0 {
return 0, errFlashInvalidWriteLength
}
waitUntilFlashDone()
// check if operation is allowed
if stm32.FLASH.GetSR_PESD() != 0 {
return 0, errFlashNotAllowedWriteData
}
// clear any previous errors
stm32.FLASH.SR.SetBits(0x3FA)
for j := 0; j < len(data); j += writeBlockSize {
// start page write operation
stm32.FLASH.SetCR_PG(1)
// write first word using double-word low order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
address += writeBlockSize / 2
// write second word using double-word high order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j : j+writeBlockSize/2])
waitUntilFlashDone()
if err := checkError(); err != nil {
return j, err
}
// end flash write
stm32.FLASH.SetCR_PG(0)
address += writeBlockSize / 2
}
return len(data), nil
}
func waitUntilFlashDone() {
for stm32.FLASH.GetSR_BSY() != 0 {
}
for stm32.FLASH.GetSR_CFGBSY() != 0 {
}
}
var (
errFlashPGS = errors.New("errFlashPGS")
errFlashSIZE = errors.New("errFlashSIZE")
errFlashPGA = errors.New("errFlashPGA")
errFlashWRP = errors.New("errFlashWRP")
errFlashPROG = errors.New("errFlashPROG")
)
func checkError() error {
switch {
case stm32.FLASH.GetSR_PGSERR() != 0:
return errFlashPGS
case stm32.FLASH.GetSR_SIZERR() != 0:
return errFlashSIZE
case stm32.FLASH.GetSR_PGAERR() != 0:
return errFlashPGA
case stm32.FLASH.GetSR_WRPERR() != 0:
return errFlashWRP
case stm32.FLASH.GetSR_PROGERR() != 0:
return errFlashPROG
}
return nil
}
+1 -14
View File
@@ -67,7 +67,7 @@ var DescriptorCDCHID = Descriptor{
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
0x09, 0x04, 0x02, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x04, 0x02, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
0x09, 0x21, 0x01, 0x01, 0x00, 0x01, 0x22, 0x7E, 0x00, 0x09, 0x21, 0x01, 0x01, 0x00, 0x01, 0x22, 0x65, 0x00,
0x07, 0x05, 0x84, 0x03, 0x40, 0x00, 0x01, 0x07, 0x05, 0x84, 0x03, 0x40, 0x00, 0x01,
}, },
HID: map[uint16][]byte{ HID: map[uint16][]byte{
@@ -80,19 +80,6 @@ var DescriptorCDCHID = Descriptor{
0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81,
0x03, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95,
0x03, 0x81, 0x06, 0xc0, 0xc0, 0x03, 0x81, 0x06, 0xc0, 0xc0,
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report ID (3)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x1F, // Logical Maximum (8191)
0x19, 0x00, // Usage Minimum (Unassigned)
0x2A, 0xFF, 0x1F, // Usage Maximum (0x1FFF)
0x75, 0x10, // Report Size (16)
0x95, 0x01, // Report Count (1)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
}, },
}, },
} }
+10 -20
View File
@@ -238,26 +238,16 @@ func (kb *keyboard) sendKey(consumer bool, b []byte) bool {
func (kb *keyboard) keyboardSendKeys(consumer bool) bool { func (kb *keyboard) keyboardSendKeys(consumer bool) bool {
var b [9]byte var b [9]byte
b[0] = 0x02
if !consumer { b[1] = kb.mod
b[0] = 0x02 // REPORT_ID b[2] = 0x02
b[1] = kb.mod b[3] = kb.key[0]
b[2] = 0x02 b[4] = kb.key[1]
b[3] = kb.key[0] b[5] = kb.key[2]
b[4] = kb.key[1] b[6] = kb.key[3]
b[5] = kb.key[2] b[7] = kb.key[4]
b[6] = kb.key[3] b[8] = kb.key[5]
b[7] = kb.key[4] return kb.sendKey(consumer, b[:])
b[8] = kb.key[5]
return kb.sendKey(consumer, b[:])
} else {
b[0] = 0x03 // REPORT_ID
b[1] = uint8(kb.con[0])
b[2] = uint8((kb.con[0] & 0x0300) >> 8)
return kb.sendKey(consumer, b[:3])
}
} }
// Down transmits a key-down event for the given Keycode. // Down transmits a key-down event for the given Keycode.
-1
View File
@@ -77,7 +77,6 @@ const (
KeyModifierRightAlt Keycode = 0x40 | 0xE000 KeyModifierRightAlt Keycode = 0x40 | 0xE000
KeyModifierRightGUI Keycode = 0x80 | 0xE000 KeyModifierRightGUI Keycode = 0x80 | 0xE000
// KeySystemXXX is not supported now
KeySystemPowerDown Keycode = 0x81 | 0xE200 KeySystemPowerDown Keycode = 0x81 | 0xE200
KeySystemSleep Keycode = 0x82 | 0xE200 KeySystemSleep Keycode = 0x82 | 0xE200
KeySystemWakeUp Keycode = 0x83 | 0xE200 KeySystemWakeUp Keycode = 0x83 | 0xE200
+12 -12
View File
@@ -71,7 +71,7 @@ var deepEqualTests = []DeepEqualTest{
{&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true}, {&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true},
{Basic{1, 0.5}, Basic{1, 0.5}, true}, {Basic{1, 0.5}, Basic{1, 0.5}, true},
{error(nil), error(nil), true}, {error(nil), error(nil), true},
{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true}, //{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true},
{fn1, fn2, true}, {fn1, fn2, true},
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true}, {[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true}, {[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
@@ -87,10 +87,10 @@ var deepEqualTests = []DeepEqualTest{
{&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false}, {&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false},
{Basic{1, 0.5}, Basic{1, 0.6}, false}, {Basic{1, 0.5}, Basic{1, 0.6}, false},
{Basic{1, 0}, Basic{2, 0}, false}, {Basic{1, 0}, Basic{2, 0}, false},
{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false}, //{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false},
{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false}, //{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false},
{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false}, //{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false},
{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false}, //{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false},
{nil, 1, false}, {nil, 1, false},
{1, nil, false}, {1, nil, false},
{fn1, fn3, false}, {fn1, fn3, false},
@@ -104,16 +104,16 @@ var deepEqualTests = []DeepEqualTest{
{&[1]float64{math.NaN()}, self{}, true}, {&[1]float64{math.NaN()}, self{}, true},
{[]float64{math.NaN()}, []float64{math.NaN()}, false}, {[]float64{math.NaN()}, []float64{math.NaN()}, false},
{[]float64{math.NaN()}, self{}, true}, {[]float64{math.NaN()}, self{}, true},
{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false}, //{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
{map[float64]float64{math.NaN(): 1}, self{}, true}, //{map[float64]float64{math.NaN(): 1}, self{}, true},
// Nil vs empty: not the same. // Nil vs empty: not the same.
{[]int{}, []int(nil), false}, {[]int{}, []int(nil), false},
{[]int{}, []int{}, true}, {[]int{}, []int{}, true},
{[]int(nil), []int(nil), true}, {[]int(nil), []int(nil), true},
{map[int]int{}, map[int]int(nil), false}, //{map[int]int{}, map[int]int(nil), false},
{map[int]int{}, map[int]int{}, true}, //{map[int]int{}, map[int]int{}, true},
{map[int]int(nil), map[int]int(nil), true}, //{map[int]int(nil), map[int]int(nil), true},
// Mismatched types // Mismatched types
{1, 1.0, false}, {1, 1.0, false},
@@ -130,8 +130,8 @@ var deepEqualTests = []DeepEqualTest{
// Possible loops. // Possible loops.
{&loopy1, &loopy1, true}, {&loopy1, &loopy1, true},
{&loopy1, &loopy2, true}, {&loopy1, &loopy2, true},
{&cycleMap1, &cycleMap2, true}, //{&cycleMap1, &cycleMap2, true},
{&cycleMap1, &cycleMap3, false}, //{&cycleMap1, &cycleMap3, false},
} }
func TestDeepEqual(t *testing.T) { func TestDeepEqual(t *testing.T) {
+1 -1
View File
@@ -15,7 +15,7 @@ import "unsafe"
type visit struct { type visit struct {
a1 unsafe.Pointer a1 unsafe.Pointer
a2 unsafe.Pointer a2 unsafe.Pointer
typ *rawType typ rawType
} }
// Tests for deep equality using reflected types. The map argument tracks // Tests for deep equality using reflected types. The map argument tracks
+61
View File
@@ -0,0 +1,61 @@
package reflect
import (
"unsafe"
)
// This stores a varint for each named type. Named types are identified by their
// name instead of by their type. The named types stored in this struct are
// non-basic types: pointer, struct, and channel.
//
//go:extern reflect.namedNonBasicTypesSidetable
var namedNonBasicTypesSidetable uintptr
//go:extern reflect.structTypesSidetable
var structTypesSidetable byte
//go:extern reflect.structNamesSidetable
var structNamesSidetable byte
//go:extern reflect.arrayTypesSidetable
var arrayTypesSidetable byte
// readStringSidetable reads a string from the given table (like
// structNamesSidetable) and returns this string. No heap allocation is
// necessary because it makes the string point directly to the raw bytes of the
// table.
func readStringSidetable(table unsafe.Pointer, index uintptr) string {
nameLen, namePtr := readVarint(unsafe.Pointer(uintptr(table) + index))
return *(*string)(unsafe.Pointer(&stringHeader{
data: namePtr,
len: nameLen,
}))
}
// readVarint decodes a varint as used in the encoding/binary package.
// It has an input pointer and returns the read varint and the pointer
// incremented to the next field in the data structure, just after the varint.
//
// Details:
// https://github.com/golang/go/blob/e37a1b1c/src/encoding/binary/varint.go#L7-L25
func readVarint(buf unsafe.Pointer) (uintptr, unsafe.Pointer) {
var n uintptr
shift := uintptr(0)
for {
// Read the next byte in the buffer.
c := *(*byte)(buf)
// Decode the bits from this byte and add them to the output number.
n |= uintptr(c&0x7f) << shift
shift += 7
// Increment the buf pointer (pointer arithmetic!).
buf = unsafe.Pointer(uintptr(buf) + 1)
// Check whether this is the last byte of this varint. The upper bit
// (msb) indicates whether any bytes follow.
if c>>7 == 0 {
return n, buf
}
}
}
+158 -257
View File
@@ -2,74 +2,36 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Type information of an interface is stored as a pointer to a global in the
// interface type (runtime._interface). This is called a type struct.
// It always starts with a byte that contains both the type kind and a few
// flags. In most cases it also contains a pointer to another type struct
// (ptrTo), that is the pointer type of the current type (for example, type int
// also has a pointer to the type *int). The exception is pointer types, to
// avoid infinite recursion.
//
// The layouts specifically look like this:
// - basic types (Bool..UnsafePointer):
// meta uint8 // actually: kind + flags
// ptrTo *typeStruct
// - named types (see elemType):
// meta uint8
// ptrTo *typeStruct
// underlying *typeStruct // the underlying, non-named type
// - channels and slices (see elemType):
// meta uint8
// ptrTo *typeStruct
// elementType *typeStruct // the type that you get with .Elem()
// - pointer types (see ptrType, this doesn't include chan, map, etc):
// meta uint8
// elementType *typeStruct
// - array types (see arrayType)
// meta uint8
// ptrTo *typeStruct
// elem *typeStruct // element type of the array
// arrayLen uintptr // length of the array (this is part of the type)
// - map types (this is still missing the key and element types)
// meta uint8
// ptrTo *typeStruct
// elem *typeStruct
// key *typeStruct
// - struct types (see structType):
// meta uint8
// numField uint16
// ptrTo *typeStruct
// fields [...]structField // the remaining fields are all of type structField
// - interface types (this is missing the interface methods):
// meta uint8
// ptrTo *typeStruct
// - signature types (this is missing input and output parameters):
// meta uint8
// ptrTo *typeStruct
//
// The type struct is essentially a union of all the above types. Which it is,
// can be determined by looking at the meta byte.
package reflect package reflect
import ( import (
"unsafe" "unsafe"
) )
// Flags stored in the first byte of the struct field byte array. Must be kept // The compiler uses a compact encoding to store type information. Unlike the
// up to date with compiler/interface.go. // main Go compiler, most of the types are stored directly in the type code.
const ( //
structFieldFlagAnonymous = 1 << iota // Type code bit allocation:
structFieldFlagHasTag // xxxxx0: basic types, where xxxxx is the basic type number (never 0).
structFieldFlagIsExported // The higher bits indicate the named type, if any.
) // nxxx1: complex types, where n indicates whether this is a named type (named
// if set) and xxx contains the type kind number:
// 0 (0001): Chan
// 1 (0011): Interface
// 2 (0101): Pointer
// 3 (0111): Slice
// 4 (1001): Array
// 5 (1011): Func
// 6 (1101): Map
// 7 (1111): Struct
// The higher bits are either the contents of the type depending on the
// type (if n is clear) or indicate the number of the named type (if n
// is set).
type Kind uint8 type Kind uintptr
// Copied from reflect/type.go // Copied from reflect/type.go
// https://golang.org/src/reflect/type.go?s=8302:8316#L217 // https://golang.org/src/reflect/type.go?s=8302:8316#L217
// These constants must match basicTypes and the typeKind* constants in
// compiler/interface.go
const ( const (
Invalid Kind = iota Invalid Kind = iota
Bool Bool
@@ -162,6 +124,11 @@ func (k Kind) String() string {
} }
} }
// basicType returns a new Type for this kind if Kind is a basic type.
func (k Kind) basicType() rawType {
return rawType(k << 1)
}
// Copied from reflect/type.go // Copied from reflect/type.go
// https://go.dev/src/reflect/type.go?#L348 // https://go.dev/src/reflect/type.go?#L348
@@ -379,71 +346,8 @@ type Type interface {
Out(i int) Type Out(i int) Type
} }
// Constants for the 'meta' byte. // The typecode as used in an interface{}.
const ( type rawType uintptr
kindMask = 31 // mask to apply to the meta byte to get the Kind value
flagNamed = 32 // flag that is set if this is a named type
)
// The base type struct. All type structs start with this.
type rawType struct {
meta uint8 // metadata byte, contains kind and flags (see contants above)
}
// All types that have an element type: named, chan, slice, array, map (but not
// pointer because it doesn't have ptrTo).
type elemType struct {
rawType
ptrTo *rawType
elem *rawType
}
type ptrType struct {
rawType
elem *rawType
}
type arrayType struct {
rawType
ptrTo *rawType
elem *rawType
arrayLen uintptr
}
type mapType struct {
rawType
ptrTo *rawType
elem *rawType
key *rawType
}
// Type for struct types. The numField value is intentionally put before ptrTo
// for better struct packing on 32-bit and 64-bit architectures. On these
// architectures, the ptrTo field still has the same offset as in all the other
// type structs.
// The fields array isn't necessarily 1 structField long, instead it is as long
// as numFields. The array is given a length of 1 to satisfy the Go type
// checker.
type structType struct {
rawType
numField uint16
ptrTo *rawType
fields [1]structField // the remaining fields are all of type structField
}
type structField struct {
fieldType *rawType
data unsafe.Pointer // various bits of information, packed in a byte array
}
// Equivalent to (go/types.Type).Underlying(): if this is a named type return
// the underlying type, else just return the type itself.
func (t *rawType) underlying() *rawType {
if t.meta&flagNamed != 0 {
return (*elemType)(unsafe.Pointer(t)).elem
}
return t
}
func TypeOf(i interface{}) Type { func TypeOf(i interface{}) Type {
return ValueOf(i).typecode return ValueOf(i).typecode
@@ -452,53 +356,70 @@ func TypeOf(i interface{}) Type {
func PtrTo(t Type) Type { return PointerTo(t) } func PtrTo(t Type) Type { return PointerTo(t) }
func PointerTo(t Type) Type { func PointerTo(t Type) Type {
switch t.Kind() { if t.Kind() == Pointer {
case Pointer:
panic("reflect: cannot make **T type") panic("reflect: cannot make **T type")
case Struct:
return (*structType)(unsafe.Pointer(t.(*rawType))).ptrTo
default:
return (*elemType)(unsafe.Pointer(t.(*rawType))).ptrTo
} }
ptrType := t.(rawType)<<5 | 5 // 0b0101 == 5
if ptrType>>5 != t {
panic("reflect: PointerTo type does not fit")
}
return ptrType
} }
func (t *rawType) String() string { func (t rawType) String() string {
return "T" return "T"
} }
func (t *rawType) Kind() Kind { func (t rawType) Kind() Kind {
return Kind(t.meta & kindMask) if t%2 == 0 {
// basic type
return Kind((t >> 1) % 32)
} else {
return Kind(t>>1)%8 + 19
}
} }
// Elem returns the element type for channel, slice and array types, the // Elem returns the element type for channel, slice and array types, the
// pointed-to value for pointer types, and the key type for map types. // pointed-to value for pointer types, and the key type for map types.
func (t *rawType) Elem() Type { func (t rawType) Elem() Type {
return t.elem() return t.elem()
} }
func (t *rawType) elem() *rawType { func (t rawType) elem() rawType {
underlying := t.underlying() switch t.Kind() {
switch underlying.Kind() { case Chan, Pointer, Slice:
case Pointer: return t.stripPrefix()
return (*ptrType)(unsafe.Pointer(underlying)).elem case Array:
case Chan, Slice, Array, Map: index := t.stripPrefix()
return (*elemType)(unsafe.Pointer(underlying)).elem elem, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(index)))
default: return rawType(elem)
panic(&TypeError{"Elem"}) default: // not implemented: Map
panic("unimplemented: (reflect.Type).Elem()")
} }
} }
func (t *rawType) key() *rawType { // stripPrefix removes the "prefix" (the low 5 bits of the type code) from
underlying := t.underlying() // the type code. If this is a named type, it will resolve the underlying type
if underlying.Kind() != Map { // (which is the data for this named type). If it is not, the lower bits are
panic(&TypeError{"Key"}) // simply shifted off.
//
// The behavior is only defined for non-basic types.
func (t rawType) stripPrefix() rawType {
// Look at the 'n' bit in the type code (see the top of this file) to see
// whether this is a named type.
if (t>>4)%2 != 0 {
// This is a named type. The data is stored in a sidetable.
namedTypeNum := t >> 5
n := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicTypesSidetable)) + uintptr(namedTypeNum)*unsafe.Sizeof(uintptr(0))))
return rawType(n)
} }
return (*mapType)(unsafe.Pointer(underlying)).key // Not a named type, so the value is stored directly in the type code.
return t >> 5
} }
// Field returns the type of the i'th field of this struct type. It panics if t // Field returns the type of the i'th field of this struct type. It panics if t
// is not a struct type. // is not a struct type.
func (t *rawType) Field(i int) StructField { func (t rawType) Field(i int) StructField {
field := t.rawField(i) field := t.rawField(i)
return StructField{ return StructField{
Name: field.Name, Name: field.Name,
@@ -514,87 +435,82 @@ func (t *rawType) Field(i int) StructField {
// Type member to an interface. // Type member to an interface.
// //
// For internal use only. // For internal use only.
func (t *rawType) rawField(n int) rawStructField { func (t rawType) rawField(i int) rawStructField {
if t.Kind() != Struct { if t.Kind() != Struct {
panic(&TypeError{"Field"}) panic(&TypeError{"Field"})
} }
descriptor := (*structType)(unsafe.Pointer(t.underlying())) structIdentifier := t.stripPrefix()
if uint(n) >= uint(descriptor.numField) {
numField, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
if uint(i) >= uint(numField) {
panic("reflect: field index out of range") panic("reflect: field index out of range")
} }
// Iterate over all the fields to calculate the offset. // Iterate over every field in the struct and update the StructField each
// This offset could have been stored directly in the array (to make the // time, until the target field has been reached. This is very much not
// lookup faster), but by calculating it on-the-fly a bit of storage can be // efficient, but it is easy to implement.
// saved. // Adding a jump table at the start to jump to the field directly would
field := &descriptor.fields[0] // make this much faster, but that would also impact code size.
var offset uintptr = 0 field := rawStructField{}
for i := 0; i < n; i++ { offset := uintptr(0)
offset += field.fieldType.Size() for fieldNum := 0; fieldNum <= i; fieldNum++ {
// Read some flags of this field, like whether the field is an
// embedded field.
flagsByte := *(*uint8)(p)
p = unsafe.Pointer(uintptr(p) + 1)
// Increment pointer to the next field. // Read the type of this struct field.
field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{}))) var fieldTypeVal uintptr
fieldTypeVal, p = readVarint(p)
fieldType := rawType(fieldTypeVal)
field.Type = fieldType
// Align the offset for the next field. // Move Offset forward to align it to this field's alignment.
offset = align(offset, uintptr(field.fieldType.Align())) // Assume alignment is a power of two.
offset = align(offset, uintptr(fieldType.Align()))
field.Offset = offset
offset += fieldType.Size() // starting (unaligned) offset for next field
// Read the field name.
var nameNum uintptr
nameNum, p = readVarint(p)
field.Name = readStringSidetable(unsafe.Pointer(&structNamesSidetable), nameNum)
// The first bit in the flagsByte indicates whether this is an embedded
// field.
field.Anonymous = flagsByte&1 != 0
// The second bit indicates whether there is a tag.
if flagsByte&2 != 0 {
// There is a tag.
var tagNum uintptr
tagNum, p = readVarint(p)
field.Tag = StructTag(readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum))
} else {
// There is no tag.
field.Tag = ""
}
// The third bit indicates whether this field is exported.
if flagsByte&4 != 0 {
// This field is exported.
field.PkgPath = ""
} else {
// This field is unexported.
// TODO: list the real package path here. Storing it should not
// significantly impact binary size as there is only a limited
// number of packages in any program.
field.PkgPath = "<unimplemented>"
}
} }
data := field.data return field
// Read some flags of this field, like whether the field is an embedded
// field. See structFieldFlagAnonymous and similar flags.
flagsByte := *(*byte)(data)
data = unsafe.Add(data, 1)
// Read the field name.
nameStart := data
var nameLen uintptr
for *(*byte)(data) != 0 {
nameLen++
data = unsafe.Add(data, 1) // C: data++
}
name := *(*string)(unsafe.Pointer(&stringHeader{
data: nameStart,
len: nameLen,
}))
// Read the field tag, if there is one.
var tag string
if flagsByte&structFieldFlagHasTag != 0 {
data = unsafe.Add(data, 1) // C: data+1
tagLen := uintptr(*(*byte)(data))
data = unsafe.Add(data, 1) // C: data+1
tag = *(*string)(unsafe.Pointer(&stringHeader{
data: data,
len: tagLen,
}))
}
// Set the PkgPath to some (arbitrary) value if the package path is not
// exported.
pkgPath := ""
if flagsByte&structFieldFlagIsExported == 0 {
// This field is unexported.
// TODO: list the real package path here. Storing it should not
// significantly impact binary size as there is only a limited
// number of packages in any program.
pkgPath = "<unimplemented>"
}
return rawStructField{
Name: name,
PkgPath: pkgPath,
Type: field.fieldType,
Tag: StructTag(tag),
Anonymous: flagsByte&structFieldFlagAnonymous != 0,
Offset: offset,
}
} }
// Bits returns the number of bits that this type uses. It is only valid for // Bits returns the number of bits that this type uses. It is only valid for
// arithmetic types (integers, floats, and complex numbers). For other types, it // arithmetic types (integers, floats, and complex numbers). For other types, it
// will panic. // will panic.
func (t *rawType) Bits() int { func (t rawType) Bits() int {
kind := t.Kind() kind := t.Kind()
if kind >= Int && kind <= Complex128 { if kind >= Int && kind <= Complex128 {
return int(t.Size()) * 8 return int(t.Size()) * 8
@@ -604,26 +520,34 @@ func (t *rawType) Bits() int {
// Len returns the number of elements in this array. It panics of the type kind // Len returns the number of elements in this array. It panics of the type kind
// is not Array. // is not Array.
func (t *rawType) Len() int { func (t rawType) Len() int {
if t.Kind() != Array { if t.Kind() != Array {
panic(TypeError{"Len"}) panic(TypeError{"Len"})
} }
return int((*arrayType)(unsafe.Pointer(t.underlying())).arrayLen) // skip past the element type
arrayIdentifier := t.stripPrefix()
_, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(arrayIdentifier)))
// Read the array length.
arrayLen, _ := readVarint(p)
return int(arrayLen)
} }
// NumField returns the number of fields of a struct type. It panics for other // NumField returns the number of fields of a struct type. It panics for other
// type kinds. // type kinds.
func (t *rawType) NumField() int { func (t rawType) NumField() int {
if t.Kind() != Struct { if t.Kind() != Struct {
panic(&TypeError{"NumField"}) panic(&TypeError{"NumField"})
} }
return int((*structType)(unsafe.Pointer(t.underlying())).numField) structIdentifier := t.stripPrefix()
n, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
return int(n)
} }
// Size returns the size in bytes of a given type. It is similar to // Size returns the size in bytes of a given type. It is similar to
// unsafe.Sizeof. // unsafe.Sizeof.
func (t *rawType) Size() uintptr { func (t rawType) Size() uintptr {
switch t.Kind() { switch t.Kind() {
case Bool, Int8, Uint8: case Bool, Int8, Uint8:
return 1 return 1
@@ -672,7 +596,7 @@ func (t *rawType) Size() uintptr {
// Align returns the alignment of this type. It is similar to calling // Align returns the alignment of this type. It is similar to calling
// unsafe.Alignof. // unsafe.Alignof.
func (t *rawType) Align() int { func (t rawType) Align() int {
switch t.Kind() { switch t.Kind() {
case Bool, Int8, Uint8: case Bool, Int8, Uint8:
return int(unsafe.Alignof(int8(0))) return int(unsafe.Alignof(int8(0)))
@@ -724,14 +648,14 @@ func (t *rawType) Align() int {
// FieldAlign returns the alignment if this type is used in a struct field. It // FieldAlign returns the alignment if this type is used in a struct field. It
// is currently an alias for Align() but this might change in the future. // is currently an alias for Align() but this might change in the future.
func (t *rawType) FieldAlign() int { func (t rawType) FieldAlign() int {
return t.Align() return t.Align()
} }
// AssignableTo returns whether a value of type t can be assigned to a variable // AssignableTo returns whether a value of type t can be assigned to a variable
// of type u. // of type u.
func (t *rawType) AssignableTo(u Type) bool { func (t rawType) AssignableTo(u Type) bool {
if t == u.(*rawType) { if t == u.(rawType) {
return true return true
} }
if u.Kind() == Interface { if u.Kind() == Interface {
@@ -740,7 +664,7 @@ func (t *rawType) AssignableTo(u Type) bool {
return false return false
} }
func (t *rawType) Implements(u Type) bool { func (t rawType) Implements(u Type) bool {
if u.Kind() != Interface { if u.Kind() != Interface {
panic("reflect: non-interface type passed to Type.Implements") panic("reflect: non-interface type passed to Type.Implements")
} }
@@ -748,7 +672,7 @@ func (t *rawType) Implements(u Type) bool {
} }
// Comparable returns whether values of this type can be compared to each other. // Comparable returns whether values of this type can be compared to each other.
func (t *rawType) Comparable() bool { func (t rawType) Comparable() bool {
switch t.Kind() { switch t.Kind() {
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return true return true
@@ -785,59 +709,36 @@ func (t *rawType) Comparable() bool {
} }
} }
// isbinary() returns if the hashmapAlgorithmBinary functions can be used on this type
func (t *rawType) isBinary() bool {
switch t.Kind() {
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return true
case Float32, Float64, Complex64, Complex128:
return true
case Pointer:
return true
case Array:
return t.elem().isBinary()
case Struct:
numField := t.NumField()
for i := 0; i < numField; i++ {
if !t.rawField(i).Type.isBinary() {
return false
}
}
return true
}
return false
}
func (t rawType) ChanDir() ChanDir { func (t rawType) ChanDir() ChanDir {
panic("unimplemented: (reflect.Type).ChanDir()") panic("unimplemented: (reflect.Type).ChanDir()")
} }
func (t *rawType) ConvertibleTo(u Type) bool { func (t rawType) ConvertibleTo(u Type) bool {
panic("unimplemented: (reflect.Type).ConvertibleTo()") panic("unimplemented: (reflect.Type).ConvertibleTo()")
} }
func (t *rawType) IsVariadic() bool { func (t rawType) IsVariadic() bool {
panic("unimplemented: (reflect.Type).IsVariadic()") panic("unimplemented: (reflect.Type).IsVariadic()")
} }
func (t *rawType) NumIn() int { func (t rawType) NumIn() int {
panic("unimplemented: (reflect.Type).NumIn()") panic("unimplemented: (reflect.Type).NumIn()")
} }
func (t *rawType) NumOut() int { func (t rawType) NumOut() int {
panic("unimplemented: (reflect.Type).NumOut()") panic("unimplemented: (reflect.Type).NumOut()")
} }
func (t *rawType) NumMethod() int { func (t rawType) NumMethod() int {
panic("unimplemented: (reflect.Type).NumMethod()") panic("unimplemented: (reflect.Type).NumMethod()")
} }
func (t *rawType) Name() string { func (t rawType) Name() string {
panic("unimplemented: (reflect.Type).Name()") panic("unimplemented: (reflect.Type).Name()")
} }
func (t *rawType) Key() Type { func (t rawType) Key() Type {
return t.key() panic("unimplemented: (reflect.Type).Key()")
} }
func (t rawType) In(i int) Type { func (t rawType) In(i int) Type {
@@ -891,7 +792,7 @@ func (f StructField) IsExported() bool {
type rawStructField struct { type rawStructField struct {
Name string Name string
PkgPath string PkgPath string
Type *rawType Type rawType
Tag StructTag Tag StructTag
Anonymous bool Anonymous bool
Offset uintptr Offset uintptr
+17 -214
View File
@@ -17,7 +17,7 @@ const (
) )
type Value struct { type Value struct {
typecode *rawType typecode rawType
value unsafe.Pointer value unsafe.Pointer
flags valueFlags flags valueFlags
} }
@@ -44,15 +44,15 @@ func Indirect(v Value) Value {
} }
//go:linkname composeInterface runtime.composeInterface //go:linkname composeInterface runtime.composeInterface
func composeInterface(unsafe.Pointer, unsafe.Pointer) interface{} func composeInterface(rawType, unsafe.Pointer) interface{}
//go:linkname decomposeInterface runtime.decomposeInterface //go:linkname decomposeInterface runtime.decomposeInterface
func decomposeInterface(i interface{}) (unsafe.Pointer, unsafe.Pointer) func decomposeInterface(i interface{}) (rawType, unsafe.Pointer)
func ValueOf(i interface{}) Value { func ValueOf(i interface{}) Value {
typecode, value := decomposeInterface(i) typecode, value := decomposeInterface(i)
return Value{ return Value{
typecode: (*rawType)(typecode), typecode: typecode,
value: value, value: value,
flags: valueFlagExported, flags: valueFlagExported,
} }
@@ -85,7 +85,7 @@ func valueInterfaceUnsafe(v Value) interface{} {
} }
v.value = unsafe.Pointer(value) v.value = unsafe.Pointer(value)
} }
return composeInterface(unsafe.Pointer(v.typecode), v.value) return composeInterface(v.typecode, v.value)
} }
func (v Value) Type() Type { func (v Value) Type() Type {
@@ -136,7 +136,7 @@ func (v Value) IsZero() bool {
// //
// RawType returns the raw, underlying type code. It is used in the runtime // RawType returns the raw, underlying type code. It is used in the runtime
// package and needs to be exported for the runtime package to access it. // package and needs to be exported for the runtime package to access it.
func (v Value) RawType() *rawType { func (v Value) RawType() rawType {
return v.typecode return v.typecode
} }
@@ -205,7 +205,7 @@ func (v Value) pointer() unsafe.Pointer {
} }
func (v Value) IsValid() bool { func (v Value) IsValid() bool {
return v.typecode != nil return v.typecode != 0
} }
func (v Value) CanInterface() bool { func (v Value) CanInterface() bool {
@@ -453,7 +453,7 @@ func (v Value) Elem() Value {
case Interface: case Interface:
typecode, value := decomposeInterface(*(*interface{})(v.value)) typecode, value := decomposeInterface(*(*interface{})(v.value))
return Value{ return Value{
typecode: (*rawType)(typecode), typecode: typecode,
value: value, value: value,
flags: v.flags &^ valueFlagIndirect, flags: v.flags &^ valueFlagIndirect,
} }
@@ -523,8 +523,6 @@ func (v Value) Field(i int) Value {
} }
} }
var uint8Type = TypeOf(uint8(0)).(*rawType)
func (v Value) Index(i int) Value { func (v Value) Index(i int) Value {
switch v.Kind() { switch v.Kind() {
case Slice: case Slice:
@@ -552,7 +550,7 @@ func (v Value) Index(i int) Value {
panic("reflect: string index out of range") panic("reflect: string index out of range")
} }
return Value{ return Value{
typecode: uint8Type, typecode: Uint8.basicType(),
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(uintptr(s.data) + uintptr(i))))), value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(uintptr(s.data) + uintptr(i))))),
flags: v.flags & valueFlagExported, flags: v.flags & valueFlagExported,
} }
@@ -641,119 +639,30 @@ func (v Value) OverflowFloat(x float64) bool {
} }
func (v Value) MapKeys() []Value { func (v Value) MapKeys() []Value {
if v.Kind() != Map { panic("unimplemented: (reflect.Value).MapKeys()")
panic(&ValueError{Method: "MapKeys", Kind: v.Kind()})
}
// empty map
if v.Len() == 0 {
return nil
}
keys := make([]Value, 0, v.Len())
it := hashmapNewIterator()
k := New(v.typecode.Key())
e := New(v.typecode.Elem())
for hashmapNext(v.pointer(), it, k.value, e.value) {
keys = append(keys, k.Elem())
k = New(v.typecode.Key())
}
return keys
} }
//go:linkname hashmapStringGet runtime.hashmapStringGetUnsafePointer
func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool
//go:linkname hashmapBinaryGet runtime.hashmapBinaryGetUnsafePointer
func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
func (v Value) MapIndex(key Value) Value { func (v Value) MapIndex(key Value) Value {
if v.Kind() != Map {
panic(&ValueError{Method: "MapIndex", Kind: v.Kind()})
}
// compare key type with actual key type of map
if key.typecode != v.typecode.key() {
// type error?
panic("reflect.Value.MapIndex: incompatible types for key")
}
elemType := v.typecode.Elem()
elem := New(elemType)
if key.Kind() == String {
if ok := hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()); !ok {
return Value{}
}
return elem.Elem()
} else if key.typecode.isBinary() {
var keyptr unsafe.Pointer
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
keyptr = key.value
} else {
keyptr = unsafe.Pointer(&key.value)
}
//TODO(dgryski): zero out padding bytes in key, if any
if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
return Value{}
}
return elem.Elem()
}
// TODO(dgryski): Add other map types. For now, just string and binary types are supported.
panic("unimplemented: (reflect.Value).MapIndex()") panic("unimplemented: (reflect.Value).MapIndex()")
} }
//go:linkname hashmapNewIterator runtime.hashmapNewIterator
func hashmapNewIterator() unsafe.Pointer
//go:linkname hashmapNext runtime.hashmapNextUnsafePointer
func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool
func (v Value) MapRange() *MapIter { func (v Value) MapRange() *MapIter {
if v.Kind() != Map { panic("unimplemented: (reflect.Value).MapRange()")
panic(&ValueError{Method: "MapRange", Kind: v.Kind()})
}
return &MapIter{
m: v,
it: hashmapNewIterator(),
key: New(v.typecode.Key()),
val: New(v.typecode.Elem()),
}
} }
type MapIter struct { type MapIter struct {
m Value
it unsafe.Pointer
key Value
val Value
valid bool
} }
func (it *MapIter) Key() Value { func (it *MapIter) Key() Value {
if !it.valid { panic("unimplemented: (*reflect.MapIter).Key()")
panic("reflect.MapIter.Key called on invalid iterator")
}
return it.key.Elem()
} }
func (it *MapIter) Value() Value { func (it *MapIter) Value() Value {
if !it.valid { panic("unimplemented: (*reflect.MapIter).Value()")
panic("reflect.MapIter.Value called on invalid iterator")
}
return it.val.Elem()
} }
func (it *MapIter) Next() bool { func (it *MapIter) Next() bool {
it.valid = hashmapNext(it.m.pointer(), it.it, it.key.value, it.val.value) panic("unimplemented: (*reflect.MapIter).Next()")
return it.valid
} }
func (v Value) Set(x Value) { func (v Value) Set(x Value) {
@@ -894,7 +803,7 @@ func Zero(typ Type) Value {
// new value of the given type. // new value of the given type.
func New(typ Type) Value { func New(typ Type) Value {
return Value{ return Value{
typecode: PtrTo(typ).(*rawType), typecode: PtrTo(typ).(rawType),
value: alloc(typ.Size(), nil), value: alloc(typ.Size(), nil),
flags: valueFlagExported, flags: valueFlagExported,
} }
@@ -992,70 +901,8 @@ func AppendSlice(s, t Value) Value {
} }
} }
//go:linkname hashmapStringSet runtime.hashmapStringSetUnsafePointer
func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer)
//go:linkname hashmapBinarySet runtime.hashmapBinarySetUnsafePointer
func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer)
//go:linkname hashmapStringDelete runtime.hashmapStringDeleteUnsafePointer
func hashmapStringDelete(m unsafe.Pointer, key string)
//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDeleteUnsafePointer
func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer)
func (v Value) SetMapIndex(key, elem Value) { func (v Value) SetMapIndex(key, elem Value) {
if v.Kind() != Map { panic("unimplemented: (reflect.Value).SetMapIndex()")
panic(&ValueError{Method: "SetMapIndex", Kind: v.Kind()})
}
// compare key type with actual key type of map
if key.typecode != v.typecode.key() {
panic("reflect.Value.SetMapIndex: incompatible types for key")
}
// if elem is the zero Value, it means delete
del := elem == Value{}
if !del && elem.typecode != v.typecode.elem() {
panic("reflect.Value.SetMapIndex: incompatible types for value")
}
if key.Kind() == String {
if del {
hashmapStringDelete(v.pointer(), *(*string)(key.value))
} else {
var elemptr unsafe.Pointer
if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
elemptr = elem.value
} else {
elemptr = unsafe.Pointer(&elem.value)
}
hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
}
} else if key.typecode.isBinary() {
var keyptr unsafe.Pointer
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
keyptr = key.value
} else {
keyptr = unsafe.Pointer(&key.value)
}
if del {
hashmapBinaryDelete(v.pointer(), keyptr)
} else {
var elemptr unsafe.Pointer
if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
elemptr = elem.value
} else {
elemptr = unsafe.Pointer(&elem.value)
}
hashmapBinarySet(v.pointer(), keyptr, elemptr)
}
} else {
panic("unimplemented: (reflect.Value).MapIndex()")
}
} }
// FieldByIndex returns the nested field corresponding to index. // FieldByIndex returns the nested field corresponding to index.
@@ -1072,53 +919,9 @@ func (v Value) FieldByName(name string) Value {
panic("unimplemented: (reflect.Value).FieldByName()") panic("unimplemented: (reflect.Value).FieldByName()")
} }
//go:linkname hashmapMake runtime.hashmapMakeUnsafePointer
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer
// MakeMapWithSize creates a new map with the specified type and initial space
// for approximately n elements.
func MakeMapWithSize(typ Type, n int) Value {
// TODO(dgryski): deduplicate these? runtime and reflect both need them.
const (
hashmapAlgorithmBinary uint8 = iota
hashmapAlgorithmString
hashmapAlgorithmInterface
)
if typ.Kind() != Map {
panic(&ValueError{"MakeMap", typ.Kind()})
}
if n < 0 {
panic("reflect.MakeMapWithSize: negative size hint")
}
key := typ.Key().(*rawType)
val := typ.Elem().(*rawType)
var alg uint8
if key.Kind() == String {
alg = hashmapAlgorithmString
} else if key.isBinary() {
alg = hashmapAlgorithmBinary
} else {
panic("reflect.MakeMap: unimplemented key type")
}
m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg)
return Value{
typecode: typ.(*rawType),
value: m,
flags: valueFlagExported,
}
}
// MakeMap creates a new map with the specified type. // MakeMap creates a new map with the specified type.
func MakeMap(typ Type) Value { func MakeMap(typ Type) Value {
return MakeMapWithSize(typ, 8) panic("unimplemented: reflect.MakeMap()")
} }
func (v Value) Call(in []Value) []Value { func (v Value) Call(in []Value) []Value {
-99
View File
@@ -2,7 +2,6 @@ package reflect_test
import ( import (
. "reflect" . "reflect"
"sort"
"testing" "testing"
) )
@@ -31,101 +30,3 @@ func TestIndirectPointers(t *testing.T) {
t.Errorf("bad indirect array index via reflect") t.Errorf("bad indirect array index via reflect")
} }
} }
func TestMap(t *testing.T) {
m := make(map[string]int)
mtyp := TypeOf(m)
if got, want := mtyp.Key().Kind().String(), "string"; got != want {
t.Errorf("m.Type().Key().String()=%q, want %q", got, want)
}
if got, want := mtyp.Elem().Kind().String(), "int"; got != want {
t.Errorf("m.Elem().String()=%q, want %q", got, want)
}
m["foo"] = 2
mref := ValueOf(m)
two := mref.MapIndex(ValueOf("foo"))
if got, want := two.Interface().(int), 2; got != want {
t.Errorf("MapIndex(`foo`)=%v, want %v", got, want)
}
m["bar"] = 3
m["baz"] = 4
m["qux"] = 5
it := mref.MapRange()
var gotKeys []string
for it.Next() {
k := it.Key()
v := it.Value()
kstr := k.Interface().(string)
vint := v.Interface().(int)
gotKeys = append(gotKeys, kstr)
if m[kstr] != vint {
t.Errorf("m[%v]=%v, want %v", kstr, vint, m[kstr])
}
}
var wantKeys []string
for k := range m {
wantKeys = append(wantKeys, k)
}
sort.Strings(gotKeys)
sort.Strings(wantKeys)
if !equal(gotKeys, wantKeys) {
t.Errorf("MapRange return unexpected keys: got %v, want %v", gotKeys, wantKeys)
}
refMapKeys := mref.MapKeys()
gotKeys = gotKeys[:0]
for _, v := range refMapKeys {
gotKeys = append(gotKeys, v.Interface().(string))
}
sort.Strings(gotKeys)
if !equal(gotKeys, wantKeys) {
t.Errorf("MapKeys return unexpected keys: got %v, want %v", gotKeys, wantKeys)
}
mref.SetMapIndex(ValueOf("bar"), Value{})
if _, ok := m["bar"]; ok {
t.Errorf("SetMapIndex failed to delete `bar`")
}
mref.SetMapIndex(ValueOf("baz"), ValueOf(6))
if got, want := m["baz"], 6; got != want {
t.Errorf("SetMapIndex(bar, 6) got %v, want %v", got, want)
}
m2ref := MakeMap(mref.Type())
m2ref.SetMapIndex(ValueOf("foo"), ValueOf(2))
m2 := m2ref.Interface().(map[string]int)
if m2["foo"] != 2 {
t.Errorf("MakeMap failed to create map")
}
}
func equal[T comparable](a, b []T) bool {
if len(a) != len(b) {
return false
}
for i, aa := range a {
if b[i] != aa {
return false
}
}
return true
}
+4 -6
View File
@@ -4,6 +4,7 @@ _tinygo_scanCurrentStack:
#else #else
.section .text.tinygo_scanCurrentStack .section .text.tinygo_scanCurrentStack
.global tinygo_scanCurrentStack .global tinygo_scanCurrentStack
.type tinygo_scanCurrentStack, %function
tinygo_scanCurrentStack: tinygo_scanCurrentStack:
#endif #endif
// Sources: // Sources:
@@ -11,16 +12,12 @@ tinygo_scanCurrentStack:
// * https://godbolt.org/z/qrvrEh // * https://godbolt.org/z/qrvrEh
// Save callee-saved registers. // Save callee-saved registers.
stp x29, x30, [sp, #-160]! stp x29, x30, [sp, #-96]!
stp x28, x27, [sp, #16] stp x28, x27, [sp, #16]
stp x26, x25, [sp, #32] stp x26, x25, [sp, #32]
stp x24, x23, [sp, #48] stp x24, x23, [sp, #48]
stp x22, x21, [sp, #64] stp x22, x21, [sp, #64]
stp x20, x19, [sp, #80] stp x20, x19, [sp, #80]
stp d8, d9, [sp, #96]
stp d10, d11, [sp, #112]
stp d12, d13, [sp, #128]
stp d14, d15, [sp, #144]
// Scan the stack. // Scan the stack.
mov x0, sp mov x0, sp
@@ -31,7 +28,7 @@ tinygo_scanCurrentStack:
#endif #endif
// Restore stack state and return. // Restore stack state and return.
ldp x29, x30, [sp], #160 ldp x29, x30, [sp], #96
ret ret
@@ -41,6 +38,7 @@ _tinygo_longjmp:
#else #else
.section .text.tinygo_longjmp .section .text.tinygo_longjmp
.global tinygo_longjmp .global tinygo_longjmp
.type tinygo_longjmp, %function
tinygo_longjmp: tinygo_longjmp:
#endif #endif
// Note: the code we jump to assumes x0 is set to a non-zero value if we // Note: the code we jump to assumes x0 is set to a non-zero value if we
+36
View File
@@ -0,0 +1,36 @@
.section .text.tinygo_scanCurrentStack,"ax"
.global tinygo_scanCurrentStack
tinygo_scanCurrentStack:
// Sources:
// * https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170
// * https://godbolt.org/z/foc1xncvb
// Save callee-saved registers.
stp x29, x30, [sp, #-160]!
stp x28, x27, [sp, #16]
stp x26, x25, [sp, #32]
stp x24, x23, [sp, #48]
stp x22, x21, [sp, #64]
stp x20, x19, [sp, #80]
stp d8, d9, [sp, #96]
stp d10, d11, [sp, #112]
stp d12, d13, [sp, #128]
stp d14, d15, [sp, #144]
// Scan the stack.
mov x0, sp
bl tinygo_scanstack
// Restore stack state and return.
ldp x29, x30, [sp], #160
ret
.section .text.tinygo_longjmp,"ax"
.global tinygo_longjmp
tinygo_longjmp:
// Note: the code we jump to assumes x0 is set to a non-zero value if we
// jump from here (which is conveniently already the case).
ldp x1, x2, [x0] // jumpSP, jumpPC
mov sp, x1
br x2
-51
View File
@@ -1,51 +0,0 @@
//go:build linux || darwin || windows
package runtime
// Update the C environment if cgo is loaded.
// Called from Go 1.20 and above.
//
//go:linkname syscallSetenv syscall.runtimeSetenv
func syscallSetenv(key, value string) {
keydata := cstring(key)
valdata := cstring(value)
setenv(&keydata[0], &valdata[0])
if key == "GODEBUG" && godebugUpdate != nil {
// Starting with Go 1.20, we need to call a callback (set by
// internal/godebug) to notify the GODEBUG environment variable has
// changed. This is necessary to get archive/zip to pass tests.
godebugUpdate(key, value)
}
}
// Update the C environment if cgo is loaded.
// Called from Go 1.20 and above.
//
//go:linkname syscallUnsetenv syscall.runtimeUnsetenv
func syscallUnsetenv(key string) {
keydata := cstring(key)
unsetenv(&keydata[0])
}
// Compatibility with Go 1.19 and below.
//
//go:linkname syscall_setenv_c syscall.setenv_c
func syscall_setenv_c(key string, val string) {
syscallSetenv(key, val)
}
// Compatibility with Go 1.19 and below.
//
//go:linkname syscall_unsetenv_c syscall.unsetenv_c
func syscall_unsetenv_c(key string) {
syscallUnsetenv(key)
}
// cstring converts a Go string to a C string.
// borrowed from syscall
func cstring(s string) []byte {
data := make([]byte, len(s)+1)
copy(data, s)
// final byte should be zero from the initial allocation
return data
}
+44 -10
View File
@@ -2,6 +2,50 @@
package runtime package runtime
// Update the C environment if cgo is loaded.
// Called from Go 1.20 and above.
//
//go:linkname syscallSetenv syscall.runtimeSetenv
func syscallSetenv(key, value string) {
keydata := cstring(key)
valdata := cstring(value)
// ignore any errors
libc_setenv(&keydata[0], &valdata[0], 1)
}
// Update the C environment if cgo is loaded.
// Called from Go 1.20 and above.
//
//go:linkname syscallUnsetenv syscall.runtimeUnsetenv
func syscallUnsetenv(key string) {
keydata := cstring(key)
// ignore any errors
libc_unsetenv(&keydata[0])
}
// Compatibility with Go 1.19 and below.
//
//go:linkname syscall_setenv_c syscall.setenv_c
func syscall_setenv_c(key string, val string) {
syscallSetenv(key, val)
}
// Compatibility with Go 1.19 and below.
//
//go:linkname syscall_unsetenv_c syscall.unsetenv_c
func syscall_unsetenv_c(key string) {
syscallUnsetenv(key)
}
// cstring converts a Go string to a C string.
// borrowed from syscall
func cstring(s string) []byte {
data := make([]byte, len(s)+1)
copy(data, s)
// final byte should be zero from the initial allocation
return data
}
// int setenv(const char *name, const char *val, int replace); // int setenv(const char *name, const char *val, int replace);
// //
//export setenv //export setenv
@@ -11,13 +55,3 @@ func libc_setenv(name *byte, val *byte, replace int32) int32
// //
//export unsetenv //export unsetenv
func libc_unsetenv(name *byte) int32 func libc_unsetenv(name *byte) int32
func setenv(key, val *byte) {
// ignore any errors
libc_setenv(key, val, 1)
}
func unsetenv(key *byte) {
// ignore any errors
libc_unsetenv(key)
}
-23
View File
@@ -1,23 +0,0 @@
//go:build windows
package runtime
// Set environment variable in Windows:
//
// BOOL SetEnvironmentVariableA(
// [in] LPCSTR lpName,
// [in, optional] LPCSTR lpValue
// );
//
//export SetEnvironmentVariableA
func _SetEnvironmentVariableA(key, val *byte) bool
func setenv(key, val *byte) {
// ignore any errors
_SetEnvironmentVariableA(key, val)
}
func unsetenv(key *byte) {
// ignore any errors
_SetEnvironmentVariableA(key, nil)
}
-8
View File
@@ -277,10 +277,6 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
size += align(unsafe.Sizeof(layout)) size += align(unsafe.Sizeof(layout))
} }
if interrupt.In() {
runtimePanic("alloc in interrupt")
}
gcTotalAlloc += uint64(size) gcTotalAlloc += uint64(size)
gcMallocs++ gcMallocs++
@@ -691,7 +687,3 @@ func ReadMemStats(m *MemStats) {
m.Frees = gcFrees m.Frees = gcFrees
m.Sys = uint64(heapEnd - heapStart) m.Sys = uint64(heapEnd - heapStart)
} }
func SetFinalizer(obj interface{}, finalizer interface{}) {
// Unimplemented.
}
-4
View File
@@ -20,7 +20,6 @@ package runtime
// - func free(ptr unsafe.Pointer) // - func free(ptr unsafe.Pointer)
// - func markRoots(start, end uintptr) // - func markRoots(start, end uintptr)
// - func GC() // - func GC()
// - func SetFinalizer(obj interface{}, finalizer interface{})
// - func ReadMemStats(ms *runtime.MemStats) // - func ReadMemStats(ms *runtime.MemStats)
// //
// //
@@ -52,9 +51,6 @@ func markRoots(start, end uintptr)
// GC is called to explicitly run garbage collection. // GC is called to explicitly run garbage collection.
func GC() func GC()
// SetFinalizer registers a finalizer.
func SetFinalizer(obj interface{}, finalizer interface{})
// ReadMemStats populates m with memory statistics. // ReadMemStats populates m with memory statistics.
func ReadMemStats(ms *MemStats) func ReadMemStats(ms *MemStats)
-4
View File
@@ -85,10 +85,6 @@ func GC() {
// No-op. // No-op.
} }
func SetFinalizer(obj interface{}, finalizer interface{}) {
// No-op.
}
func initHeap() { func initHeap() {
// preinit() may have moved heapStart; reset heapptr // preinit() may have moved heapStart; reset heapptr
heapptr = heapStart heapptr = heapStart
-4
View File
@@ -26,10 +26,6 @@ func GC() {
// Unimplemented. // Unimplemented.
} }
func SetFinalizer(obj interface{}, finalizer interface{}) {
// Unimplemented.
}
func initHeap() { func initHeap() {
// Nothing to initialize. // Nothing to initialize.
} }
+6 -48
View File
@@ -49,10 +49,6 @@ type hashmapIterator struct {
bucketIndex uint8 // current index into bucket bucketIndex uint8 // current index into bucket
} }
func hashmapNewIterator() unsafe.Pointer {
return unsafe.Pointer(new(hashmapIterator))
}
// Get the topmost 8 bits of the hash, without using a special value (like 0). // Get the topmost 8 bits of the hash, without using a special value (like 0).
func hashmapTopHash(hash uint32) uint8 { func hashmapTopHash(hash uint32) uint8 {
tophash := uint8(hash >> 24) tophash := uint8(hash >> 24)
@@ -88,10 +84,6 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashm
} }
} }
func hashmapMakeUnsafePointer(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer {
return (unsafe.Pointer)(hashmapMake(keySize, valueSize, sizeHint, alg))
}
func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool { func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool {
switch alg { switch alg {
case hashmapAlgorithmBinary: case hashmapAlgorithmBinary:
@@ -150,8 +142,10 @@ func hashmapLen(m *hashmap) int {
return int(m.count) return int(m.count)
} }
func hashmapLenUnsafePointer(m unsafe.Pointer) int { // wrapper for use in reflect
return hashmapLen((*hashmap)(m)) func hashmapLenUnsafePointer(p unsafe.Pointer) int {
m := (*hashmap)(p)
return hashmapLen(m)
} }
// Set a specified key to a given value. Grow the map if necessary. // Set a specified key to a given value. Grow the map if necessary.
@@ -214,10 +208,6 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
*emptySlotTophash = tophash *emptySlotTophash = tophash
} }
func hashmapSetUnsafePointer(m unsafe.Pointer, key unsafe.Pointer, value unsafe.Pointer, hash uint32) {
hashmapSet((*hashmap)(m), key, value, hash)
}
// hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and // hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
// value into the bucket, and returns a pointer to this bucket. // value into the bucket, and returns a pointer to this bucket.
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket { func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
@@ -309,10 +299,6 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u
return false return false
} }
func hashmapGetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr, hash uint32) bool {
return hashmapGet((*hashmap)(m), key, value, valueSize, hash)
}
// Delete a given key from the map. No-op when the key does not exist in the // Delete a given key from the map. No-op when the key does not exist in the
// map. // map.
// //
@@ -423,10 +409,6 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
} }
} }
func hashmapNextUnsafePointer(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool {
return hashmapNext((*hashmap)(m), (*hashmapIterator)(it), key, value)
}
// Hashmap with plain binary data keys (not containing strings etc.). // Hashmap with plain binary data keys (not containing strings etc.).
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
if m == nil { if m == nil {
@@ -436,10 +418,6 @@ func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
hashmapSet(m, key, value, hash) hashmapSet(m, key, value, hash)
} }
func hashmapBinarySetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer) {
hashmapBinarySet((*hashmap)(m), key, value)
}
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool { func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool {
if m == nil { if m == nil {
memzero(value, uintptr(valueSize)) memzero(value, uintptr(valueSize))
@@ -449,10 +427,6 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr)
return hashmapGet(m, key, value, valueSize, hash) return hashmapGet(m, key, value, valueSize, hash)
} }
func hashmapBinaryGetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool {
return hashmapBinaryGet((*hashmap)(m), key, value, valueSize)
}
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) { func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
if m == nil { if m == nil {
return return
@@ -461,10 +435,6 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
hashmapDelete(m, key, hash) hashmapDelete(m, key, hash)
} }
func hashmapBinaryDeleteUnsafePointer(m unsafe.Pointer, key unsafe.Pointer) {
hashmapBinaryDelete((*hashmap)(m), key)
}
// Hashmap with string keys (a common case). // Hashmap with string keys (a common case).
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool { func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
@@ -489,10 +459,6 @@ func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
hashmapSet(m, unsafe.Pointer(&key), value, hash) hashmapSet(m, unsafe.Pointer(&key), value, hash)
} }
func hashmapStringSetUnsafePointer(m unsafe.Pointer, key string, value unsafe.Pointer) {
hashmapStringSet((*hashmap)(m), key, value)
}
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool { func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
if m == nil { if m == nil {
memzero(value, uintptr(valueSize)) memzero(value, uintptr(valueSize))
@@ -502,10 +468,6 @@ func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize ui
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash) return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
} }
func hashmapStringGetUnsafePointer(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool {
return hashmapStringGet((*hashmap)(m), key, value, valueSize)
}
func hashmapStringDelete(m *hashmap, key string) { func hashmapStringDelete(m *hashmap, key string) {
if m == nil { if m == nil {
return return
@@ -514,10 +476,6 @@ func hashmapStringDelete(m *hashmap, key string) {
hashmapDelete(m, unsafe.Pointer(&key), hash) hashmapDelete(m, unsafe.Pointer(&key), hash)
} }
func hashmapStringDeleteUnsafePointer(m unsafe.Pointer, key string) {
hashmapStringDelete((*hashmap)(m), key)
}
// Hashmap with interface keys (for everything else). // Hashmap with interface keys (for everything else).
// This is a method that is intentionally unexported in the reflect package. It // This is a method that is intentionally unexported in the reflect package. It
@@ -548,7 +506,7 @@ func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 { func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
x := reflect.ValueOf(itf) x := reflect.ValueOf(itf)
if x.RawType() == nil { if x.RawType() == 0 {
return 0 // nil interface return 0 // nil interface
} }
@@ -605,7 +563,7 @@ func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
} }
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 { func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
_i := *(*interface{})(iptr) _i := *(*_interface)(iptr)
return hashmapInterfaceHash(_i, seed) return hashmapInterfaceHash(_i, seed)
} }
+42 -7
View File
@@ -11,17 +11,17 @@ import (
) )
type _interface struct { type _interface struct {
typecode unsafe.Pointer typecode uintptr
value unsafe.Pointer value unsafe.Pointer
} }
//go:inline //go:inline
func composeInterface(typecode, value unsafe.Pointer) _interface { func composeInterface(typecode uintptr, value unsafe.Pointer) _interface {
return _interface{typecode, value} return _interface{typecode, value}
} }
//go:inline //go:inline
func decomposeInterface(i _interface) (unsafe.Pointer, unsafe.Pointer) { func decomposeInterface(i _interface) (uintptr, unsafe.Pointer) {
return i.typecode, i.value return i.typecode, i.value
} }
@@ -34,7 +34,7 @@ func reflectValueEqual(x, y reflect.Value) bool {
// Note: doing a x.Type() == y.Type() comparison would not work here as that // Note: doing a x.Type() == y.Type() comparison would not work here as that
// would introduce an infinite recursion: comparing two reflect.Type values // would introduce an infinite recursion: comparing two reflect.Type values
// is done with this reflectValueEqual runtime call. // is done with this reflectValueEqual runtime call.
if x.RawType() == nil || y.RawType() == nil { if x.RawType() == 0 || y.RawType() == 0 {
// One of them is nil. // One of them is nil.
return x.RawType() == y.RawType() return x.RawType() == y.RawType()
} }
@@ -94,13 +94,48 @@ func interfaceTypeAssert(ok bool) {
// lowered to inline IR in the interface lowering pass. // lowered to inline IR in the interface lowering pass.
// See compiler/interface-lowering.go for details. // See compiler/interface-lowering.go for details.
type interfaceMethodInfo struct {
signature *uint8 // external *i8 with a name identifying the Go function signature
funcptr uintptr // bitcast from the actual function pointer
}
type typecodeID struct {
// Depending on the type kind of this typecodeID, this pointer is something
// different:
// * basic types: null
// * named type: the underlying type
// * interface: null
// * chan/pointer/slice/array: the element type
// * struct: bitcast of global with structField array
// * func/map: TODO
references *typecodeID
// The array length, for array types.
length uintptr
methodSet *interfaceMethodInfo // nil or a GEP of an array
// The type that's a pointer to this type, nil if it is already a pointer.
// Keeping the type struct alive here is important so that values from
// reflect.New (which uses reflect.PtrTo) can be used in type asserts etc.
ptrTo *typecodeID
// typeAssert is a ptrtoint of a declared interface assert function.
// It only exists to make the rtcalls pass easier.
typeAssert uintptr
}
// structField is used by the compiler to pass information to the interface
// lowering pass. It is not used in the final binary.
type structField struct { type structField struct {
typecode unsafe.Pointer // type of this struct field typecode *typecodeID // type of this struct field
data *uint8 // pointer to byte array containing name, tag, and 'embedded' flag name *uint8 // pointer to char array
tag *uint8 // pointer to char array, or nil
embedded bool
} }
// Pseudo function call used during a type assert. It is used during interface // Pseudo function call used during a type assert. It is used during interface
// lowering, to assign the lowest type numbers to the types with the most type // lowering, to assign the lowest type numbers to the types with the most type
// asserts. Also, it is replaced with const false if this type assert can never // asserts. Also, it is replaced with const false if this type assert can never
// happen. // happen.
func typeAssert(actualType unsafe.Pointer, assertedType *uint8) bool func typeAssert(actualType uintptr, assertedType *uint8) bool
-9
View File
@@ -34,12 +34,3 @@ func Restore(state State) {
"state": state, "state": state,
}) })
} }
// In returns whether the system is currently in an interrupt.
//
// Warning: this always returns false on AVR, as there does not appear to be a
// reliable way to determine whether we're currently running inside an interrupt
// handler.
func In() bool {
return false
}
@@ -50,13 +50,3 @@ func Disable() (state State) {
func Restore(state State) { func Restore(state State) {
arm.EnableInterrupts(uintptr(state)) arm.EnableInterrupts(uintptr(state))
} }
// In returns whether the system is currently in an interrupt.
func In() bool {
// The VECTACTIVE field gives the instruction vector that is currently
// active (in handler mode), or 0 if not in an interrupt.
// Documentation:
// https://developer.arm.com/documentation/dui0497/a/cortex-m0-peripherals/system-control-block/interrupt-control-and-state-register
vectactive := uint8(arm.SCB.ICSR.Get())
return vectactive != 0
}
@@ -2,8 +2,6 @@
package interrupt package interrupt
// This is good documentation of the GBA: https://www.akkit.org/info/gbatek.htm
import ( import (
"runtime/volatile" "runtime/volatile"
"unsafe" "unsafe"
@@ -38,11 +36,8 @@ func (irq Interrupt) Enable() {
regInterruptEnable.SetBits(1 << uint(irq.num)) regInterruptEnable.SetBits(1 << uint(irq.num))
} }
var inInterrupt bool
//export handleInterrupt //export handleInterrupt
func handleInterrupt() { func handleInterrupt() {
inInterrupt = true
flags := regInterruptRequestFlags.Get() flags := regInterruptRequestFlags.Get()
for i := 0; i < 14; i++ { for i := 0; i < 14; i++ {
if flags&(1<<uint(i)) != 0 { if flags&(1<<uint(i)) != 0 {
@@ -50,7 +45,6 @@ func handleInterrupt() {
callInterruptHandler(i) callInterruptHandler(i)
} }
} }
inInterrupt = false
} }
// Pseudo function call that is replaced by the compiler with the actual // Pseudo function call that is replaced by the compiler with the actual
@@ -121,8 +115,3 @@ func Restore(state State) {
// Restore interrupts to the previous state. // Restore interrupts to the previous state.
regGlobalInterruptEnable.Set(uint16(state)) regGlobalInterruptEnable.Set(uint16(state))
} }
// In returns whether the system is currently in an interrupt.
func In() bool {
return inInterrupt
}
-6
View File
@@ -23,9 +23,3 @@ func Disable() (state State) {
// calling Disable, this will not re-enable interrupts, allowing for nested // calling Disable, this will not re-enable interrupts, allowing for nested
// cricital sections. // cricital sections.
func Restore(state State) {} func Restore(state State) {}
// In returns whether the system is currently in an interrupt.
func In() bool {
// There are no interrupts, so it can't be in one.
return false
}
@@ -27,12 +27,3 @@ func Disable() (state State) {
func Restore(state State) { func Restore(state State) {
riscv.EnableInterrupts(uintptr(state)) riscv.EnableInterrupts(uintptr(state))
} }
// In returns whether the system is currently in an interrupt.
func In() bool {
// There is one exception that has the value 0 (instruction address
// misaligned), but it's not very likely and even if it happens, it's not
// really something that can be recovered from. Therefore I think it's safe
// to ignore it. It's handled specially (in handleException).
return riscv.MCAUSE.Get() != 0
}
@@ -29,11 +29,3 @@ func Restore(state State) {
"state": state, "state": state,
}) })
} }
// In returns whether the system is currently in an interrupt.
//
// Warning: interrupts have not been implemented for Xtensa yet so this always
// returns false.
func In() bool {
return false
}
+18
View File
@@ -9,6 +9,11 @@ package runtime
type MemStats struct { type MemStats struct {
// General statistics. // General statistics.
// Alloc is bytes of allocated heap objects.
//
// This is the same as HeapAlloc (see below).
Alloc uint64
// Sys is the total bytes of memory obtained from the OS. // Sys is the total bytes of memory obtained from the OS.
// //
// Sys is the sum of the XSys fields below. Sys measures the // Sys is the sum of the XSys fields below. Sys measures the
@@ -18,6 +23,19 @@ type MemStats struct {
// Heap memory statistics. // Heap memory statistics.
// HeapAlloc is bytes of allocated heap objects.
//
// "Allocated" heap objects include all reachable objects, as
// well as unreachable objects that the garbage collector has
// not yet freed. Specifically, HeapAlloc increases as heap
// objects are allocated and decreases as the heap is swept
// and unreachable objects are freed. Sweeping occurs
// incrementally between GC cycles, so these two processes
// occur simultaneously, and as a result HeapAlloc tends to
// change smoothly (in contrast with the sawtooth that is
// typical of stop-the-world garbage collectors).
HeapAlloc uint64
// HeapSys is bytes of heap memory, total. // HeapSys is bytes of heap memory, total.
// //
// In TinyGo unlike upstream Go, we make no distinction between // In TinyGo unlike upstream Go, we make no distinction between
+8 -7
View File
@@ -88,15 +88,16 @@ func LockOSThread() {
func UnlockOSThread() { func UnlockOSThread() {
} }
// KeepAlive makes sure the value in the interface is alive until at least the func KeepAlive(x interface{}) {
// point of the call. // Unimplemented. Only required with SetFinalizer().
func KeepAlive(x interface{}) }
var godebugUpdate func(string, string) func SetFinalizer(obj interface{}, finalizer interface{}) {
// Unimplemented.
}
//go:linkname godebug_setUpdate internal/godebug.setUpdate //go:linkname godebug_setUpdate internal/godebug.setUpdate
func godebug_setUpdate(update func(string, string)) { func godebug_setUpdate(update func(string, string)) {
// The 'update' function needs to be called whenever the GODEBUG environment // Unimplemented. The 'update' function needs to be called whenever the
// variable changes (for example, via os.Setenv). // GODEBUG environment variable changes (for example, via os.Setenv).
godebugUpdate = update
} }
-5
View File
@@ -27,7 +27,6 @@ func init() {
initSERCOMClocks() initSERCOMClocks()
initUSBClock() initUSBClock()
initADCClock() initADCClock()
enableCache()
cdc.EnableUSBCDC() cdc.EnableUSBCDC()
machine.USBDev.Configure(machine.UARTConfig{}) machine.USBDev.Configure(machine.UARTConfig{})
@@ -368,10 +367,6 @@ func initADCClock() {
sam.GCLK_PCHCTRL_CHEN) sam.GCLK_PCHCTRL_CHEN)
} }
func enableCache() {
sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN)
}
func waitForEvents() { func waitForEvents() {
arm.Asm("wfe") arm.Asm("wfe")
} }
-10
View File
@@ -25,12 +25,6 @@ func main() {
// Zero the threshold value to allow all priorities of interrupts. // Zero the threshold value to allow all priorities of interrupts.
sifive.PLIC.THRESHOLD.Set(0) sifive.PLIC.THRESHOLD.Set(0)
// Zero MCAUSE, which is set to the reset reason on reset. It must be zeroed
// to make interrupt.In() work.
// This would also be a good time to save the reset reason, but that hasn't
// been implemented yet.
riscv.MCAUSE.Set(0)
// Set the interrupt address. // Set the interrupt address.
// Note that this address must be aligned specially, otherwise the MODE bits // Note that this address must be aligned specially, otherwise the MODE bits
// of MTVEC won't be zero. // of MTVEC won't be zero.
@@ -79,10 +73,6 @@ func handleInterrupt() {
// misaligned loads). However, for now we'll just print a fatal error. // misaligned loads). However, for now we'll just print a fatal error.
handleException(code) handleException(code)
} }
// Zero MCAUSE so that it can later be used to see whether we're in an
// interrupt or not.
riscv.MCAUSE.Set(0)
} }
// initPeripherals configures periperhals the way the runtime expects them. // initPeripherals configures periperhals the way the runtime expects them.
-10
View File
@@ -31,12 +31,6 @@ func main() {
kendryte.PLIC.PRIORITY[i].Set(0) kendryte.PLIC.PRIORITY[i].Set(0)
} }
// Zero MCAUSE, which is set to the reset reason on reset. It must be zeroed
// to make interrupt.In() work.
// This would also be a good time to save the reset reason, but that hasn't
// been implemented yet.
riscv.MCAUSE.Set(0)
// Set the interrupt address. // Set the interrupt address.
// Note that this address must be aligned specially, otherwise the MODE bits // Note that this address must be aligned specially, otherwise the MODE bits
// of MTVEC won't be zero. // of MTVEC won't be zero.
@@ -99,10 +93,6 @@ func handleInterrupt() {
// misaligned loads). However, for now we'll just print a fatal error. // misaligned loads). However, for now we'll just print a fatal error.
handleException(code) handleException(code)
} }
// Zero MCAUSE so that it can later be used to see whether we're in an
// interrupt or not.
riscv.MCAUSE.Set(0)
} }
// initPeripherals configures periperhals the way the runtime expects them. // initPeripherals configures periperhals the way the runtime expects them.
-14
View File
@@ -146,8 +146,6 @@ func Unlink(path string) (err error) {
return return
} }
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error)
func Kill(pid int, sig Signal) (err error) { func Kill(pid int, sig Signal) (err error) {
return ENOSYS // TODO return ENOSYS // TODO
} }
@@ -292,18 +290,6 @@ func Environ() []string {
return envs return envs
} }
// BytePtrFromString returns a pointer to a NUL-terminated array of
// bytes containing the text of s. If s contains a NUL byte at any
// location, it returns (nil, EINVAL).
func BytePtrFromString(s string) (*byte, error) {
for i := 0; i < len(s); i++ {
if s[i] == 0 {
return nil, EINVAL
}
}
return &cstring(s)[0], nil
}
// cstring converts a Go string to a C string. // cstring converts a Go string to a C string.
func cstring(s string) []byte { func cstring(s string) []byte {
data := make([]byte, len(s)+1) data := make([]byte, len(s)+1)
-30
View File
@@ -52,9 +52,6 @@ const (
DT_SOCK = 0xc DT_SOCK = 0xc
DT_UNKNOWN = 0x0 DT_UNKNOWN = 0x0
DT_WHT = 0xe DT_WHT = 0xe
F_GETFL = 0x3
F_SETFL = 0x4
O_NONBLOCK = 0x4
) )
// Source: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/sys/errno.h.auto.html // Source: https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/sys/errno.h.auto.html
@@ -65,7 +62,6 @@ const (
EEXIST Errno = 17 EEXIST Errno = 17
EINTR Errno = 4 EINTR Errno = 4
ENOTDIR Errno = 20 ENOTDIR Errno = 20
EISDIR Errno = 21
EINVAL Errno = 22 EINVAL Errno = 22
EMFILE Errno = 24 EMFILE Errno = 24
EPIPE Errno = 32 EPIPE Errno = 32
@@ -275,32 +271,6 @@ func Getpagesize() int {
return int(libc_getpagesize()) return int(libc_getpagesize())
} }
// The following RawSockAddr* types have been copied from the Go source tree and
// are here purely to fix build errors.
type RawSockaddr struct {
Len uint8
Family uint8
Data [14]int8
}
type RawSockaddrInet4 struct {
Len uint8
Family uint8
Port uint16
Addr [4]byte /* in_addr */
Zero [8]int8
}
type RawSockaddrInet6 struct {
Len uint8
Family uint8
Port uint16
Flowinfo uint32
Addr [16]byte /* in6_addr */
Scope_id uint32
}
// int pipe(int32 *fds); // int pipe(int32 *fds);
// //
//export pipe //export pipe
@@ -71,14 +71,6 @@ func Getpagesize() int {
return 4096 // TODO return 4096 // TODO
} }
type RawSockaddrInet4 struct {
// stub
}
type RawSockaddrInet6 struct {
// stub
}
// int open(const char *pathname, int flags, mode_t mode); // int open(const char *pathname, int flags, mode_t mode);
// //
//export open //export open
-42
View File
@@ -87,22 +87,6 @@ const (
PROT_READ = 1 PROT_READ = 1
PROT_WRITE = 2 PROT_WRITE = 2
PROT_EXEC = 4 PROT_EXEC = 4
// ../../lib/wasi-libc/expected/wasm32-wasi/predefined-macros.txt
F_GETFL = 3
F_SETFL = 4
)
// These values are needed as a stub until Go supports WASI as a full target.
// The constant values don't have a meaning and don't correspond to anything
// real.
const (
_ = iota
SYS_FCNTL
SYS_FCNTL64
SYS_FSTATAT64
SYS_OPENAT
SYS_UNLINKAT
) )
//go:extern errno //go:extern errno
@@ -324,32 +308,6 @@ func Getpagesize() int {
return 65536 return 65536
} }
type Utsname struct {
Sysname [65]int8
Nodename [65]int8
Release [65]int8
Version [65]int8
Machine [65]int8
Domainname [65]int8
}
// Stub Utsname, needed because WASI pretends to be linux/arm.
func Uname(buf *Utsname) (err error)
type RawSockaddrInet4 struct {
// stub
}
type RawSockaddrInet6 struct {
// stub
}
// This is a stub, it is not functional.
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
// This is a stub, it is not functional.
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
// int stat(const char *path, struct stat * buf); // int stat(const char *path, struct stat * buf);
// //
//export stat //export stat
-8
View File
@@ -210,11 +210,3 @@ func Getpagesize() int {
// common assumption when pagesize is unknown // common assumption when pagesize is unknown
return 4096 return 4096
} }
type RawSockaddrInet4 struct {
// stub
}
type RawSockaddrInet6 struct {
// stub
}
-15
View File
@@ -1,18 +1,3 @@
package syscall package syscall
func Exec(argv0 string, argv []string, envv []string) (err error) func Exec(argv0 string, argv []string, envv []string) (err error)
// The two SockaddrInet* structs have been copied from the Go source tree.
type SockaddrInet4 struct {
Port int
Addr [4]byte
raw RawSockaddrInet4
}
type SockaddrInet6 struct {
Port int
ZoneId uint32
Addr [16]byte
raw RawSockaddrInet6
}
-4
View File
@@ -69,7 +69,3 @@ _heap_start = _ebss;
_heap_end = ORIGIN(RAM) + LENGTH(RAM); _heap_end = ORIGIN(RAM) + LENGTH(RAM);
_globals_start = _sdata; _globals_start = _sdata;
_globals_end = _ebss; _globals_end = _ebss;
/* For the flash API */
__flash_data_start = LOADADDR(.data) + SIZEOF(.data);
__flash_data_end = ORIGIN(FLASH_TEXT) + LENGTH(FLASH_TEXT);

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