mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e5f602655 |
+14
-10
@@ -22,16 +22,15 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-14-v2
|
||||
- llvm-source-13-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-14-v2
|
||||
key: llvm-source-13-v1
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/compiler-rt
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
hack-ninja-jobs:
|
||||
@@ -86,10 +85,10 @@ commands:
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-systemclang-v5
|
||||
- wasi-libc-sysroot-systemclang-v4
|
||||
- run: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-systemclang-v5
|
||||
key: wasi-libc-sysroot-systemclang-v4
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run: make gen-device -j4
|
||||
@@ -102,16 +101,21 @@ commands:
|
||||
- run: make fmt-check
|
||||
|
||||
jobs:
|
||||
test-llvm13-go116:
|
||||
test-llvm11-go115:
|
||||
docker:
|
||||
- image: circleci/golang:1.15-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "11"
|
||||
test-llvm12-go116:
|
||||
docker:
|
||||
- image: circleci/golang:1.16-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "13"
|
||||
llvm: "12"
|
||||
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
- test-llvm13-go116
|
||||
- test-llvm11-go115
|
||||
- test-llvm12-go116
|
||||
|
||||
@@ -10,12 +10,12 @@ on:
|
||||
jobs:
|
||||
build-macos:
|
||||
name: build-macos
|
||||
runs-on: macos-11
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
go-version: '1.17'
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -35,11 +35,10 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-macos-v1
|
||||
key: llvm-source-13-macos-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
@@ -49,7 +48,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-14-macos-v1
|
||||
key: llvm-build-13-macos-v3
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -67,7 +66,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v3
|
||||
key: wasi-libc-sysroot-v2
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
|
||||
@@ -57,27 +57,3 @@ jobs:
|
||||
--header 'Content-Type: application/json' \
|
||||
-d '{"branch": "dev"}' \
|
||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
||||
- name: Trigger Bluetooth repo build on CircleCI
|
||||
run: |
|
||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/bluetooth/pipeline' \
|
||||
--header 'Content-Type: application/json' \
|
||||
-d '{"branch": "dev"}' \
|
||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
||||
- name: Trigger TinyFS repo build on CircleCI
|
||||
run: |
|
||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfs/pipeline' \
|
||||
--header 'Content-Type: application/json' \
|
||||
-d '{"branch": "dev"}' \
|
||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
||||
- name: Trigger TinyFont repo build on CircleCI
|
||||
run: |
|
||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinyfont/pipeline' \
|
||||
--header 'Content-Type: application/json' \
|
||||
-d '{"branch": "dev"}' \
|
||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
||||
- name: Trigger TinyDraw repo build on CircleCI
|
||||
run: |
|
||||
curl --location --request POST 'https://circleci.com/api/v2/project/github/tinygo-org/tinydraw/pipeline' \
|
||||
--header 'Content-Type: application/json' \
|
||||
-d '{"branch": "dev"}' \
|
||||
-u "${{ secrets.CIRCLECI_API_TOKEN }}"
|
||||
|
||||
+12
-219
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
go-version: '1.17'
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
@@ -38,11 +38,10 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-linux-v2
|
||||
key: llvm-source-13-linux-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
@@ -52,7 +51,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-14-linux-v1
|
||||
key: llvm-build-13-linux-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -77,13 +76,14 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v4
|
||||
key: wasi-libc-sysroot-linux-asserts-v3
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
run: make wasi-libc
|
||||
- name: Install fpm
|
||||
run: |
|
||||
sudo apt-get install ruby ruby-dev
|
||||
sudo gem install --no-document fpm
|
||||
- name: Build TinyGo release
|
||||
run: |
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
name: release-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-amd64.tar.gz
|
||||
/tmp/tinygo_amd64.deb
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
go-version: '1.17'
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
name: release-double-zipped
|
||||
- name: Extract release tarball
|
||||
run: |
|
||||
mkdir -p ~/lib
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
go-version: '1.17'
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
@@ -179,11 +179,10 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-linux-asserts-v2
|
||||
key: llvm-source-13-linux-asserts-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
@@ -193,7 +192,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-14-linux-asserts-v1
|
||||
key: llvm-build-13-linux-asserts-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -218,7 +217,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-linux-asserts-v4
|
||||
key: wasi-libc-sysroot-linux-asserts-v3
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
@@ -240,209 +239,3 @@ jobs:
|
||||
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
|
||||
- run: make smoketest
|
||||
- run: make wasmtest
|
||||
- run: make tinygo-baremetal
|
||||
build-linux-arm:
|
||||
# Build ARM Linux binaries, ready for release.
|
||||
# This intentionally uses an older Linux image, so that we compile against
|
||||
# an older glibc version and therefore are compatible with a wide range of
|
||||
# Linux distributions.
|
||||
# It is set to "needs: build-linux" because it modifies the release created
|
||||
# in that process to avoid doing lots of duplicate work and to avoid
|
||||
# complications around precompiled libraries such as compiler-rt shipped as
|
||||
# part of the release tarball.
|
||||
runs-on: ubuntu-18.04
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends \
|
||||
qemu-user \
|
||||
g++-arm-linux-gnueabihf \
|
||||
libc6-dev-armhf-cross
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: go-cache-linux-arm-v2-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Cache LLVM source
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-linux-v2
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache LLVM build
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-14-linux-arm-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# Install build dependencies.
|
||||
sudo apt-get install --no-install-recommends ninja-build
|
||||
# build!
|
||||
make llvm-build CROSS=arm-linux-gnueabihf
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v2
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-arm-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends ninja-build
|
||||
git submodule update --init lib/binaryen
|
||||
make CROSS=arm-linux-gnueabihf binaryen
|
||||
- name: Install fpm
|
||||
run: |
|
||||
sudo gem install --no-document fpm
|
||||
- name: Build TinyGo binary
|
||||
run: |
|
||||
make CROSS=arm-linux-gnueabihf
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||
- name: Modify release
|
||||
run: |
|
||||
cp -p build/tinygo build/release/tinygo/bin
|
||||
cp -p build/wasm-opt build/release/tinygo/bin
|
||||
- name: Create arm release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=armhf
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_armhf.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: linux-arm-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-arm.tar.gz
|
||||
/tmp/tinygo_armhf.deb
|
||||
build-linux-arm64:
|
||||
# Build ARM64 Linux binaries, ready for release.
|
||||
# It is set to "needs: build-linux" because it modifies the release created
|
||||
# in that process to avoid doing lots of duplicate work and to avoid
|
||||
# complications around precompiled libraries such as compiler-rt shipped as
|
||||
# part of the release tarball.
|
||||
runs-on: ubuntu-18.04
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends \
|
||||
qemu-user \
|
||||
g++-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
- name: Cache Go
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: go-cache-linux-arm64-v2-${{ hashFiles('go.mod') }}
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
- name: Cache LLVM source
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-linux-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Cache LLVM build
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-14-linux-arm64-v1
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# build!
|
||||
make llvm-build CROSS=aarch64-linux-gnu
|
||||
# Remove unnecessary object files (to reduce cache size).
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
- name: Cache Binaryen
|
||||
uses: actions/cache@v2
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-arm64-v1
|
||||
path: build/wasm-opt
|
||||
- name: Build Binaryen
|
||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git submodule update --init lib/binaryen
|
||||
make CROSS=aarch64-linux-gnu binaryen
|
||||
- name: Install fpm
|
||||
run: |
|
||||
sudo gem install --no-document fpm
|
||||
- name: Build TinyGo binary
|
||||
run: |
|
||||
make CROSS=aarch64-linux-gnu
|
||||
- name: Download amd64 release
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: linux-amd64-double-zipped
|
||||
- name: Extract amd64 release
|
||||
run: |
|
||||
mkdir -p build/release
|
||||
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
|
||||
- name: Modify release
|
||||
run: |
|
||||
cp -p build/tinygo build/release/tinygo/bin
|
||||
cp -p build/wasm-opt build/release/tinygo/bin
|
||||
- name: Create arm64 release
|
||||
run: |
|
||||
make release deb RELEASEONLY=1 DEB_ARCH=arm64
|
||||
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
|
||||
cp -p build/release.deb /tmp/tinygo_arm64.deb
|
||||
- name: Publish release artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: linux-arm64-double-zipped
|
||||
path: |
|
||||
/tmp/tinygo.linux-arm64.tar.gz
|
||||
/tmp/tinygo_arm64.deb
|
||||
|
||||
@@ -9,15 +9,13 @@ on:
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-2022
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18.1'
|
||||
- uses: brechtm/setup-scoop@v2
|
||||
with:
|
||||
scoop_update: 'false'
|
||||
go-version: '1.17'
|
||||
- uses: MinoruSekine/setup-scoop@v1
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -37,11 +35,10 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-14-windows-v2
|
||||
key: llvm-source-13-windows-v1
|
||||
path: |
|
||||
llvm-project/clang/lib/Headers
|
||||
llvm-project/clang/include
|
||||
llvm-project/compiler-rt
|
||||
llvm-project/lld/include
|
||||
llvm-project/llvm/include
|
||||
- name: Download LLVM source
|
||||
@@ -51,7 +48,7 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-llvm-build
|
||||
with:
|
||||
key: llvm-build-14-windows-v1
|
||||
key: llvm-build-13-windows-v2
|
||||
path: llvm-build
|
||||
- name: Build LLVM
|
||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||
@@ -68,14 +65,11 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
id: cache-wasi-libc
|
||||
with:
|
||||
key: wasi-libc-sysroot-v3
|
||||
key: wasi-libc-sysroot-v2
|
||||
path: lib/wasi-libc/sysroot
|
||||
- name: Build wasi-libc
|
||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
||||
run: make wasi-libc
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
scoop install wasmtime
|
||||
- name: Test TinyGo
|
||||
shell: bash
|
||||
run: make test GOTESTFLAGS="-v -short"
|
||||
@@ -101,5 +95,3 @@ jobs:
|
||||
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
|
||||
- name: Test stdlib packages
|
||||
run: make tinygo-test
|
||||
- name: Test stdlib packages on wasi
|
||||
run: make tinygo-test-wasi-fast
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
[submodule "lib/cmsis-svd"]
|
||||
path = lib/cmsis-svd
|
||||
url = https://github.com/tinygo-org/cmsis-svd
|
||||
[submodule "lib/compiler-rt"]
|
||||
path = lib/compiler-rt
|
||||
url = https://github.com/llvm-mirror/compiler-rt.git
|
||||
branch = release_80
|
||||
[submodule "lib/wasi-libc"]
|
||||
path = lib/wasi-libc
|
||||
url = https://github.com/CraneStation/wasi-libc
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
|
||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||
|
||||
* Go (1.16+)
|
||||
* Go (1.15+)
|
||||
* Standard build tools (gcc/clang)
|
||||
* git
|
||||
* CMake
|
||||
|
||||
@@ -1,101 +1,3 @@
|
||||
0.23.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- add `-work` flag
|
||||
- add Go 1.18 support
|
||||
- add LLVM 14 support
|
||||
- `run`: add support for command-line parameters
|
||||
- `build`: calculate default output path if `-o` is not specified
|
||||
- `build`: add JSON output
|
||||
- `test`: support multiple test binaries with `-c`
|
||||
- `test`: support flags like `-v` on all targets (including emulated firmware)
|
||||
* **compiler**
|
||||
- add support for ThinLTO
|
||||
- use compiler-rt from LLVM
|
||||
- `builder`: prefer GNU build ID over Go build ID for caching
|
||||
- `builder`: add support for cross compiling to Darwin
|
||||
- `builder`: support machine outlining pass in stacksize calculation
|
||||
- `builder`: disable asynchronous unwind tables
|
||||
- `compileopts`: fix emulator configuration on non-amd64 Linux architectures
|
||||
- `compiler`: move allocations > 256 bytes to the heap
|
||||
- `compiler`: fix incorrect `unsafe.Alignof` on some 32-bit architectures
|
||||
- `compiler`: accept alias for slice `cap` builtin
|
||||
- `compiler`: allow slices of empty structs
|
||||
- `compiler`: fix difference in aliases in interface methods
|
||||
- `compiler`: make `RawSyscall` an alias for `Syscall`
|
||||
- `compiler`: remove support for memory references in `AsmFull`
|
||||
- `loader`: only add Clang header path for CGo
|
||||
- `transform`: fix poison value in heap-to-stack transform
|
||||
* **standard library**
|
||||
- `internal/fuzz`: add this package as a shim
|
||||
- `os`: implement readdir for darwin and linux
|
||||
- `os`: add `DirFS`, which is used by many programs to access readdir.
|
||||
- `os`: isWine: be compatible with older versions of wine, too
|
||||
- `os`: implement `RemoveAll`
|
||||
- `os`: Use a `uintptr` for `NewFile`
|
||||
- `os`: add stubs for `exec.ExitError` and `ProcessState.ExitCode`
|
||||
- `os`: export correct values for `DevNull` for each OS
|
||||
- `os`: improve support for `Signal` by fixing various bugs
|
||||
- `os`: implement `File.Fd` method
|
||||
- `os`: implement `UserHomeDir`
|
||||
- `os`: add `exec.ProcessState` stub
|
||||
- `os`: implement `Pipe` for darwin
|
||||
- `os`: define stub `ErrDeadlineExceeded`
|
||||
- `reflect`: add stubs for more missing methods
|
||||
- `reflect`: rename `reflect.Ptr` to `reflect.Pointer`
|
||||
- `reflect`: add `Value.FieldByIndexErr` stub
|
||||
- `runtime`: fix various small GC bugs
|
||||
- `runtime`: use memzero for leaking collector instead of manually zeroing objects
|
||||
- `runtime`: implement `memhash`
|
||||
- `runtime`: implement `fastrand`
|
||||
- `runtime`: add stub for `debug.ReadBuildInfo`
|
||||
- `runtime`: add stub for `NumCPU`
|
||||
- `runtime`: don't inline `runtime.alloc` with `-gc=leaking`
|
||||
- `runtime`: add `Version`
|
||||
- `runtime`: add stubs for `NumCgoCall` and `NumGoroutine`
|
||||
- `runtime`: stub {Lock,Unlock}OSThread on Windows
|
||||
- `runtime`: be able to deal with a very small heap
|
||||
- `syscall`: make `Environ` return a copy of the environment
|
||||
- `syscall`: implement getpagesize and munmap
|
||||
- `syscall`: `wasi`: define `MAP_SHARED` and `PROT_READ`
|
||||
- `syscall`: stub mmap(), munmap(), MAP_SHARED, PROT_READ, SIGBUS, etc. on nonhosted targets
|
||||
- `syscall`: darwin: more complete list of signals
|
||||
- `syscall`: `wasi`: more complete list of signals
|
||||
- `syscall`: stub `WaitStatus`
|
||||
- `syscall/js`: allow copyBytesTo(Go|JS) to use `Uint8ClampedArray`
|
||||
- `testing`: implement `TempDir`
|
||||
- `testing`: nudge type TB closer to upstream; should be a no-op change.
|
||||
- `testing`: on baremetal platforms, use simpler test matcher
|
||||
* **targets**
|
||||
- `atsamd`: fix usbcdc initialization when `-serial=uart`
|
||||
- `atsamd51`: allow higher frequency when using SPI
|
||||
- `esp`: support CGo
|
||||
- `esp32c3`: add support for input pin
|
||||
- `esp32c3`: add support for GPIO interrupts
|
||||
- `esp32c3`: add support to receive UART data
|
||||
- `rp2040`: fix PWM bug at high frequency
|
||||
- `rp2040`: fix some minor I2C bugs
|
||||
- `rp2040`: fix incorrect inline assembly
|
||||
- `rp2040`: fix spurious i2c STOP during write+read transaction
|
||||
- `rp2040`: improve ADC support
|
||||
- `wasi`: remove `--export-dynamic` linker flag
|
||||
- `wasm`: remove heap allocator from wasi-libc
|
||||
* **boards**
|
||||
- `circuitplay-bluefruit`: move pin mappings so board can be compiled for WASM use in Playground
|
||||
- `esp32-c3-12f`: add the ESP32-C3-12f Kit
|
||||
- `m5stamp-c3`: add pin setting of UART
|
||||
- `macropad-rp2040`: add the Adafruit MacroPad RP2040 board
|
||||
- `nano-33-ble`: typo in LPS22HB peripheral definition and documentation (#2579)
|
||||
- `teensy41`: add the Teensy 4.1 board
|
||||
- `teensy40`: add ADC support
|
||||
- `teensy40`: add SPI support
|
||||
- `thingplus-rp2040`: add the SparkFun Thing Plus RP2040 board
|
||||
- `wioterminal`: add DefaultUART
|
||||
- `wioterminal`: verify written data when flashing through OpenOCD
|
||||
- `xiao-ble`: add XIAO BLE nRF52840 support
|
||||
|
||||
|
||||
0.22.0
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# tinygo-llvm stage obtains the llvm source for TinyGo
|
||||
FROM golang:1.18 AS tinygo-llvm
|
||||
FROM golang:1.17 AS tinygo-llvm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
|
||||
|
||||
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
||||
|
||||
# Try to autodetect LLVM build tools.
|
||||
# Versions are listed here in descending priority order.
|
||||
LLVM_VERSIONS = 14 13 12 11
|
||||
LLVM_VERSIONS = 13 12 11
|
||||
errifempty = $(if $(1),$(1),$(error $(2)))
|
||||
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
||||
toolSearchPathsVersion = $(1)-$(2)
|
||||
@@ -50,38 +50,6 @@ else
|
||||
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
|
||||
endif
|
||||
|
||||
# Cross compiling support.
|
||||
ifneq ($(CROSS),)
|
||||
CC = $(CROSS)-gcc
|
||||
CXX = $(CROSS)-g++
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_C_COMPILER=$(CC) \
|
||||
-DCMAKE_CXX_COMPILER=$(CXX) \
|
||||
-DLLVM_DEFAULT_TARGET_TRIPLE=$(CROSS) \
|
||||
-DCROSS_TOOLCHAIN_FLAGS_NATIVE="-UCMAKE_C_COMPILER;-UCMAKE_CXX_COMPILER"
|
||||
ifeq ($(CROSS), arm-linux-gnueabihf)
|
||||
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||
LLVM_CONFIG_PREFIX = qemu-arm -L /usr/arm-linux-gnueabihf/
|
||||
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DLLVM_TARGET_ARCH=ARM
|
||||
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||
else ifeq ($(CROSS), aarch64-linux-gnu)
|
||||
# Assume we're building on a Debian-like distro, with QEMU installed.
|
||||
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
|
||||
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
|
||||
LLVM_OPTION += \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DLLVM_TARGET_ARCH=AArch64
|
||||
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
|
||||
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
|
||||
else
|
||||
$(error Unknown cross compilation target: $(CROSS))
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
|
||||
@@ -123,11 +91,11 @@ CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGe
|
||||
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||
|
||||
# Libraries that should be linked in for the statically linked LLD.
|
||||
LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
|
||||
LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO2 lldMinGW lldReaderWriter lldWasm lldYAML
|
||||
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
||||
|
||||
# Other libraries that are needed to link TinyGo.
|
||||
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA
|
||||
EXTRA_LIB_NAMES = LLVMInterpreter
|
||||
|
||||
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
|
||||
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
|
||||
@@ -142,9 +110,9 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(ad
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++14
|
||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
endif
|
||||
|
||||
clean:
|
||||
@@ -207,12 +175,12 @@ gen-device-rp: build/gen-device-svd
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
git clone -b xtensa_release_14.0.0-patched --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
git clone -b xtensa_release_13.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
$(LLVM_BUILDDIR)/build.ninja:
|
||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
||||
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.
|
||||
@@ -240,18 +208,20 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
# Build the Go compiler.
|
||||
tinygo:
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||
|
||||
test: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
|
||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||
# Tests that take over a minute in wasi
|
||||
TEST_PACKAGES_SLOW = \
|
||||
compress/bzip2 \
|
||||
compress/flate \
|
||||
crypto/dsa \
|
||||
index/suffixarray \
|
||||
|
||||
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
|
||||
TEST_PACKAGES_FAST = \
|
||||
TEST_PACKAGES_BASE = \
|
||||
compress/lzw \
|
||||
compress/zlib \
|
||||
container/heap \
|
||||
container/list \
|
||||
@@ -265,7 +235,6 @@ TEST_PACKAGES_FAST = \
|
||||
crypto/sha256 \
|
||||
crypto/sha512 \
|
||||
debug/macho \
|
||||
embed/internal/embedtest \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
encoding/base32 \
|
||||
@@ -295,71 +264,47 @@ TEST_PACKAGES_FAST = \
|
||||
unicode/utf16 \
|
||||
unicode/utf8 \
|
||||
|
||||
# archive/zip requires os.ReadAt, which is not yet supported on windows
|
||||
# Standard library packages that pass tests natively
|
||||
TEST_PACKAGES := \
|
||||
$(TEST_PACKAGES_BASE)
|
||||
|
||||
# archive/zip requires ReadAt, which is not yet supported on windows
|
||||
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
||||
# io/fs 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
|
||||
# compress/flate fails windows go 1.18, https://github.com/tinygo-org/tinygo/issues/2762
|
||||
# compress/lzw fails windows go 1.18 wasi, https://github.com/tinygo-org/tinygo/issues/2762
|
||||
|
||||
# Additional standard library packages that pass tests on individual platforms
|
||||
TEST_PACKAGES_LINUX := \
|
||||
ifneq ($(OS),Windows_NT)
|
||||
TEST_PACKAGES := \
|
||||
$(TEST_PACKAGES) \
|
||||
archive/zip \
|
||||
compress/flate \
|
||||
compress/lzw \
|
||||
debug/dwarf \
|
||||
debug/plan9obj \
|
||||
io/fs \
|
||||
io/ioutil \
|
||||
testing/fstest
|
||||
|
||||
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
|
||||
|
||||
TEST_PACKAGES_WINDOWS := \
|
||||
compress/lzw
|
||||
|
||||
# Report platforms on which each standard library package is known to pass tests
|
||||
jointmp := $(shell echo /tmp/join.$$$$)
|
||||
report-stdlib-tests-pass:
|
||||
@for t in $(TEST_PACKAGES_DARWIN); do echo "$$t darwin"; done | sort > $(jointmp).darwin
|
||||
@for t in $(TEST_PACKAGES_LINUX); do echo "$$t linux"; done | sort > $(jointmp).linux
|
||||
@for t in $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW); do echo "$$t darwin linux wasi windows"; done | sort > $(jointmp).portable
|
||||
@join -a1 -a2 $(jointmp).darwin $(jointmp).linux | \
|
||||
join -a1 -a2 - $(jointmp).portable
|
||||
@rm $(jointmp).*
|
||||
|
||||
# Standard library packages that pass tests quickly on the current platform
|
||||
ifeq ($(shell uname),Darwin)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
||||
endif
|
||||
ifeq ($(shell uname),Linux)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||
endif
|
||||
ifeq ($(OS),Windows_NT)
|
||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
endif
|
||||
|
||||
# Standard library packages that pass tests on wasi
|
||||
TEST_PACKAGES_WASI = \
|
||||
$(TEST_PACKAGES_BASE)
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test $(TEST_PACKAGES) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-test-fast:
|
||||
$(TINYGO) test $(TEST_PACKAGES_HOST)
|
||||
$(TINYGO) test $(TEST_PACKAGES)
|
||||
tinygo-bench:
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-fast:
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST)
|
||||
|
||||
# Same thing, except for wasi rather than the current platform.
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES)
|
||||
tinygo-test-wasi:
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_WASI) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-test-wasi-fast:
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST)
|
||||
$(TINYGO) test -target wasi $(TEST_PACKAGES_WASI)
|
||||
tinygo-bench-wasi:
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_WASI) $(TEST_PACKAGES_SLOW)
|
||||
tinygo-bench-wasi-fast:
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST)
|
||||
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_WASI)
|
||||
|
||||
# Test external packages in a large corpus.
|
||||
test-corpus:
|
||||
@@ -369,10 +314,6 @@ test-corpus-fast:
|
||||
test-corpus-wasi: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasi
|
||||
|
||||
tinygo-baremetal:
|
||||
# Regression tests that run on a baremetal target and don't fit in either main_test.go or smoketest.
|
||||
# regression test for #2666: e.g. encoding/hex must pass on baremetal
|
||||
$(TINYGO) test -target cortex-m-qemu encoding/hex
|
||||
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
@@ -426,8 +367,6 @@ ifneq ($(WASM), 0)
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
endif
|
||||
# test all targets/boards
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||
@@ -644,8 +583,7 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -o test ./testdata/cgo
|
||||
ifneq ($(OS),Windows_NT)
|
||||
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
||||
# not garbage collected.
|
||||
@@ -660,6 +598,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||
@@ -681,6 +620,9 @@ endif
|
||||
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
|
||||
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
|
||||
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
|
||||
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
|
||||
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
|
||||
@cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk
|
||||
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
|
||||
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
||||
@@ -717,29 +659,21 @@ endif
|
||||
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
||||
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||
@cp -rp src build/release/tinygo/src
|
||||
@cp -rp targets build/release/tinygo/targets
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
||||
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
|
||||
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
|
||||
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
|
||||
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
|
||||
|
||||
release:
|
||||
release: build/release
|
||||
tar -czf build/release.tar.gz -C build/release tinygo
|
||||
|
||||
DEB_ARCH ?= native
|
||||
deb:
|
||||
deb: build/release
|
||||
@mkdir -p build/release-deb/usr/local/bin
|
||||
@mkdir -p build/release-deb/usr/local/lib
|
||||
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
|
||||
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
|
||||
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
||||
|
||||
ifneq ($(RELEASEONLY), 1)
|
||||
release: build/release
|
||||
deb: build/release
|
||||
endif
|
||||
fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
|
||||
|
||||
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 85 microcontroller boards are currently supported:
|
||||
The following 82 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
@@ -85,7 +85,6 @@ The following 85 microcontroller boards are currently supported:
|
||||
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
|
||||
* [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32)
|
||||
* [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32)
|
||||
* [ESP32-C3-12f](https://www.espressif.com/en/products/socs/esp32-c3)
|
||||
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
|
||||
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
|
||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||
@@ -113,12 +112,10 @@ The following 85 microcontroller boards are currently supported:
|
||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
|
||||
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
|
||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
|
||||
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
|
||||
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
||||
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
|
||||
|
||||
+67
-309
@@ -4,7 +4,6 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
@@ -38,14 +37,8 @@ import (
|
||||
// BuildResult is the output of a build. This includes the binary itself and
|
||||
// some other metadata that is obtained while building the binary.
|
||||
type BuildResult struct {
|
||||
// The executable directly from the linker, usually including debug
|
||||
// information. Used for GDB for example.
|
||||
Executable string
|
||||
|
||||
// A path to the output binary. It will be removed after Build returns, so
|
||||
// if it should be kept it must be copied or moved away.
|
||||
// It is often the same as Executable, but differs if the output format is
|
||||
// .hex for example (instead of the usual ELF).
|
||||
Binary string
|
||||
|
||||
// The directory of the main package. This is useful for testing as the test
|
||||
@@ -81,7 +74,6 @@ type packageAction struct {
|
||||
Config *compiler.Config
|
||||
CFlags []string
|
||||
FileHashes map[string]string // hash of every file that's part of the package
|
||||
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
|
||||
Imports map[string]string // map from imported package to action ID hash
|
||||
OptLevel int // LLVM optimization level (0-3)
|
||||
SizeLevel int // LLVM optimization for size level (0-2)
|
||||
@@ -113,15 +105,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
defer os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
// Look up the build cache directory, which is used to speed up incremental
|
||||
// builds.
|
||||
cacheDir := goenv.Get("GOCACHE")
|
||||
if cacheDir == "off" {
|
||||
// Use temporary build directory instead, effectively disabling the
|
||||
// build cache.
|
||||
cacheDir = dir
|
||||
}
|
||||
|
||||
// Check for a libc dependency.
|
||||
// As a side effect, this also creates the headers for the given libc, if
|
||||
// the libc needs them.
|
||||
@@ -190,7 +173,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
||||
@@ -211,23 +193,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Add jobs to compile each package.
|
||||
// Packages that have a cache hit will not be compiled again.
|
||||
var packageJobs []*compileJob
|
||||
packageActionIDJobs := make(map[string]*compileJob)
|
||||
|
||||
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
||||
version := goenv.Version
|
||||
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
|
||||
version += "-" + goenv.GitSha1
|
||||
}
|
||||
if config.Options.GlobalValues == nil {
|
||||
config.Options.GlobalValues = make(map[string]map[string]string)
|
||||
}
|
||||
if config.Options.GlobalValues["runtime"] == nil {
|
||||
config.Options.GlobalValues["runtime"] = make(map[string]string)
|
||||
}
|
||||
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
||||
}
|
||||
|
||||
var embedFileObjects []*compileJob
|
||||
packageBitcodePaths := make(map[string]string)
|
||||
packageActionIDs := make(map[string]string)
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg // necessary to avoid a race condition
|
||||
|
||||
@@ -237,114 +204,58 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
}
|
||||
sort.Strings(undefinedGlobals)
|
||||
|
||||
// Make compile jobs to load files to be embedded in the output binary.
|
||||
var actionIDDependencies []*compileJob
|
||||
allFiles := map[string][]*loader.EmbedFile{}
|
||||
for _, files := range pkg.EmbedGlobals {
|
||||
for _, file := range files {
|
||||
allFiles[file.Name] = append(allFiles[file.Name], file)
|
||||
}
|
||||
// Create a cache key: a hash from the action ID below that contains all
|
||||
// the parameters for the build.
|
||||
actionID := packageAction{
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerBuildID: string(compilerBuildID),
|
||||
TinyGoVersion: goenv.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
OptLevel: optLevel,
|
||||
SizeLevel: sizeLevel,
|
||||
UndefinedGlobals: undefinedGlobals,
|
||||
}
|
||||
for name, files := range allFiles {
|
||||
name := name
|
||||
files := files
|
||||
job := &compileJob{
|
||||
description: "make object file for " + name,
|
||||
run: func(job *compileJob) error {
|
||||
// Read the file contents in memory.
|
||||
path := filepath.Join(pkg.Dir, name)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hash the file.
|
||||
sum := sha256.Sum256(data)
|
||||
hexSum := hex.EncodeToString(sum[:16])
|
||||
|
||||
for _, file := range files {
|
||||
file.Size = uint64(len(data))
|
||||
file.Hash = hexSum
|
||||
if file.NeedsData {
|
||||
file.Data = data
|
||||
}
|
||||
}
|
||||
|
||||
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), dir, compilerConfig)
|
||||
return err
|
||||
},
|
||||
}
|
||||
actionIDDependencies = append(actionIDDependencies, job)
|
||||
embedFileObjects = append(embedFileObjects, job)
|
||||
for filePath, hash := range pkg.FileHashes {
|
||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||
}
|
||||
|
||||
// Action ID jobs need to know the action ID of all the jobs the package
|
||||
// imports.
|
||||
var importedPackages []*compileJob
|
||||
for _, imported := range pkg.Pkg.Imports() {
|
||||
job, ok := packageActionIDJobs[imported.Path()]
|
||||
hash, ok := packageActionIDs[imported.Path()]
|
||||
if !ok {
|
||||
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
||||
}
|
||||
importedPackages = append(importedPackages, job)
|
||||
actionIDDependencies = append(actionIDDependencies, job)
|
||||
actionID.Imports[imported.Path()] = hash
|
||||
}
|
||||
|
||||
// Create a job that will calculate the action ID for a package compile
|
||||
// job. The action ID is the cache key that is used for caching this
|
||||
// package.
|
||||
packageActionIDJob := &compileJob{
|
||||
description: "calculate cache key for package " + pkg.ImportPath,
|
||||
dependencies: actionIDDependencies,
|
||||
run: func(job *compileJob) error {
|
||||
// Create a cache key: a hash from the action ID below that contains all
|
||||
// the parameters for the build.
|
||||
actionID := packageAction{
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerBuildID: string(compilerBuildID),
|
||||
TinyGoVersion: goenv.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
EmbeddedFiles: make(map[string]string, len(allFiles)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
OptLevel: optLevel,
|
||||
SizeLevel: sizeLevel,
|
||||
UndefinedGlobals: undefinedGlobals,
|
||||
}
|
||||
for filePath, hash := range pkg.FileHashes {
|
||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||
}
|
||||
for name, files := range allFiles {
|
||||
actionID.EmbeddedFiles[name] = files[0].Hash
|
||||
}
|
||||
for i, imported := range pkg.Pkg.Imports() {
|
||||
actionID.Imports[imported.Path()] = importedPackages[i].result
|
||||
}
|
||||
buf, err := json.Marshal(actionID)
|
||||
if err != nil {
|
||||
return err // shouldn't happen
|
||||
}
|
||||
hash := sha512.Sum512_224(buf)
|
||||
job.result = hex.EncodeToString(hash[:])
|
||||
return nil
|
||||
},
|
||||
buf, err := json.Marshal(actionID)
|
||||
if err != nil {
|
||||
panic(err) // shouldn't happen
|
||||
}
|
||||
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
|
||||
hash := sha512.Sum512_224(buf)
|
||||
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
|
||||
|
||||
// Now create the job to actually build the package. It will exit early
|
||||
// if the package is already compiled.
|
||||
// Determine the path of the bitcode file (which is a serialized version
|
||||
// of a LLVM module).
|
||||
cacheDir := goenv.Get("GOCACHE")
|
||||
if cacheDir == "off" {
|
||||
// Use temporary build directory instead, effectively disabling the
|
||||
// build cache.
|
||||
cacheDir = dir
|
||||
}
|
||||
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
|
||||
packageBitcodePaths[pkg.ImportPath] = bitcodePath
|
||||
|
||||
// The package has not yet been compiled, so create a job to do so.
|
||||
job := &compileJob{
|
||||
description: "compile package " + pkg.ImportPath,
|
||||
dependencies: []*compileJob{packageActionIDJob},
|
||||
run: func(job *compileJob) error {
|
||||
job.result = filepath.Join(cacheDir, "pkg-"+packageActionIDJob.result+".bc")
|
||||
description: "compile package " + pkg.ImportPath,
|
||||
run: func(*compileJob) error {
|
||||
// Acquire a lock (if supported).
|
||||
unlock := lock(job.result + ".lock")
|
||||
unlock := lock(bitcodePath + ".lock")
|
||||
defer unlock()
|
||||
|
||||
if _, err := os.Stat(job.result); err == nil {
|
||||
if _, err := os.Stat(bitcodePath); err == nil {
|
||||
// Already cached, don't recreate this package.
|
||||
return nil
|
||||
}
|
||||
@@ -352,8 +263,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Compile AST to IR. The compiler.CompilePackage function will
|
||||
// build the SSA as needed.
|
||||
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||
defer mod.Context().Dispose()
|
||||
defer mod.Dispose()
|
||||
if errs != nil {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
@@ -465,7 +374,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Write to a temporary path that is renamed to the destination
|
||||
// file to avoid race conditions with other TinyGo invocatiosn
|
||||
// that might also be compiling this package at the same time.
|
||||
f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result))
|
||||
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -485,13 +394,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||
// own, so create a somewhat useful error message here.
|
||||
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, job.result)
|
||||
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(f.Name(), job.result)
|
||||
return os.Rename(f.Name(), bitcodePath)
|
||||
},
|
||||
}
|
||||
packageJobs = append(packageJobs, job)
|
||||
@@ -499,13 +408,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
|
||||
// Add job that links and optimizes all packages together.
|
||||
var mod llvm.Module
|
||||
defer func() {
|
||||
if !mod.IsNil() {
|
||||
ctx := mod.Context()
|
||||
mod.Dispose()
|
||||
ctx.Dispose()
|
||||
}
|
||||
}()
|
||||
var stackSizeLoads []string
|
||||
programJob := &compileJob{
|
||||
description: "link+optimize packages (LTO)",
|
||||
@@ -514,9 +416,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Load and link all the bitcode files. This does not yet optimize
|
||||
// anything, it only links the bitcode files together.
|
||||
ctx := llvm.NewContext()
|
||||
mod = ctx.NewModule("main")
|
||||
for _, pkgJob := range packageJobs {
|
||||
pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result)
|
||||
mod = ctx.NewModule("")
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||
}
|
||||
@@ -608,17 +510,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer llvmBuf.Dispose()
|
||||
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
||||
case ".bc":
|
||||
var buf llvm.MemoryBuffer
|
||||
if config.UseThinLTO() {
|
||||
buf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
||||
} else {
|
||||
buf = llvm.WriteBitcodeToMemoryBuffer(mod)
|
||||
}
|
||||
defer buf.Dispose()
|
||||
return ioutil.WriteFile(outpath, buf.Bytes(), 0666)
|
||||
data := llvm.WriteBitcodeToMemoryBuffer(mod).Bytes()
|
||||
return ioutil.WriteFile(outpath, data, 0666)
|
||||
case ".ll":
|
||||
data := []byte(mod.String())
|
||||
return ioutil.WriteFile(outpath, data, 0666)
|
||||
@@ -638,17 +533,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
dependencies: []*compileJob{programJob},
|
||||
result: objfile,
|
||||
run: func(*compileJob) error {
|
||||
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
|
||||
}
|
||||
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer llvmBuf.Dispose()
|
||||
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
||||
},
|
||||
}
|
||||
@@ -681,7 +569,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
job := &compileJob{
|
||||
description: "compile extra file " + path,
|
||||
run: func(job *compileJob) error {
|
||||
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands)
|
||||
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.Options.PrintCommands)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -699,7 +587,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
job := &compileJob{
|
||||
description: "compile CGo file " + abspath,
|
||||
run: func(job *compileJob) error {
|
||||
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands)
|
||||
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.Options.PrintCommands)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -717,9 +605,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Add libc dependencies, if they exist.
|
||||
linkerDependencies = append(linkerDependencies, libcDependencies...)
|
||||
|
||||
// Add embedded files.
|
||||
linkerDependencies = append(linkerDependencies, embedFileObjects...)
|
||||
|
||||
// Strip debug information with -no-debug.
|
||||
if !config.Debug() {
|
||||
for _, tag := range config.BuildTags() {
|
||||
@@ -771,37 +656,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
||||
}
|
||||
if config.UseThinLTO() {
|
||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||
if config.GOOS() == "windows" {
|
||||
// Options for the MinGW wrapper for the lld COFF linker.
|
||||
ldflags = append(ldflags,
|
||||
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||
} else if config.GOOS() == "darwin" {
|
||||
// Options for the ld64-compatible lld linker.
|
||||
ldflags = append(ldflags,
|
||||
"--lto-O"+strconv.Itoa(optLevel),
|
||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||
} else {
|
||||
// Options for the ELF linker.
|
||||
ldflags = append(ldflags,
|
||||
"--lto-O"+strconv.Itoa(optLevel),
|
||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||
)
|
||||
}
|
||||
if config.CodeModel() != "default" {
|
||||
ldflags = append(ldflags,
|
||||
"-mllvm", "-code-model="+config.CodeModel())
|
||||
}
|
||||
if sizeLevel >= 2 {
|
||||
// Workaround with roughly the same effect as
|
||||
// https://reviews.llvm.org/D119342.
|
||||
// Can hopefully be removed in LLVM 15.
|
||||
ldflags = append(ldflags,
|
||||
"-mllvm", "--rotation-max-header-size=0")
|
||||
}
|
||||
}
|
||||
err = link(config.Target.Linker, ldflags...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to link", executable, err}
|
||||
@@ -932,7 +786,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "esp32", "esp32-img", "esp32c3", "esp8266":
|
||||
case "esp32", "esp32c3", "esp8266":
|
||||
// Special format for the ESP family of chips (parsed by the ROM
|
||||
// bootloader).
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
@@ -964,7 +818,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
}
|
||||
|
||||
return action(BuildResult{
|
||||
Executable: executable,
|
||||
Binary: tmppath,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
ModuleRoot: moduleroot,
|
||||
@@ -972,112 +825,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
})
|
||||
}
|
||||
|
||||
// createEmbedObjectFile creates a new object file with the given contents, for
|
||||
// the embed package.
|
||||
func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, compilerConfig *compiler.Config) (string, error) {
|
||||
// TODO: this works for small files, but can be a problem for larger files.
|
||||
// For larger files, it seems more appropriate to generate the object file
|
||||
// manually without going through LLVM.
|
||||
// On the other hand, generating DWARF like we do here can be difficult
|
||||
// without assistance from LLVM.
|
||||
|
||||
// Create new LLVM module just for this file.
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
mod := ctx.NewModule("data")
|
||||
defer mod.Dispose()
|
||||
|
||||
// Create data global.
|
||||
value := ctx.ConstString(data, false)
|
||||
globalName := "embed/file_" + hexSum
|
||||
global := llvm.AddGlobal(mod, value.Type(), globalName)
|
||||
global.SetInitializer(value)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetAlignment(1)
|
||||
if compilerConfig.GOOS != "darwin" {
|
||||
// MachO doesn't support COMDATs, while COFF requires it (to avoid
|
||||
// "duplicate symbol" errors). ELF works either way.
|
||||
// Therefore, only use a COMDAT on non-MachO systems (aka non-MacOS).
|
||||
global.SetComdat(mod.Comdat(globalName))
|
||||
}
|
||||
|
||||
// Add DWARF debug information to this global, so that it is
|
||||
// correctly counted when compiling with the -size= flag.
|
||||
dibuilder := llvm.NewDIBuilder(mod)
|
||||
dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||
File: sourceFile,
|
||||
Dir: sourceDir,
|
||||
Producer: "TinyGo",
|
||||
Optimized: false,
|
||||
})
|
||||
ditype := dibuilder.CreateArrayType(llvm.DIArrayType{
|
||||
SizeInBits: uint64(len(data)) * 8,
|
||||
AlignInBits: 8,
|
||||
ElementType: dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||
Name: "byte",
|
||||
SizeInBits: 8,
|
||||
Encoding: llvm.DW_ATE_unsigned_char,
|
||||
}),
|
||||
Subscripts: []llvm.DISubrange{
|
||||
{
|
||||
Lo: 0,
|
||||
Count: int64(len(data)),
|
||||
},
|
||||
},
|
||||
})
|
||||
difile := dibuilder.CreateFile(sourceFile, sourceDir)
|
||||
diglobalexpr := dibuilder.CreateGlobalVariableExpression(difile, llvm.DIGlobalVariableExpression{
|
||||
Name: globalName,
|
||||
File: difile,
|
||||
Line: 1,
|
||||
Type: ditype,
|
||||
Expr: dibuilder.CreateExpression(nil),
|
||||
AlignInBits: 8,
|
||||
})
|
||||
global.AddMetadata(0, diglobalexpr)
|
||||
mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
|
||||
ctx.MDString("Debug Info Version"),
|
||||
llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(),
|
||||
}),
|
||||
)
|
||||
mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
|
||||
ctx.MDString("Dwarf Version"),
|
||||
llvm.ConstInt(ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||
}),
|
||||
)
|
||||
dibuilder.Finalize()
|
||||
dibuilder.Destroy()
|
||||
|
||||
// Write this LLVM module out as an object file.
|
||||
machine, err := compiler.NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer machine.Dispose()
|
||||
outfile, err := os.CreateTemp(tmpdir, "embed-"+hexSum+"-*.o")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer outfile.Close()
|
||||
buf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer buf.Dispose()
|
||||
_, err = outfile.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return outfile.Name(), outfile.Close()
|
||||
}
|
||||
|
||||
// optimizeProgram runs a series of optimizations and transformations that are
|
||||
// needed to convert a program to its final form. Some transformations are not
|
||||
// optional and must be run as the compiler expects them to run.
|
||||
@@ -1099,7 +846,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
}
|
||||
}
|
||||
|
||||
if config.GOOS() != "darwin" && !config.UseThinLTO() {
|
||||
if config.GOOS() != "darwin" {
|
||||
transform.ApplyFunctionSections(mod) // -ffunction-sections
|
||||
}
|
||||
|
||||
@@ -1132,6 +879,17 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
return errors.New("verification failure after LLVM optimization passes")
|
||||
}
|
||||
|
||||
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
||||
// disabled) unless it is explicitly disabled with a function attribute.
|
||||
// This is a problem, as it tries to emit them and prints an error when it
|
||||
// can't with this feature disabled.
|
||||
// Because as of september 2020 tail calls are not yet widely supported,
|
||||
// they need to be disabled until they are widely supported (at which point
|
||||
// the +tail-call target feautre can be set).
|
||||
if strings.HasPrefix(config.Triple(), "wasm") {
|
||||
transform.DisableTailCalls(mod)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
"cortex-m0",
|
||||
"cortex-m0plus",
|
||||
"cortex-m3",
|
||||
"cortex-m33",
|
||||
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
|
||||
"cortex-m4",
|
||||
"cortex-m7",
|
||||
"esp32c3",
|
||||
|
||||
+3
-22
@@ -1,11 +1,7 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
// These are the GENERIC_SOURCES according to CMakeList.txt.
|
||||
@@ -40,6 +36,7 @@ var genericBuiltins = []string{
|
||||
"divdf3.c",
|
||||
"divdi3.c",
|
||||
"divmoddi4.c",
|
||||
"divmodsi4.c",
|
||||
"divsc3.c",
|
||||
"divsf3.c",
|
||||
"divsi3.c",
|
||||
@@ -75,7 +72,6 @@ var genericBuiltins = []string{
|
||||
"floatunsisf.c",
|
||||
"floatuntidf.c",
|
||||
"floatuntisf.c",
|
||||
"fp_mode.c",
|
||||
//"int_util.c",
|
||||
"lshrdi3.c",
|
||||
"lshrti3.c",
|
||||
@@ -126,6 +122,7 @@ var genericBuiltins = []string{
|
||||
"ucmpti2.c",
|
||||
"udivdi3.c",
|
||||
"udivmoddi4.c",
|
||||
"udivmodsi4.c",
|
||||
"udivmodti4.c",
|
||||
"udivsi3.c",
|
||||
"udivti3.c",
|
||||
@@ -152,14 +149,6 @@ var aeabiBuiltins = []string{
|
||||
"arm/aeabi_memset.S",
|
||||
"arm/aeabi_uidivmod.S",
|
||||
"arm/aeabi_uldivmod.S",
|
||||
|
||||
// These two are not technically EABI builtins but are used by them and only
|
||||
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
|
||||
// other architectures.
|
||||
// Most importantly, they have a different calling convention on AVR so
|
||||
// should not be used on AVR.
|
||||
"divmodsi4.c",
|
||||
"udivmodsi4.c",
|
||||
}
|
||||
|
||||
// CompilerRT is a library with symbols required by programs compiled with LLVM.
|
||||
@@ -172,15 +161,7 @@ var CompilerRT = Library{
|
||||
cflags: func(target, headerPath string) []string {
|
||||
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
|
||||
},
|
||||
sourceDir: func() string {
|
||||
llvmDir := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project/compiler-rt/lib/builtins")
|
||||
if _, err := os.Stat(llvmDir); err == nil {
|
||||
// Release build.
|
||||
return llvmDir
|
||||
}
|
||||
// Development build.
|
||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
||||
},
|
||||
sourceDir: "lib/compiler-rt/lib/builtins",
|
||||
librarySources: func(target string) []string {
|
||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
|
||||
+6
-14
@@ -56,7 +56,7 @@ import (
|
||||
// depfile but without invalidating its name. For this reason, the depfile is
|
||||
// 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.
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) {
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) {
|
||||
// Hash input file.
|
||||
fileHash, err := hashFile(abspath)
|
||||
if err != nil {
|
||||
@@ -67,11 +67,6 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
||||
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock"))
|
||||
defer unlock()
|
||||
|
||||
ext := ".o"
|
||||
if thinlto {
|
||||
ext = ".bc"
|
||||
}
|
||||
|
||||
// Create cache key for the dependencies file.
|
||||
buf, err := json.Marshal(struct {
|
||||
Path string
|
||||
@@ -104,7 +99,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
||||
}
|
||||
|
||||
// Obtain hashes of all the files listed as a dependency.
|
||||
outpath, err := makeCFileCachePath(dependencies, depfileNameHash, ext)
|
||||
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
|
||||
if err == nil {
|
||||
if _, err := os.Stat(outpath); err == nil {
|
||||
return outpath, nil
|
||||
@@ -117,7 +112,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
||||
return "", err
|
||||
}
|
||||
|
||||
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*"+ext)
|
||||
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*.o")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -129,9 +124,6 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
||||
depTmpFile.Close()
|
||||
flags := append([]string{}, cflags...) // copy cflags
|
||||
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)
|
||||
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
|
||||
// If this is an assembly file (.s or .S, lowercase or uppercase), then
|
||||
@@ -189,7 +181,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
||||
}
|
||||
|
||||
// Move temporary object file to final location.
|
||||
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash, ext)
|
||||
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -204,7 +196,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
||||
// 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
|
||||
// including compiler flags) and the hash of all input files in the paths slice.
|
||||
func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, error) {
|
||||
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
|
||||
// Hash all input files.
|
||||
fileHashes := make(map[string]string, len(paths))
|
||||
for _, path := range paths {
|
||||
@@ -229,7 +221,7 @@ func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, er
|
||||
outFileNameBuf := sha512.Sum512_224(buf)
|
||||
cacheKey := hex.EncodeToString(outFileNameBuf[:])
|
||||
|
||||
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+ext)
|
||||
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".o")
|
||||
return outpath, nil
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -38,7 +38,6 @@
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/MCTargetOptions.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Option/OptTable.h"
|
||||
@@ -52,6 +51,7 @@
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/Timer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@@ -120,6 +120,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||
.Case("none", llvm::DebugCompressionType::None)
|
||||
.Case("zlib", llvm::DebugCompressionType::Z)
|
||||
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
|
||||
.Default(llvm::DebugCompressionType::None);
|
||||
}
|
||||
|
||||
@@ -381,7 +382,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
|
||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
||||
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
||||
/*DWARFMustBeAtTheEnd*/ true));
|
||||
Str.get()->initSections(Opts.NoExecStack, *STI);
|
||||
Str.get()->InitSections(Opts.NoExecStack);
|
||||
}
|
||||
|
||||
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||
@@ -441,7 +442,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts,
|
||||
return Failed;
|
||||
}
|
||||
|
||||
static void LLVMErrorHandler(void *UserData, const char *Message,
|
||||
static void LLVMErrorHandler(void *UserData, const std::string &Message,
|
||||
bool GenCrashDiag) {
|
||||
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
|
||||
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ func LookupCommand(name string) (string, error) {
|
||||
}
|
||||
return cmdName, nil
|
||||
}
|
||||
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
|
||||
}
|
||||
|
||||
func execCommand(name string, args ...string) error {
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
if major != 1 || minor < 16 || minor > 18 {
|
||||
return nil, fmt.Errorf("requires go version 1.16 through 1.18, got go%d.%d", major, minor)
|
||||
if major != 1 || minor < 15 || minor > 17 {
|
||||
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
@@ -39,7 +39,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
|
||||
// Link object file to dynamic library.
|
||||
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
|
||||
flags = []string{
|
||||
"-flavor", "darwin",
|
||||
"-flavor", "darwinnew",
|
||||
"-demangle",
|
||||
"-dynamic",
|
||||
"-dylib",
|
||||
|
||||
+3
-30
@@ -15,7 +15,6 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type espImageSegment struct {
|
||||
@@ -79,31 +78,15 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
// An added benefit is that we don't need to check for errors all the time.
|
||||
outf := &bytes.Buffer{}
|
||||
|
||||
// Separate esp32 and esp32-img. The -img suffix indicates we should make an
|
||||
// image, not just a binary to be flashed at 0x1000 for example.
|
||||
chip := format
|
||||
makeImage := false
|
||||
if strings.HasSuffix(format, "-img") {
|
||||
makeImage = true
|
||||
chip = format[:len(format)-len("-img")]
|
||||
}
|
||||
|
||||
if makeImage {
|
||||
// The bootloader starts at 0x1000, or 4096.
|
||||
// TinyGo doesn't use a separate bootloader and runs the entire
|
||||
// application in the bootloader location.
|
||||
outf.Write(make([]byte, 4096))
|
||||
}
|
||||
|
||||
// Chip IDs. Source:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
||||
chip_id := map[string]uint16{
|
||||
"esp32": 0x0000,
|
||||
"esp32c3": 0x0005,
|
||||
}[chip]
|
||||
}[format]
|
||||
|
||||
// Image header.
|
||||
switch chip {
|
||||
switch format {
|
||||
case "esp32", "esp32c3":
|
||||
// Header format:
|
||||
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||
@@ -172,22 +155,12 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
||||
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||
outf.WriteByte(checksum)
|
||||
|
||||
if chip != "esp8266" {
|
||||
if format != "esp8266" {
|
||||
// SHA256 hash (to protect against image corruption, not for security).
|
||||
hash := sha256.Sum256(outf.Bytes())
|
||||
outf.Write(hash[:])
|
||||
}
|
||||
|
||||
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
|
||||
// image to be a certain size.
|
||||
if makeImage {
|
||||
// Use a default image size of 4MB.
|
||||
grow := 4096*1024 - outf.Len()
|
||||
if grow > 0 {
|
||||
outf.Write(make([]byte, grow))
|
||||
}
|
||||
}
|
||||
|
||||
// Write the image to the output file.
|
||||
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
||||
}
|
||||
|
||||
+5
-30
@@ -24,8 +24,8 @@ type Library struct {
|
||||
// cflags returns the C flags specific to this library
|
||||
cflags func(target, headerPath string) []string
|
||||
|
||||
// The source directory.
|
||||
sourceDir func() string
|
||||
// The source directory, relative to TINYGOROOT.
|
||||
sourceDir string
|
||||
|
||||
// The source files, relative to sourceDir.
|
||||
librarySources func(target string) []string
|
||||
@@ -103,10 +103,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = os.Chmod(temporaryHeaderPath, 0o755) // TempDir uses 0o700 by default
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = os.Rename(temporaryHeaderPath, headerPath)
|
||||
if err != nil {
|
||||
switch {
|
||||
@@ -148,21 +144,12 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// However, ARM has not done this.
|
||||
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
||||
args = append(args, "-march="+cpu)
|
||||
} else if strings.HasPrefix(target, "avr") {
|
||||
args = append(args, "-mmcu="+cpu)
|
||||
} else {
|
||||
args = append(args, "-mcpu="+cpu)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
}
|
||||
if strings.HasPrefix(target, "avr") {
|
||||
// AVR defaults to C float and double both being 32-bit. This deviates
|
||||
// from what most code (and certainly compiler-rt) expects. So we need
|
||||
// to force the compiler to use 64-bit floating point numbers for
|
||||
// double.
|
||||
args = append(args, "-mdouble=64")
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables")
|
||||
}
|
||||
if strings.HasPrefix(target, "riscv32-") {
|
||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||
@@ -170,12 +157,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
if strings.HasPrefix(target, "riscv64-") {
|
||||
args = append(args, "-march=rv64gc", "-mabi=lp64")
|
||||
}
|
||||
if strings.HasPrefix(target, "xtensa") {
|
||||
// Hack to work around an issue in the Xtensa port:
|
||||
// https://github.com/espressif/llvm-project/issues/52
|
||||
// Hopefully this will be fixed soon (LLVM 14).
|
||||
args = append(args, "-D__ELF__")
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
|
||||
@@ -201,17 +182,11 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chmod(f.Name(), 0o644) // TempFile uses 0o600 by default
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Store this archive in the cache.
|
||||
return os.Rename(f.Name(), archiveFilePath)
|
||||
},
|
||||
}
|
||||
|
||||
sourceDir := l.sourceDir()
|
||||
|
||||
// Create jobs to compile all sources. These jobs are depended upon by the
|
||||
// archive job above, so must be run first.
|
||||
for _, path := range l.librarySources(target) {
|
||||
@@ -220,7 +195,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
for strings.HasPrefix(cleanpath, "../") {
|
||||
cleanpath = cleanpath[3:]
|
||||
}
|
||||
srcpath := filepath.Join(sourceDir, path)
|
||||
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path)
|
||||
objpath := filepath.Join(dir, cleanpath+".o")
|
||||
os.MkdirAll(filepath.Dir(objpath), 0o777)
|
||||
objs = append(objs, objpath)
|
||||
@@ -244,7 +219,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// (It could be done in parallel with creating the ar file, but it probably
|
||||
// won't make much of a difference in speed).
|
||||
if l.crt1Source != "" {
|
||||
srcpath := filepath.Join(sourceDir, l.crt1Source)
|
||||
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source)
|
||||
job.dependencies = append(job.dependencies, &compileJob{
|
||||
description: "compile " + srcpath,
|
||||
run: func(*compileJob) error {
|
||||
|
||||
+4
-4
@@ -8,22 +8,22 @@ extern "C" {
|
||||
|
||||
bool tinygo_link_elf(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
|
||||
}
|
||||
|
||||
bool tinygo_link_macho(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
return lld::macho::link(args, false, llvm::outs(), llvm::errs());
|
||||
}
|
||||
|
||||
bool tinygo_link_mingw(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::mingw::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
|
||||
}
|
||||
|
||||
bool tinygo_link_wasm(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
return lld::wasm::link(args, llvm::outs(), llvm::errs(), false, false);
|
||||
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
|
||||
}
|
||||
|
||||
} // external "C"
|
||||
|
||||
@@ -26,7 +26,6 @@ var MinGW = Library{
|
||||
_, err = io.Copy(outf, inf)
|
||||
return err
|
||||
},
|
||||
sourceDir: func() string { return "" }, // unused
|
||||
cflags: func(target, headerPath string) []string {
|
||||
// No flags necessary because there are no files to compile.
|
||||
return nil
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ var Musl = Library{
|
||||
"-fno-stack-protector",
|
||||
}
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
||||
sourceDir: "lib/musl/src",
|
||||
librarySources: func(target string) []string {
|
||||
arch := compileopts.MuslArchitecture(target)
|
||||
globs := []string{
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ var Picolibc = Library{
|
||||
"-I" + headerPath,
|
||||
}
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc") },
|
||||
sourceDir: "lib/picolibc/newlib/libc",
|
||||
librarySources: func(target string) []string {
|
||||
return picolibcSources
|
||||
},
|
||||
|
||||
+1
-54
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
@@ -117,7 +116,7 @@ var (
|
||||
// alloc: heap allocations during init interpretation
|
||||
// pack: data created when storing a constant in an interface for example
|
||||
// string: buffer behind strings
|
||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
|
||||
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
|
||||
|
||||
// Reflect sidetables. Created by the reflect lowering pass.
|
||||
// See src/reflect/sidetables.go.
|
||||
@@ -369,58 +368,6 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if file, err := macho.NewFile(f); err == nil {
|
||||
// TODO: read DWARF information. On MacOS, DWARF debug information isn't
|
||||
// stored in the executable but stays in the object files. The
|
||||
// executable does however contain the object file paths that contain
|
||||
// debug information.
|
||||
|
||||
// Read segments, for use while reading through sections.
|
||||
segments := map[string]*macho.Segment{}
|
||||
for _, load := range file.Loads {
|
||||
switch load := load.(type) {
|
||||
case *macho.Segment:
|
||||
segments[load.Name] = load
|
||||
}
|
||||
}
|
||||
|
||||
// Read MachO sections.
|
||||
for _, section := range file.Sections {
|
||||
sectionType := section.Flags & 0xff
|
||||
sectionFlags := section.Flags >> 8
|
||||
segment := segments[section.Seg]
|
||||
// For the constants used here, see:
|
||||
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
|
||||
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
|
||||
// Section containing only instructions.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryCode,
|
||||
})
|
||||
} else if sectionType == 1 { // S_ZEROFILL
|
||||
// Section filled with zeroes on demand.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryBSS,
|
||||
})
|
||||
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
|
||||
// Protection doesn't allow writes, so mark this section read-only.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryROData,
|
||||
})
|
||||
} else {
|
||||
// The rest is assumed to be regular data.
|
||||
sections = append(sections, memorySection{
|
||||
Address: section.Addr,
|
||||
Size: uint64(section.Size),
|
||||
Type: memoryData,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if file, err := pe.NewFile(f); err == nil {
|
||||
// Read DWARF information. The error is intentionally ignored.
|
||||
data, _ := file.DWARF()
|
||||
|
||||
@@ -54,7 +54,7 @@ func RunTool(tool string, args ...string) error {
|
||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||
case "ld.lld":
|
||||
switch linker {
|
||||
case "darwin":
|
||||
case "darwinnew":
|
||||
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
|
||||
case "elf":
|
||||
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
||||
|
||||
+533
-367
File diff suppressed because it is too large
Load Diff
+200
-384
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
/*
|
||||
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
|
||||
#include <clang-c/Index.h> // if this fails, install libclang-11-dev
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -72,11 +72,7 @@ var diagnosticSeverity = [...]string{
|
||||
C.CXDiagnostic_Fatal: "fatal",
|
||||
}
|
||||
|
||||
// Alias so that cgo.go (which doesn't import Clang related stuff and is in
|
||||
// theory decoupled from Clang) can also use this type.
|
||||
type clangCursor = C.GoCXCursor
|
||||
|
||||
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
|
||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
|
||||
index := C.clang_createIndex(0, 0)
|
||||
defer C.clang_disposeIndex(index)
|
||||
|
||||
@@ -123,8 +119,8 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||
location := C.clang_getDiagnosticLocation(diagnostic)
|
||||
pos := f.getClangLocationPosition(location, unit)
|
||||
f.addError(pos, severity+": "+spelling)
|
||||
pos := p.getClangLocationPosition(location, unit)
|
||||
p.addError(pos, severity+": "+spelling)
|
||||
}
|
||||
for i := 0; i < numDiagnostics; i++ {
|
||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||
@@ -139,7 +135,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
}
|
||||
|
||||
// Extract information required by CGo.
|
||||
ref := storedRefs.Put(f)
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||
@@ -159,64 +155,35 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||
|
||||
// Hash the contents if it isn't hashed yet.
|
||||
if _, ok := f.visitedFiles[path]; !ok {
|
||||
if _, ok := p.visitedFiles[path]; !ok {
|
||||
// already stored
|
||||
sum := sha512.Sum512_224(data)
|
||||
f.visitedFiles[path] = sum[:]
|
||||
p.visitedFiles[path] = sum[:]
|
||||
}
|
||||
}
|
||||
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||
defer storedRefs.Remove(inclusionCallbackRef)
|
||||
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||
|
||||
// Do all the C AST operations inside a callback. This makes sure that
|
||||
// libclang related memory is only freed after it is not necessary anymore.
|
||||
callback(f.names)
|
||||
}
|
||||
|
||||
// Convert the AST node under the given Clang cursor to a Go AST node and return
|
||||
// it.
|
||||
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) {
|
||||
//export tinygo_clang_globals_visitor
|
||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||
kind := C.tinygo_clang_getCursorKind(c)
|
||||
pos := f.getCursorPosition(c)
|
||||
pos := p.getCursorPosition(c)
|
||||
switch kind {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: "C." + name,
|
||||
}
|
||||
args := make([]*ast.Field, numArgs)
|
||||
decl := &ast.FuncDecl{
|
||||
Doc: &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
{
|
||||
Slash: pos - 1,
|
||||
Text: "//export " + name,
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: pos,
|
||||
Params: &ast.FieldList{
|
||||
Opening: pos,
|
||||
List: args,
|
||||
Closing: pos,
|
||||
},
|
||||
},
|
||||
}
|
||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
|
||||
Slash: pos - 1,
|
||||
Text: "//go:variadic",
|
||||
})
|
||||
fn := &functionInfo{
|
||||
pos: pos,
|
||||
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
|
||||
}
|
||||
p.functions[name] = fn
|
||||
for i := 0; i < numArgs; i++ {
|
||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||
@@ -224,108 +191,50 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
|
||||
if argName == "" {
|
||||
argName = "$" + strconv.Itoa(i)
|
||||
}
|
||||
args[i] = &ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
NamePos: pos,
|
||||
Name: argName,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: argName,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: f.makeDecayingASTType(argType, pos),
|
||||
}
|
||||
fn.args = append(fn.args, paramInfo{
|
||||
name: argName,
|
||||
typeExpr: p.makeDecayingASTType(argType, pos),
|
||||
})
|
||||
}
|
||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||
if resultType.kind != C.CXType_Void {
|
||||
decl.Type.Results = &ast.FieldList{
|
||||
fn.results = &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
{
|
||||
Type: f.makeASTType(resultType, pos),
|
||||
Type: p.makeASTType(resultType, pos),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
obj.Decl = decl
|
||||
return decl, nil
|
||||
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
|
||||
typ := f.makeASTRecordType(c, pos)
|
||||
typeName := "C." + name
|
||||
typeExpr := typ.typeExpr
|
||||
if typ.unionSize != 0 {
|
||||
// Convert to a single-field struct type.
|
||||
typeExpr = f.makeUnionField(typ)
|
||||
case C.CXCursor_StructDecl:
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols["struct_"+name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: typ.pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, typ
|
||||
p.makeASTType(typ, pos)
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typeName := "C." + name
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
typedefType := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: f.makeASTType(underlyingType, pos),
|
||||
}
|
||||
if underlyingType.kind != C.CXType_Enum {
|
||||
typeSpec.Assign = pos
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
p.makeASTType(typedefType, pos)
|
||||
case C.CXCursor_VarDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
typeExpr := f.makeASTType(cursorType, pos)
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: pos,
|
||||
Tok: token.VAR,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
Doc: &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
{
|
||||
Slash: pos - 1,
|
||||
Text: "//go:extern " + name,
|
||||
},
|
||||
},
|
||||
},
|
||||
p.globals[name] = globalInfo{
|
||||
typeExpr: p.makeASTType(cursorType, pos),
|
||||
pos: pos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_MacroDefinition:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||
start := C.clang_getRangeStart(sourceRange)
|
||||
end := C.clang_getRangeEnd(sourceRange)
|
||||
@@ -333,17 +242,17 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
|
||||
var startOffset, endOffset C.unsigned
|
||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||
if file == nil {
|
||||
f.addError(pos, "internal error: could not find file where macro is defined")
|
||||
return nil, nil
|
||||
p.addError(pos, "internal error: could not find file where macro is defined")
|
||||
break
|
||||
}
|
||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||
if file != endFile {
|
||||
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||
return nil, nil
|
||||
p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
|
||||
break
|
||||
}
|
||||
if startOffset > endOffset {
|
||||
f.addError(pos, "internal error: start offset of macro is after end offset")
|
||||
return nil, nil
|
||||
p.addError(pos, "internal error: start offset of macro is after end offset")
|
||||
break
|
||||
}
|
||||
|
||||
// read file contents and extract the relevant byte range
|
||||
@@ -351,94 +260,31 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
|
||||
var size C.size_t
|
||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||
if endOffset >= C.uint(size) {
|
||||
f.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||
return nil, nil
|
||||
p.addError(pos, "internal error: end offset of macro lies after end of file")
|
||||
break
|
||||
}
|
||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||
if !strings.HasPrefix(source, name) {
|
||||
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||
return nil, nil
|
||||
p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
|
||||
break
|
||||
}
|
||||
value := source[len(name):]
|
||||
// Try to convert this #define into a Go constant expression.
|
||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
|
||||
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
|
||||
if scannerError != nil {
|
||||
f.errors = append(f.errors, *scannerError)
|
||||
return nil, nil
|
||||
p.errors = append(p.errors, *scannerError)
|
||||
}
|
||||
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.CONST,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
if expr != nil {
|
||||
// Parsing was successful.
|
||||
p.constants[name] = constantInfo{expr, pos}
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
case C.CXCursor_EnumDecl:
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "C." + name,
|
||||
}
|
||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
|
||||
// TODO: gc's CGo implementation uses types such as `uint32` for enums
|
||||
// instead of types such as C.int, which are used here.
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Assign: pos,
|
||||
Type: f.makeASTType(underlying, pos),
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
return typeSpec, nil
|
||||
case C.CXCursor_EnumConstantDecl:
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
expr := &ast.BasicLit{
|
||||
ValuePos: pos,
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(value), 10),
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.CONST,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{{
|
||||
NamePos: pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
return gen, nil
|
||||
default:
|
||||
f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind))
|
||||
return nil, nil
|
||||
// Visit all enums, because the fields may be used even when the enum
|
||||
// type itself is not.
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
p.makeASTType(typ, pos)
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
func getString(clangString C.CXString) (s string) {
|
||||
@@ -448,49 +294,6 @@ func getString(clangString C.CXString) (s string) {
|
||||
return
|
||||
}
|
||||
|
||||
//export tinygo_clang_globals_visitor
|
||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile)
|
||||
switch C.tinygo_clang_getCursorKind(c) {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_StructDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
f.names["struct_"+name] = c
|
||||
}
|
||||
case C.CXCursor_UnionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
f.names["union_"+name] = c
|
||||
}
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typedefType := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_VarDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_MacroDefinition:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
case C.CXCursor_EnumDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name != "" {
|
||||
// Named enum, which can be referenced from Go using C.enum_foo.
|
||||
f.names["enum_"+name] = c
|
||||
}
|
||||
// The enum fields are in global scope, so recurse to visit them.
|
||||
return C.CXChildVisit_Recurse
|
||||
case C.CXCursor_EnumConstantDecl:
|
||||
// We arrive here because of the "Recurse" above.
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
f.names[name] = c
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
// getCursorPosition returns a usable token.Pos from a libclang cursor.
|
||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
|
||||
@@ -588,7 +391,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
|
||||
// makeDecayingASTType does the same as makeASTType but takes care of decaying
|
||||
// types (arrays in function parameters, etc). It is otherwise identical to
|
||||
// makeASTType.
|
||||
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
// Strip typedefs, if any.
|
||||
underlyingType := typ
|
||||
if underlyingType.kind == C.CXType_Typedef {
|
||||
@@ -614,15 +417,15 @@ func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
pointeeType := C.clang_getElementType(underlyingType)
|
||||
return &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: f.makeASTType(pointeeType, pos),
|
||||
X: p.makeASTType(pointeeType, pos),
|
||||
}
|
||||
}
|
||||
return f.makeASTType(typ, pos)
|
||||
return p.makeASTType(typ, pos)
|
||||
}
|
||||
|
||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||
// it converts a libclang type to a type in the Go AST.
|
||||
func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
var typeName string
|
||||
switch typ.kind {
|
||||
case C.CXType_Char_S, C.CXType_Char_U:
|
||||
@@ -683,7 +486,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
return &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: f.makeASTType(pointeeType, pos),
|
||||
X: p.makeASTType(pointeeType, pos),
|
||||
}
|
||||
case C.CXType_ConstantArray:
|
||||
return &ast.ArrayType{
|
||||
@@ -693,7 +496,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||
},
|
||||
Elt: f.makeASTType(C.clang_getElementType(typ), pos),
|
||||
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
||||
}
|
||||
case C.CXType_FunctionProto:
|
||||
// Be compatible with gc, which uses the *[0]byte type for function
|
||||
@@ -714,21 +517,71 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
case C.CXType_Typedef:
|
||||
name := getString(C.clang_getTypedefName(typ))
|
||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
if _, ok := p.typedefs[name]; !ok {
|
||||
p.typedefs[name] = nil // don't recurse
|
||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
expr := p.makeASTType(underlyingType, pos)
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
expr := expr.(*ast.Ident)
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
switch expr.Name {
|
||||
case "C.char":
|
||||
if typeSize != 1 {
|
||||
// This happens for some very special purpose architectures
|
||||
// (DSPs etc.) that are not currently targeted.
|
||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||
}
|
||||
switch underlyingType.kind {
|
||||
case C.CXType_Char_S:
|
||||
expr.Name = "int8"
|
||||
case C.CXType_Char_U:
|
||||
expr.Name = "uint8"
|
||||
}
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "int8"
|
||||
case 2:
|
||||
expr.Name = "int16"
|
||||
case 4:
|
||||
expr.Name = "int32"
|
||||
case 8:
|
||||
expr.Name = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "uint8"
|
||||
case 2:
|
||||
expr.Name = "uint16"
|
||||
case 4:
|
||||
expr.Name = "uint32"
|
||||
case 8:
|
||||
expr.Name = "uint64"
|
||||
}
|
||||
}
|
||||
}
|
||||
p.typedefs[name] = &typedefInfo{
|
||||
typeExpr: expr,
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: f.getASTDeclName(name, c, false),
|
||||
Name: "C." + name,
|
||||
}
|
||||
case C.CXType_Elaborated:
|
||||
underlying := C.clang_Type_getNamedType(typ)
|
||||
switch underlying.kind {
|
||||
case C.CXType_Record:
|
||||
return f.makeASTType(underlying, pos)
|
||||
return p.makeASTType(underlying, pos)
|
||||
case C.CXType_Enum:
|
||||
return f.makeASTType(underlying, pos)
|
||||
return p.makeASTType(underlying, pos)
|
||||
default:
|
||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
|
||||
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||
p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
|
||||
typeName = "<unknown>"
|
||||
}
|
||||
case C.CXType_Record:
|
||||
@@ -746,46 +599,63 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
if name == "" {
|
||||
// Anonymous record, probably inside a typedef.
|
||||
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||
var file C.CXFile
|
||||
var line C.unsigned
|
||||
var column C.unsigned
|
||||
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
|
||||
location := token.Position{
|
||||
Filename: getString(C.clang_getFileName(file)),
|
||||
Line: int(line),
|
||||
Column: int(column),
|
||||
typeInfo := p.makeASTRecordType(cursor, pos)
|
||||
if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
|
||||
// This record is a union or is a struct with bitfields, so we
|
||||
// have to declare it as a named type (for getters/setters to
|
||||
// work).
|
||||
p.anonStructNum++
|
||||
cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
|
||||
p.elaboratedTypes[cgoName] = typeInfo
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
}
|
||||
if location.Filename == "" || location.Line == 0 {
|
||||
// Not sure when this would happen, but protect from it anyway.
|
||||
f.addError(pos, "could not find file/line information")
|
||||
}
|
||||
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
|
||||
return typeInfo.typeExpr
|
||||
} else {
|
||||
name = cgoRecordPrefix + name
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: f.getASTDeclName(name, cursor, false),
|
||||
cgoName := cgoRecordPrefix + name
|
||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||
p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
}
|
||||
case C.CXType_Enum:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
|
||||
if name == "" {
|
||||
name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
|
||||
// anonymous enum
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||
return p.makeASTType(underlying, pos)
|
||||
} else {
|
||||
name = "enum_" + name
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: f.getASTDeclName(name, cursor, false),
|
||||
// named enum
|
||||
if _, ok := p.enums[name]; !ok {
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||
p.enums[name] = enumInfo{
|
||||
typeExpr: p.makeASTType(underlying, pos),
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C.enum_" + name,
|
||||
}
|
||||
}
|
||||
}
|
||||
if typeName == "" {
|
||||
// Report this as an error.
|
||||
typeSpelling := getString(C.clang_getTypeSpelling(typ))
|
||||
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
|
||||
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||
p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
|
||||
typeName = "C.<unknown>"
|
||||
}
|
||||
return &ast.Ident{
|
||||
@@ -794,80 +664,9 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
}
|
||||
|
||||
// getIntegerType returns an AST node that defines types such as C.int.
|
||||
func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec {
|
||||
pos := p.getCursorPosition(cursor)
|
||||
|
||||
// Find a Go type that matches the size and signedness of the given C type.
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor)
|
||||
var goName string
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
switch name {
|
||||
case "C.char":
|
||||
if typeSize != 1 {
|
||||
// This happens for some very special purpose architectures
|
||||
// (DSPs etc.) that are not currently targeted.
|
||||
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
|
||||
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
|
||||
}
|
||||
switch underlyingType.kind {
|
||||
case C.CXType_Char_S:
|
||||
goName = "int8"
|
||||
case C.CXType_Char_U:
|
||||
goName = "uint8"
|
||||
}
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
goName = "int8"
|
||||
case 2:
|
||||
goName = "int16"
|
||||
case 4:
|
||||
goName = "int32"
|
||||
case 8:
|
||||
goName = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
goName = "uint8"
|
||||
case 2:
|
||||
goName = "uint16"
|
||||
case 4:
|
||||
goName = "uint32"
|
||||
case 8:
|
||||
goName = "uint64"
|
||||
}
|
||||
}
|
||||
|
||||
if goName == "" { // should not happen
|
||||
p.addError(pos, "internal error: did not find Go type for C type "+name)
|
||||
goName = "int"
|
||||
}
|
||||
|
||||
// Construct an *ast.TypeSpec for this type.
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: name,
|
||||
}
|
||||
spec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: goName,
|
||||
},
|
||||
}
|
||||
obj.Decl = spec
|
||||
return spec
|
||||
}
|
||||
|
||||
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||
// a Go struct type.
|
||||
func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||
fieldList := &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
@@ -877,11 +676,11 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
|
||||
bitfieldNum := 0
|
||||
ref := storedRefs.Put(struct {
|
||||
fieldList *ast.FieldList
|
||||
file *cgoFile
|
||||
pkg *cgoPackage
|
||||
inBitfield *bool
|
||||
bitfieldNum *int
|
||||
bitfieldList *[]bitfieldInfo
|
||||
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||
renameFieldKeywords(fieldList)
|
||||
@@ -910,13 +709,13 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
|
||||
}
|
||||
if bitfieldList != nil {
|
||||
// This is valid C... but please don't do this.
|
||||
f.addError(pos, "bitfield in a union is not supported")
|
||||
p.addError(pos, "bitfield in a union is not supported")
|
||||
}
|
||||
typ := C.tinygo_clang_getCursorType(cursor)
|
||||
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
||||
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
||||
if sizeInBytes == 0 {
|
||||
f.addError(pos, "zero-length union is not supported")
|
||||
p.addError(pos, "zero-length union is not supported")
|
||||
}
|
||||
typeInfo.unionSize = sizeInBytes
|
||||
typeInfo.unionAlign = alignInBytes
|
||||
@@ -924,7 +723,7 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
|
||||
default:
|
||||
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||
p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||
return &elaboratedTypeInfo{
|
||||
typeExpr: &ast.StructType{
|
||||
Struct: pos,
|
||||
@@ -938,17 +737,17 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
|
||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||
fieldList *ast.FieldList
|
||||
file *cgoFile
|
||||
pkg *cgoPackage
|
||||
inBitfield *bool
|
||||
bitfieldNum *int
|
||||
bitfieldList *[]bitfieldInfo
|
||||
})
|
||||
fieldList := passed.fieldList
|
||||
f := passed.file
|
||||
p := passed.pkg
|
||||
inBitfield := passed.inBitfield
|
||||
bitfieldNum := passed.bitfieldNum
|
||||
bitfieldList := passed.bitfieldList
|
||||
pos := f.getCursorPosition(c)
|
||||
pos := p.getCursorPosition(c)
|
||||
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
|
||||
case C.CXCursor_FieldDecl:
|
||||
// Expected. This is a regular field.
|
||||
@@ -957,7 +756,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
return C.CXChildVisit_Continue
|
||||
default:
|
||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||
p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
@@ -968,14 +767,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
}
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
field := &ast.Field{
|
||||
Type: f.makeASTType(typ, f.getCursorPosition(c)),
|
||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
||||
}
|
||||
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
||||
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
||||
bitfieldOffset := offsetof % alignOf
|
||||
if bitfieldOffset != 0 {
|
||||
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
||||
f.addError(pos, "expected a bitfield")
|
||||
p.addError(pos, "expected a bitfield")
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
if !*inBitfield {
|
||||
@@ -1022,6 +821,23 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
//export tinygo_clang_enum_visitor
|
||||
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
pos := p.getCursorPosition(c)
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
p.constants[name] = constantInfo{
|
||||
expr: &ast.BasicLit{
|
||||
ValuePos: pos,
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(value), 10),
|
||||
},
|
||||
pos: pos,
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
//export tinygo_clang_inclusion_visitor
|
||||
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//go:build !byollvm && llvm11
|
||||
// +build !byollvm,llvm11
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
||||
#cgo darwin amd64 CFLAGS: -I/usr/local/opt/llvm@11/include
|
||||
#cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/llvm@11/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
||||
#cgo darwin amd64 LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -0,0 +1,16 @@
|
||||
//go:build !byollvm && llvm12
|
||||
// +build !byollvm,llvm12
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-12/include
|
||||
#cgo darwin amd64 CFLAGS: -I/usr/local/opt/llvm@12/include
|
||||
#cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/llvm@12/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm12/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang
|
||||
#cgo darwin amd64 LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi
|
||||
#cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@12/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -1,16 +1,16 @@
|
||||
//go:build !byollvm && !llvm14
|
||||
// +build !byollvm,!llvm14
|
||||
//go:build !byollvm && !llvm11 && !llvm12
|
||||
// +build !byollvm,!llvm11,!llvm12
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-13/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@13/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@13/include
|
||||
#cgo darwin amd64 CFLAGS: -I/usr/local/opt/llvm@13/include
|
||||
#cgo darwin arm64 CFLAGS: -I/opt/homebrew/opt/llvm@13/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm13/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-13/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@13/lib -lclang -lffi
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@13/lib -lclang -lffi
|
||||
#cgo darwin amd64 LDFLAGS: -L/usr/local/opt/llvm@13/lib -lclang -lffi
|
||||
#cgo darwin arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@13/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm13/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
//go:build !byollvm && llvm14
|
||||
// +build !byollvm,llvm14
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-14/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm14/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -3,7 +3,7 @@
|
||||
// are slightly different from the ones defined in libclang.go, but they
|
||||
// should be ABI compatible.
|
||||
|
||||
#include <clang-c/Index.h> // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/
|
||||
#include <clang-c/Index.h> // if this fails, install libclang-11-dev
|
||||
|
||||
CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) {
|
||||
return clang_getTranslationUnitCursor(tu);
|
||||
|
||||
Vendored
+20
-13
@@ -24,16 +24,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+22
-15
@@ -24,19 +24,26 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
const C.foo = 3
|
||||
const C.bar = C.foo
|
||||
const C.foo = 3
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ import "C"
|
||||
//line errors.go:100
|
||||
var (
|
||||
// constant too large
|
||||
_ C.char = 2 << 10
|
||||
_ C.uint8_t = 2 << 10
|
||||
|
||||
// z member does not exist
|
||||
_ C.point_t = C.point_t{z: 3}
|
||||
|
||||
Vendored
+24
-18
@@ -6,7 +6,7 @@
|
||||
// testdata/errors.go:19:26: unexpected token ), expected end of expression
|
||||
|
||||
// Type checking errors after CGo processing:
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
|
||||
// testdata/errors.go:105: unknown field z in struct literal
|
||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||
@@ -38,23 +38,29 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
type C._Ctype_struct___0 struct {
|
||||
const C.SOME_CONST_3 = 1234
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.point_t = struct {
|
||||
x C.int
|
||||
y C.int
|
||||
}
|
||||
type C.point_t = C._Ctype_struct___0
|
||||
|
||||
const C.SOME_CONST_3 = 1234
|
||||
|
||||
Vendored
+21
-14
@@ -29,19 +29,26 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
const C.BAR = 3
|
||||
const C.FOO_H = 1
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+23
-18
@@ -24,36 +24,41 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
|
||||
//export foo
|
||||
func C.foo(a C.int, b C.int) C.int
|
||||
|
||||
var C.foo$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic0
|
||||
//go:variadic
|
||||
func C.variadic0()
|
||||
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
|
||||
//export variadic2
|
||||
//go:variadic
|
||||
func C.variadic2(x C.int, y C.int)
|
||||
|
||||
var C.foo$funcaddr unsafe.Pointer
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
var C.variadic2$funcaddr unsafe.Pointer
|
||||
|
||||
//go:extern someValue
|
||||
var C.someValue C.int
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
|
||||
Vendored
+106
-100
@@ -24,126 +24,132 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
return C.__GoBytes(ptr, uintptr(length))
|
||||
}
|
||||
|
||||
type (
|
||||
C.char uint8
|
||||
C.schar int8
|
||||
C.uchar uint8
|
||||
C.short int16
|
||||
C.ushort uint16
|
||||
C.int int32
|
||||
C.uint uint32
|
||||
C.long int32
|
||||
C.ulong uint32
|
||||
C.longlong int64
|
||||
C.ulonglong uint64
|
||||
)
|
||||
const C.option2A = 20
|
||||
const C.optionA = 0
|
||||
const C.optionB = 1
|
||||
const C.optionC = -5
|
||||
const C.optionD = -4
|
||||
const C.optionE = 10
|
||||
const C.optionF = 11
|
||||
const C.optionG = 12
|
||||
const C.unused1 = 5
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.bitfield_t = C.struct_4
|
||||
type C.myIntArray = [10]C.int
|
||||
type C.myint = C.int
|
||||
type C._Ctype_struct___0 struct {
|
||||
type C.option2_t = C.uint
|
||||
type C.option_t = C.enum_option
|
||||
type C.point2d_t = struct {
|
||||
x C.int
|
||||
y C.int
|
||||
}
|
||||
type C.point2d_t = C._Ctype_struct___0
|
||||
type C.struct_point3d struct {
|
||||
x C.int
|
||||
y C.int
|
||||
z C.int
|
||||
}
|
||||
type C.point3d_t = C.struct_point3d
|
||||
type C.struct_type1 struct {
|
||||
_type C.int
|
||||
__type C.int
|
||||
___type C.int
|
||||
}
|
||||
type C.struct_type2 struct{ _type C.int }
|
||||
type C._Ctype_union___1 struct{ i C.int }
|
||||
type C.union1_t = C._Ctype_union___1
|
||||
type C._Ctype_union___2 struct{ $union uint64 }
|
||||
|
||||
func (union *C._Ctype_union___2) unionfield_i() *C.int {
|
||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___2) unionfield_d() *float64 {
|
||||
return (*float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___2) unionfield_s() *C.short {
|
||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union3_t = C._Ctype_union___2
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C._Ctype_union___3 struct{ arr [10]C.uchar }
|
||||
type C.unionarray_t = C._Ctype_union___3
|
||||
type C._Ctype_union___5 struct{ $union [3]uint32 }
|
||||
|
||||
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
|
||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C._Ctype_struct___4 struct {
|
||||
type C.struct_nested_t = struct {
|
||||
begin C.point2d_t
|
||||
end C.point2d_t
|
||||
tag C.int
|
||||
|
||||
coord C._Ctype_union___5
|
||||
coord C.union_2
|
||||
}
|
||||
type C.struct_nested_t = C._Ctype_struct___4
|
||||
type C._Ctype_union___6 struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
|
||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
|
||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_nested_t = C._Ctype_union___6
|
||||
type C.enum_option = C.int
|
||||
type C.option_t = C.enum_option
|
||||
type C._Ctype_enum___7 = C.uint
|
||||
type C.option2_t = C._Ctype_enum___7
|
||||
type C._Ctype_struct___8 struct {
|
||||
type C.types_t = struct {
|
||||
f float32
|
||||
d float64
|
||||
ptr *C.int
|
||||
}
|
||||
type C.types_t = C._Ctype_struct___8
|
||||
type C.myIntArray = [10]C.int
|
||||
type C._Ctype_struct___9 struct {
|
||||
type C.union1_t = struct{ i C.int }
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C.union3_t = C.union_1
|
||||
type C.union_nested_t = C.union_3
|
||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
||||
|
||||
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C.struct_4) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C.struct_4) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C.struct_4) set_bitfield_c(value C.uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.struct_4 struct {
|
||||
start C.uchar
|
||||
__bitfield_1 C.uchar
|
||||
|
||||
d C.uchar
|
||||
e C.uchar
|
||||
}
|
||||
type C.struct_point3d struct {
|
||||
x C.int
|
||||
y C.int
|
||||
z C.int
|
||||
}
|
||||
type C.struct_type1 struct {
|
||||
_type C.int
|
||||
__type C.int
|
||||
___type C.int
|
||||
}
|
||||
type C.struct_type2 struct{ _type C.int }
|
||||
|
||||
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||
}
|
||||
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
|
||||
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
|
||||
}
|
||||
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
|
||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
type C.union_1 struct{ $union uint64 }
|
||||
|
||||
type C.bitfield_t = C._Ctype_struct___9
|
||||
func (union *C.union_2) unionfield_area() *C.point2d_t {
|
||||
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_2) unionfield_solid() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_2 struct{ $union [3]uint32 }
|
||||
|
||||
func (union *C.union_3) unionfield_point() *C.point3d_t {
|
||||
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_3) unionfield_array() *C.unionarray_t {
|
||||
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
func (union *C.union_3) unionfield_thing() *C.union3_t {
|
||||
return (*C.union3_t)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_3 struct{ $union [2]uint64 }
|
||||
|
||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||
}
|
||||
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
type C.enum_option C.int
|
||||
type C.enum_unused C.uint
|
||||
|
||||
+5
-86
@@ -10,7 +10,6 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
@@ -177,25 +176,6 @@ func (c *Config) AutomaticStackSize() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// UseThinLTO returns whether ThinLTO should be used for the given target. Some
|
||||
// targets (such as wasm) are not yet supported.
|
||||
// We should try and remove as many exceptions as possible in the future, so
|
||||
// that this optimization can be applied in more places.
|
||||
func (c *Config) UseThinLTO() bool {
|
||||
parts := strings.Split(c.Triple(), "-")
|
||||
if parts[0] == "wasm32" {
|
||||
// wasm-ld doesn't seem to support ThinLTO yet.
|
||||
return false
|
||||
}
|
||||
if parts[0] == "avr" || parts[0] == "xtensa" {
|
||||
// These use external (GNU) linkers which might perhaps support ThinLTO
|
||||
// through a plugin, but it's too much hassle to set up.
|
||||
return false
|
||||
}
|
||||
// Other architectures support ThinLTO.
|
||||
return true
|
||||
}
|
||||
|
||||
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
|
||||
// calculates and patches in the checksum for the 2nd stage bootloader.
|
||||
func (c *Config) RP2040BootPatch() bool {
|
||||
@@ -237,29 +217,6 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
||||
return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname), false
|
||||
}
|
||||
|
||||
// DefaultBinaryExtension returns the default extension for binaries, such as
|
||||
// .exe, .wasm, or no extension (depending on the target).
|
||||
func (c *Config) DefaultBinaryExtension() string {
|
||||
parts := strings.Split(c.Triple(), "-")
|
||||
if parts[0] == "wasm32" {
|
||||
// WebAssembly files always have the .wasm file extension.
|
||||
return ".wasm"
|
||||
}
|
||||
if len(parts) >= 3 && parts[2] == "windows" {
|
||||
// Windows uses .exe.
|
||||
return ".exe"
|
||||
}
|
||||
if len(parts) >= 3 && parts[2] == "unknown" {
|
||||
// There appears to be a convention to use the .elf file extension for
|
||||
// ELF files intended for microcontrollers. I'm not aware of the origin
|
||||
// of this, it's just something that is used by many projects.
|
||||
// I think it's a good tradition, so let's keep it.
|
||||
return ".elf"
|
||||
}
|
||||
// Linux, MacOS, etc, don't use a file extension. Use it as a fallback.
|
||||
return ""
|
||||
}
|
||||
|
||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||
// preprocessing.
|
||||
func (c *Config) CFlags() []string {
|
||||
@@ -279,7 +236,6 @@ func (c *Config) CFlags() []string {
|
||||
path, _ := c.LibcPath("picolibc")
|
||||
cflags = append(cflags,
|
||||
"--sysroot="+path,
|
||||
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
|
||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||
)
|
||||
@@ -386,13 +342,6 @@ func (c *Config) BinaryFormat(ext string) string {
|
||||
return c.Target.BinaryFormat
|
||||
}
|
||||
return "bin"
|
||||
case ".img":
|
||||
// Image file. Only defined for the ESP32 at the moment, where it is a
|
||||
// full (runnable) image that can be used in the Espressif QEMU fork.
|
||||
if c.Target.BinaryFormat != "" {
|
||||
return c.Target.BinaryFormat + "-img"
|
||||
}
|
||||
return "bin"
|
||||
case ".hex":
|
||||
// Similar to bin, but includes the start address and is thus usually a
|
||||
// better format.
|
||||
@@ -493,43 +442,13 @@ func (c *Config) WasmAbi() string {
|
||||
return c.Target.WasmAbi
|
||||
}
|
||||
|
||||
// EmulatorName is a shorthand to get the command for this emulator, something
|
||||
// like qemu-system-arm or simavr.
|
||||
func (c *Config) EmulatorName() string {
|
||||
parts := strings.SplitN(c.Target.Emulator, " ", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// EmulatorFormat returns the binary format for the emulator and the associated
|
||||
// file extension. An empty string means to pass directly whatever the linker
|
||||
// produces directly without conversion (usually ELF format).
|
||||
func (c *Config) EmulatorFormat() (format, fileExt string) {
|
||||
switch {
|
||||
case strings.Contains(c.Target.Emulator, "{img}"):
|
||||
return "img", ".img"
|
||||
default:
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
|
||||
// Emulator returns a ready-to-run command to run the given binary in an
|
||||
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
|
||||
// the compiled binary.
|
||||
func (c *Config) Emulator(format, binary string) ([]string, error) {
|
||||
parts, err := shlex.Split(c.Target.Emulator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse emulator command: %w", err)
|
||||
}
|
||||
// Emulator returns the emulator target config
|
||||
func (c *Config) Emulator() []string {
|
||||
var emulator []string
|
||||
for _, s := range parts {
|
||||
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
|
||||
s = strings.ReplaceAll(s, "{"+format+"}", binary)
|
||||
emulator = append(emulator, s)
|
||||
for _, s := range c.Target.Emulator {
|
||||
emulator = append(emulator, strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")))
|
||||
}
|
||||
return emulator, nil
|
||||
return emulator
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
|
||||
@@ -32,8 +32,8 @@ type Options struct {
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||
PrintCommands func(cmd string, args ...string)
|
||||
Semaphore chan struct{} // -p flag controls cap
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
@@ -46,7 +46,6 @@ type Options struct {
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
Directory string
|
||||
PrintJSON bool
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
|
||||
+30
-13
@@ -12,9 +12,11 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Target specification for a given target. Used for bare metal targets.
|
||||
@@ -42,8 +44,8 @@ type TargetSpec struct {
|
||||
LDFlags []string `json:"ldflags"`
|
||||
LinkerScript string `json:"linkerscript"`
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
||||
Emulator string `json:"emulator"`
|
||||
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
|
||||
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
|
||||
FlashCommand string `json:"flash-command"`
|
||||
GDB []string `json:"gdb"`
|
||||
PortReset string `json:"flash-1200-bps-reset"`
|
||||
@@ -57,7 +59,6 @@ type TargetSpec struct {
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
OpenOCDCommands []string `json:"openocd-commands"`
|
||||
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
|
||||
JLinkDevice string `json:"jlink-device"`
|
||||
CodeModel string `json:"code-model"`
|
||||
RelocationModel string `json:"relocation-model"`
|
||||
@@ -88,8 +89,19 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
||||
if !src.IsNil() {
|
||||
dst.Set(src)
|
||||
}
|
||||
case reflect.Slice: // for slices, append the field
|
||||
dst.Set(reflect.AppendSlice(dst, src))
|
||||
case reflect.Slice: // for slices...
|
||||
if src.Len() > 0 { // ... if not empty ...
|
||||
switch tag := field.Tag.Get("override"); tag {
|
||||
case "copy":
|
||||
// copy the field of child to spec
|
||||
dst.Set(src)
|
||||
case "append", "":
|
||||
// or append the field of child to spec
|
||||
dst.Set(reflect.AppendSlice(dst, src))
|
||||
default:
|
||||
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic("unknown field type : " + kind.String())
|
||||
}
|
||||
@@ -246,7 +258,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "arm":
|
||||
spec.CPU = "generic"
|
||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
||||
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables")
|
||||
switch strings.Split(triple, "-")[0] {
|
||||
case "armv5":
|
||||
spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
@@ -265,7 +277,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
arch := strings.Split(triple, "-")[0]
|
||||
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"-flavor", "darwin",
|
||||
"-flavor", "darwinnew",
|
||||
"-dead_strip",
|
||||
"-arch", arch,
|
||||
"-platform_version", "macos", platformVersion, platformVersion,
|
||||
@@ -290,8 +302,13 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
"--image-base", "0x400000",
|
||||
"--gc-sections",
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase",
|
||||
)
|
||||
llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||
if llvmMajor >= 12 {
|
||||
// This flag was added in LLVM 12. At the same time, LLVM 12
|
||||
// switched the default from --dynamicbase to --no-dynamicbase.
|
||||
spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase")
|
||||
}
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
@@ -313,20 +330,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
case "386":
|
||||
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
|
||||
if runtime.GOARCH != "amd64" {
|
||||
spec.Emulator = "qemu-i386 {}"
|
||||
spec.Emulator = []string{"qemu-i386"}
|
||||
}
|
||||
case "amd64":
|
||||
spec.Emulator = "qemu-x86_64 {}"
|
||||
spec.Emulator = []string{"qemu-x86_64"}
|
||||
case "arm":
|
||||
spec.Emulator = "qemu-arm {}"
|
||||
spec.Emulator = []string{"qemu-arm"}
|
||||
case "arm64":
|
||||
spec.Emulator = "qemu-aarch64 {}"
|
||||
spec.Emulator = []string{"qemu-aarch64"}
|
||||
}
|
||||
}
|
||||
}
|
||||
if goos != runtime.GOOS {
|
||||
if goos == "windows" {
|
||||
spec.Emulator = "wine {}"
|
||||
spec.Emulator = []string{"wine"}
|
||||
}
|
||||
}
|
||||
return &spec, nil
|
||||
|
||||
@@ -29,6 +29,7 @@ func TestOverrideProperties(t *testing.T) {
|
||||
CPU: "baseCpu",
|
||||
CFlags: []string{"-base-foo", "-base-bar"},
|
||||
BuildTags: []string{"bt1", "bt2"},
|
||||
Emulator: []string{"be1", "be2"},
|
||||
DefaultStackSize: 42,
|
||||
AutoStackSize: &baseAutoStackSize,
|
||||
}
|
||||
@@ -37,6 +38,7 @@ func TestOverrideProperties(t *testing.T) {
|
||||
GOOS: "",
|
||||
CPU: "chlidCpu",
|
||||
CFlags: []string{"-child-foo", "-child-bar"},
|
||||
Emulator: []string{"ce1", "ce2"},
|
||||
AutoStackSize: &childAutoStackSize,
|
||||
DefaultStackSize: 64,
|
||||
}
|
||||
@@ -55,6 +57,9 @@ func TestOverrideProperties(t *testing.T) {
|
||||
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.BuildTags)
|
||||
}
|
||||
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.Emulator)
|
||||
}
|
||||
if *base.AutoStackSize != false {
|
||||
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
|
||||
}
|
||||
|
||||
@@ -52,6 +52,30 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
ptr := b.getValue(call.Args[0])
|
||||
old := b.getValue(call.Args[1])
|
||||
newVal := b.getValue(call.Args[2])
|
||||
if strings.HasSuffix(name, "64") {
|
||||
if strings.HasPrefix(b.Triple, "thumb") {
|
||||
// Work around a bug in LLVM, at least LLVM 11:
|
||||
// https://reviews.llvm.org/D95891
|
||||
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
|
||||
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
||||
if compareAndSwap.IsNil() {
|
||||
// Declare the function if it isn't already declared.
|
||||
i64Type := b.ctx.Int64Type()
|
||||
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
|
||||
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
|
||||
}
|
||||
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
|
||||
// The __sync_val_compare_and_swap_8 function returns the old
|
||||
// value. However, we shouldn't return the old value, we should
|
||||
// return whether the compare/exchange was successful. This is
|
||||
// easily done by comparing the returned (actual) old value with
|
||||
// the expected old value passed to
|
||||
// __sync_val_compare_and_swap_8.
|
||||
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
|
||||
return swapped, true
|
||||
}
|
||||
}
|
||||
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
swapped := b.CreateExtractValue(tuple, 1, "")
|
||||
return swapped, true
|
||||
|
||||
+43
-209
@@ -9,7 +9,6 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/bits"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -77,7 +76,6 @@ type compilerContext struct {
|
||||
program *ssa.Program
|
||||
diagnostics []error
|
||||
astComments map[string]*ast.CommentGroup
|
||||
embedGlobals map[string][]*loader.EmbedFile
|
||||
pkg *types.Package
|
||||
packageDir string // directory for this package
|
||||
runtimePkg *types.Package
|
||||
@@ -236,15 +234,10 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
||||
panic("unknown pointer size")
|
||||
}
|
||||
|
||||
// Construct a complex128 type because that's likely the type with the
|
||||
// biggest alignment on most/all ABIs.
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
complex128Type := ctx.StructType([]llvm.Type{ctx.DoubleType(), ctx.DoubleType()}, false)
|
||||
return &stdSizes{
|
||||
IntSize: int64(intWidth / 8),
|
||||
PtrSize: int64(targetData.PointerSize()),
|
||||
MaxAlign: int64(targetData.ABITypeAlignment(complex128Type)),
|
||||
MaxAlign: int64(targetData.PrefTypeAlignment(targetData.IntPtrType())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +245,6 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
||||
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||
c.packageDir = pkg.OriginalDir()
|
||||
c.embedGlobals = pkg.EmbedGlobals
|
||||
c.pkg = pkg.Pkg
|
||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||
c.program = ssaPkg.Prog
|
||||
@@ -304,7 +296,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
}),
|
||||
)
|
||||
c.dibuilder.Finalize()
|
||||
c.dibuilder.Destroy()
|
||||
}
|
||||
|
||||
return c.mod, c.diagnostics
|
||||
@@ -818,9 +809,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
// Global variable.
|
||||
info := c.getGlobalInfo(member)
|
||||
global := c.getGlobal(member)
|
||||
if files, ok := c.embedGlobals[member.Name()]; ok {
|
||||
c.createEmbedGlobal(member, global, files)
|
||||
} else if !info.extern {
|
||||
if !info.extern {
|
||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
if info.section != "" {
|
||||
@@ -854,150 +843,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
}
|
||||
}
|
||||
|
||||
// createEmbedGlobal creates an initializer for a //go:embed global variable.
|
||||
func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) {
|
||||
switch typ := member.Type().(*types.Pointer).Elem().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
// String type.
|
||||
if typ.Kind() != types.String {
|
||||
// This is checked at the AST level, so should be unreachable.
|
||||
panic("expected a string type")
|
||||
}
|
||||
if len(files) != 1 {
|
||||
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
|
||||
return
|
||||
}
|
||||
strObj := c.getEmbedFileString(files[0])
|
||||
global.SetInitializer(strObj)
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
|
||||
case *types.Slice:
|
||||
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
|
||||
// This is checked at the AST level, so should be unreachable.
|
||||
panic("expected a byte slice")
|
||||
}
|
||||
if len(files) != 1 {
|
||||
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
|
||||
return
|
||||
}
|
||||
file := files[0]
|
||||
bufferValue := c.ctx.ConstString(string(file.Data), false)
|
||||
bufferGlobal := llvm.AddGlobal(c.mod, bufferValue.Type(), c.pkg.Path()+"$embedslice")
|
||||
bufferGlobal.SetInitializer(bufferValue)
|
||||
bufferGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
bufferGlobal.SetAlignment(1)
|
||||
slicePtr := llvm.ConstInBoundsGEP(bufferGlobal, []llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
})
|
||||
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||
global.SetInitializer(sliceObj)
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
|
||||
case *types.Struct:
|
||||
// Assume this is an embed.FS struct:
|
||||
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/embed/embed.go;l=148
|
||||
// It looks like this:
|
||||
// type FS struct {
|
||||
// files *file
|
||||
// }
|
||||
|
||||
// Make a slice of the files, as they will appear in the binary. They
|
||||
// are sorted in a special way to allow for binary searches, see
|
||||
// src/embed/embed.go for details.
|
||||
dirset := map[string]struct{}{}
|
||||
var allFiles []*loader.EmbedFile
|
||||
for _, file := range files {
|
||||
allFiles = append(allFiles, file)
|
||||
dirname := file.Name
|
||||
for {
|
||||
dirname, _ = path.Split(path.Clean(dirname))
|
||||
if dirname == "" {
|
||||
break
|
||||
}
|
||||
if _, ok := dirset[dirname]; ok {
|
||||
break
|
||||
}
|
||||
dirset[dirname] = struct{}{}
|
||||
allFiles = append(allFiles, &loader.EmbedFile{
|
||||
Name: dirname,
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.Slice(allFiles, func(i, j int) bool {
|
||||
dir1, name1 := path.Split(path.Clean(allFiles[i].Name))
|
||||
dir2, name2 := path.Split(path.Clean(allFiles[j].Name))
|
||||
if dir1 != dir2 {
|
||||
return dir1 < dir2
|
||||
}
|
||||
return name1 < name2
|
||||
})
|
||||
|
||||
// Make the backing array for the []files slice. This is a LLVM global.
|
||||
embedFileStructType := c.getLLVMType(typ.Field(0).Type().(*types.Pointer).Elem().(*types.Slice).Elem())
|
||||
var fileStructs []llvm.Value
|
||||
for _, file := range allFiles {
|
||||
fileStruct := llvm.ConstNull(embedFileStructType)
|
||||
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
|
||||
fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
|
||||
if file.Hash != "" {
|
||||
data := c.getEmbedFileString(file)
|
||||
fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
|
||||
}
|
||||
fileStructs = append(fileStructs, fileStruct)
|
||||
}
|
||||
sliceDataInitializer := llvm.ConstArray(embedFileStructType, fileStructs)
|
||||
sliceDataGlobal := llvm.AddGlobal(c.mod, sliceDataInitializer.Type(), c.pkg.Path()+"$embedfsfiles")
|
||||
sliceDataGlobal.SetInitializer(sliceDataInitializer)
|
||||
sliceDataGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
sliceDataGlobal.SetGlobalConstant(true)
|
||||
sliceDataGlobal.SetUnnamedAddr(true)
|
||||
sliceDataGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceDataInitializer.Type()))
|
||||
|
||||
// Create the slice object itself.
|
||||
// Because embed.FS refers to it as *[]embed.file instead of a plain
|
||||
// []embed.file, we have to store this as a global.
|
||||
slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
})
|
||||
sliceLen := llvm.ConstInt(c.uintptrType, uint64(len(fileStructs)), false)
|
||||
sliceInitializer := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||
sliceGlobal := llvm.AddGlobal(c.mod, sliceInitializer.Type(), c.pkg.Path()+"$embedfsslice")
|
||||
sliceGlobal.SetInitializer(sliceInitializer)
|
||||
sliceGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
sliceGlobal.SetGlobalConstant(true)
|
||||
sliceGlobal.SetUnnamedAddr(true)
|
||||
sliceGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceInitializer.Type()))
|
||||
|
||||
// Define the embed.FS struct. It has only one field: the files (as a
|
||||
// *[]embed.file).
|
||||
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
||||
globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
|
||||
global.SetInitializer(globalInitializer)
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
// getEmbedFileString returns the (constant) string object with the contents of
|
||||
// the given file. This is a llvm.Value of a regular Go string.
|
||||
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
|
||||
dataGlobalName := "embed/file_" + file.Hash
|
||||
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
|
||||
if dataGlobal.IsNil() {
|
||||
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
|
||||
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
|
||||
}
|
||||
strPtr := llvm.ConstInBoundsGEP(dataGlobal, []llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
})
|
||||
strLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
}
|
||||
|
||||
// createFunction builds the LLVM IR implementation for this function. The
|
||||
// function must not yet be defined, otherwise this function will create a
|
||||
// diagnostic.
|
||||
@@ -1107,10 +952,10 @@ func (b *builder) createFunction() {
|
||||
} else {
|
||||
fieldOffsets := b.expandFormalParamOffsets(llvmType)
|
||||
for i, field := range fields {
|
||||
expr := b.dibuilder.CreateExpression([]uint64{
|
||||
0x1000, // DW_OP_LLVM_fragment
|
||||
fieldOffsets[i] * 8, // offset in bits
|
||||
b.targetData.TypeAllocSize(field.Type()) * 8, // size in bits
|
||||
expr := b.dibuilder.CreateExpression([]int64{
|
||||
0x1000, // DW_OP_LLVM_fragment
|
||||
int64(fieldOffsets[i]) * 8, // offset in bits
|
||||
int64(b.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
|
||||
})
|
||||
b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
|
||||
}
|
||||
@@ -1383,7 +1228,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
case "cap":
|
||||
value := argValues[0]
|
||||
var llvmCap llvm.Value
|
||||
switch argTypes[0].Underlying().(type) {
|
||||
switch argTypes[0].(type) {
|
||||
case *types.Chan:
|
||||
llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
|
||||
case *types.Slice:
|
||||
@@ -1590,7 +1435,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.emitSV64Call(instr.Args)
|
||||
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
||||
return b.emitCSROperation(instr)
|
||||
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall"):
|
||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||
return b.createSyscall(instr)
|
||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
|
||||
return b.createRawSyscallNoError(instr)
|
||||
@@ -1619,14 +1464,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
case *ssa.Function:
|
||||
// Regular function call. No context is necessary.
|
||||
context = llvm.Undef(b.i8ptrType)
|
||||
if info.variadic && len(fn.Params) == 0 {
|
||||
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
|
||||
// Eventually we might be able to eliminate this special case
|
||||
// entirely. For details, see:
|
||||
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
|
||||
fnType := llvm.FunctionType(callee.Type().ElementType().ReturnType(), nil, false)
|
||||
callee = llvm.ConstBitCast(callee, llvm.PointerType(fnType, b.funcPtrAddrSpace))
|
||||
}
|
||||
case *ssa.MakeClosure:
|
||||
// A call on a func value, but the callee is trivial to find. For
|
||||
// example: immediately applied functions.
|
||||
@@ -1712,11 +1549,7 @@ func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
||||
// Determine the maximum allowed size for a slice. The biggest possible
|
||||
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
|
||||
// divide by the element type to get the real maximum size.
|
||||
elementSize := c.targetData.TypeAllocSize(elementType)
|
||||
if elementSize == 0 {
|
||||
elementSize = 1
|
||||
}
|
||||
maxSize := maxPointerValue / elementSize
|
||||
maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
|
||||
|
||||
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||
// an int.
|
||||
@@ -2619,41 +2452,42 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
|
||||
}
|
||||
|
||||
// createConst creates a LLVM constant value from a Go constant.
|
||||
func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
|
||||
func (b *builder) createConst(expr *ssa.Const) llvm.Value {
|
||||
switch typ := expr.Type().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
llvmType := c.getLLVMType(typ)
|
||||
llvmType := b.getLLVMType(typ)
|
||||
if typ.Info()&types.IsBoolean != 0 {
|
||||
b := constant.BoolVal(expr.Value)
|
||||
n := uint64(0)
|
||||
if constant.BoolVal(expr.Value) {
|
||||
if b {
|
||||
n = 1
|
||||
}
|
||||
return llvm.ConstInt(llvmType, n, false)
|
||||
} else if typ.Info()&types.IsString != 0 {
|
||||
str := constant.StringVal(expr.Value)
|
||||
strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false)
|
||||
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
||||
var strPtr llvm.Value
|
||||
if str != "" {
|
||||
objname := c.pkg.Path() + "$string"
|
||||
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(c.ctx.ConstString(str, false))
|
||||
objname := b.pkg.Path() + "$string"
|
||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetAlignment(1)
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
} else {
|
||||
strPtr = llvm.ConstNull(c.i8ptrType)
|
||||
strPtr = llvm.ConstNull(b.i8ptrType)
|
||||
}
|
||||
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
if !expr.IsNil() {
|
||||
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType)
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
|
||||
}
|
||||
return llvm.ConstNull(c.i8ptrType)
|
||||
return llvm.ConstNull(b.i8ptrType)
|
||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||
return llvm.ConstInt(llvmType, n, false)
|
||||
@@ -2664,18 +2498,18 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
|
||||
n, _ := constant.Float64Val(expr.Value)
|
||||
return llvm.ConstFloat(llvmType, n)
|
||||
} else if typ.Kind() == types.Complex64 {
|
||||
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
||||
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
||||
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx
|
||||
} else if typ.Kind() == types.Complex128 {
|
||||
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
|
||||
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
|
||||
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
|
||||
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx
|
||||
} else {
|
||||
panic("unknown constant of basic type: " + expr.String())
|
||||
@@ -2684,35 +2518,35 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
|
||||
if expr.Value != nil {
|
||||
panic("expected nil chan constant")
|
||||
}
|
||||
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
||||
case *types.Signature:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil signature constant")
|
||||
}
|
||||
return llvm.ConstNull(c.getLLVMType(expr.Type()))
|
||||
return llvm.ConstNull(b.getLLVMType(expr.Type()))
|
||||
case *types.Interface:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil interface constant")
|
||||
}
|
||||
// Create a generic nil interface with no dynamic type (typecode=0).
|
||||
fields := []llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstPointerNull(c.i8ptrType),
|
||||
llvm.ConstInt(b.uintptrType, 0, false),
|
||||
llvm.ConstPointerNull(b.i8ptrType),
|
||||
}
|
||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||
return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields)
|
||||
case *types.Pointer:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil pointer constant")
|
||||
}
|
||||
return llvm.ConstPointerNull(c.getLLVMType(typ))
|
||||
return llvm.ConstPointerNull(b.getLLVMType(typ))
|
||||
case *types.Slice:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil slice constant")
|
||||
}
|
||||
elemType := c.getLLVMType(typ.Elem())
|
||||
elemType := b.getLLVMType(typ.Elem())
|
||||
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
|
||||
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
slice := c.ctx.ConstStruct([]llvm.Value{
|
||||
llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
|
||||
slice := b.ctx.ConstStruct([]llvm.Value{
|
||||
llvmPtr, // backing array
|
||||
llvmLen, // len
|
||||
llvmLen, // cap
|
||||
@@ -2723,7 +2557,7 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
|
||||
// I believe this is not allowed by the Go spec.
|
||||
panic("non-nil map constant")
|
||||
}
|
||||
llvmType := c.getLLVMType(typ)
|
||||
llvmType := b.getLLVMType(typ)
|
||||
return llvm.ConstNull(llvmType)
|
||||
default:
|
||||
panic("unknown constant: " + expr.String())
|
||||
|
||||
@@ -28,11 +28,18 @@ type testCase struct {
|
||||
func TestCompiler(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Determine LLVM version.
|
||||
// Check LLVM version.
|
||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||
if err != nil {
|
||||
t.Fatal("could not parse LLVM version:", llvm.Version)
|
||||
}
|
||||
if llvmMajor < 11 {
|
||||
// It is likely this version needs to be bumped in the future.
|
||||
// The goal is to at least test the LLVM version that's used by default
|
||||
// in TinyGo and (if possible without too many workarounds) also some
|
||||
// previous versions.
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
}
|
||||
|
||||
// Determine Go minor version (e.g. 16 in go1.16.3).
|
||||
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
@@ -106,7 +113,6 @@ func TestCompiler(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
||||
@@ -215,9 +221,14 @@ func filterIrrelevantIRLines(lines []string) []string {
|
||||
if strings.HasPrefix(line, "source_filename = ") {
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
|
||||
if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
|
||||
// Ignore attribute groups. These may change between LLVM versions.
|
||||
// Right now test outputs are for LLVM 12 and higher.
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 13 && strings.HasPrefix(line, "target datalayout = ") {
|
||||
// The datalayout string may vary betewen LLVM versions.
|
||||
// Right now test outputs are for LLVM 14 and higher.
|
||||
// Right now test outputs are for LLVM 13 and higher.
|
||||
continue
|
||||
}
|
||||
out = append(out, line)
|
||||
|
||||
@@ -98,10 +98,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
case llvm.IntegerTypeKind:
|
||||
constraints = append(constraints, "r")
|
||||
case llvm.PointerTypeKind:
|
||||
// Memory references require a type in LLVM 14, probably as a
|
||||
// preparation for opaque pointers.
|
||||
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
|
||||
return s
|
||||
constraints = append(constraints, "*m")
|
||||
default:
|
||||
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
|
||||
return s
|
||||
|
||||
+45
-79
@@ -152,27 +152,6 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
||||
return structGlobal
|
||||
}
|
||||
|
||||
var basicTypes = [...]string{
|
||||
types.Bool: "bool",
|
||||
types.Int: "int",
|
||||
types.Int8: "int8",
|
||||
types.Int16: "int16",
|
||||
types.Int32: "int32",
|
||||
types.Int64: "int64",
|
||||
types.Uint: "uint",
|
||||
types.Uint8: "uint8",
|
||||
types.Uint16: "uint16",
|
||||
types.Uint32: "uint32",
|
||||
types.Uint64: "uint64",
|
||||
types.Uintptr: "uintptr",
|
||||
types.Float32: "float32",
|
||||
types.Float64: "float64",
|
||||
types.Complex64: "complex64",
|
||||
types.Complex128: "complex128",
|
||||
types.String: "string",
|
||||
types.UnsafePointer: "unsafe.Pointer",
|
||||
}
|
||||
|
||||
// getTypeCodeName returns a name for this type that can be used in the
|
||||
// interface lowering pass to assign type codes as expected by the reflect
|
||||
// package. See getTypeCodeNum.
|
||||
@@ -183,7 +162,48 @@ func getTypeCodeName(t types.Type) string {
|
||||
case *types.Array:
|
||||
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
|
||||
case *types.Basic:
|
||||
return "basic:" + basicTypes[t.Kind()]
|
||||
var kind string
|
||||
switch t.Kind() {
|
||||
case types.Bool:
|
||||
kind = "bool"
|
||||
case types.Int:
|
||||
kind = "int"
|
||||
case types.Int8:
|
||||
kind = "int8"
|
||||
case types.Int16:
|
||||
kind = "int16"
|
||||
case types.Int32:
|
||||
kind = "int32"
|
||||
case types.Int64:
|
||||
kind = "int64"
|
||||
case types.Uint:
|
||||
kind = "uint"
|
||||
case types.Uint8:
|
||||
kind = "uint8"
|
||||
case types.Uint16:
|
||||
kind = "uint16"
|
||||
case types.Uint32:
|
||||
kind = "uint32"
|
||||
case types.Uint64:
|
||||
kind = "uint64"
|
||||
case types.Uintptr:
|
||||
kind = "uintptr"
|
||||
case types.Float32:
|
||||
kind = "float32"
|
||||
case types.Float64:
|
||||
kind = "float64"
|
||||
case types.Complex64:
|
||||
kind = "complex64"
|
||||
case types.Complex128:
|
||||
kind = "complex128"
|
||||
case types.String:
|
||||
kind = "string"
|
||||
case types.UnsafePointer:
|
||||
kind = "unsafeptr"
|
||||
default:
|
||||
panic("unknown basic type: " + t.Name())
|
||||
}
|
||||
return "basic:" + kind
|
||||
case *types.Chan:
|
||||
return "chan:" + getTypeCodeName(t.Elem())
|
||||
case *types.Interface:
|
||||
@@ -571,77 +591,23 @@ func signature(sig *types.Signature) string {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += typestring(sig.Params().At(i).Type())
|
||||
s += sig.Params().At(i).Type().String()
|
||||
}
|
||||
s += ")"
|
||||
}
|
||||
if sig.Results().Len() == 0 {
|
||||
// keep as-is
|
||||
} else if sig.Results().Len() == 1 {
|
||||
s += " " + typestring(sig.Results().At(0).Type())
|
||||
s += " " + sig.Results().At(0).Type().String()
|
||||
} else {
|
||||
s += " ("
|
||||
for i := 0; i < sig.Results().Len(); i++ {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += typestring(sig.Results().At(i).Type())
|
||||
s += sig.Results().At(i).Type().String()
|
||||
}
|
||||
s += ")"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// typestring returns a stable (human-readable) type string for the given type
|
||||
// that can be used for interface equality checks. It is almost (but not
|
||||
// exactly) the same as calling t.String(). The main difference is some
|
||||
// normalization around `byte` vs `uint8` for example.
|
||||
func typestring(t types.Type) string {
|
||||
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
|
||||
switch t := t.(type) {
|
||||
case *types.Array:
|
||||
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
|
||||
case *types.Basic:
|
||||
return basicTypes[t.Kind()]
|
||||
case *types.Chan:
|
||||
switch t.Dir() {
|
||||
case types.SendRecv:
|
||||
return "chan (" + typestring(t.Elem()) + ")"
|
||||
case types.SendOnly:
|
||||
return "chan<- (" + typestring(t.Elem()) + ")"
|
||||
case types.RecvOnly:
|
||||
return "<-chan (" + typestring(t.Elem()) + ")"
|
||||
default:
|
||||
panic("unknown channel direction")
|
||||
}
|
||||
case *types.Interface:
|
||||
methods := make([]string, t.NumMethods())
|
||||
for i := range methods {
|
||||
method := t.Method(i)
|
||||
methods[i] = method.Name() + signature(method.Type().(*types.Signature))
|
||||
}
|
||||
return "interface{" + strings.Join(methods, ";") + "}"
|
||||
case *types.Map:
|
||||
return "map[" + typestring(t.Key()) + "]" + typestring(t.Elem())
|
||||
case *types.Named:
|
||||
return t.String()
|
||||
case *types.Pointer:
|
||||
return "*" + typestring(t.Elem())
|
||||
case *types.Signature:
|
||||
return "func" + signature(t)
|
||||
case *types.Slice:
|
||||
return "[]" + typestring(t.Elem())
|
||||
case *types.Struct:
|
||||
fields := make([]string, t.NumFields())
|
||||
for i := range fields {
|
||||
field := t.Field(i)
|
||||
fields[i] = field.Name() + " " + typestring(field.Type())
|
||||
if tag := t.Tag(i); tag != "" {
|
||||
fields[i] += " " + strconv.Quote(tag)
|
||||
}
|
||||
}
|
||||
return "struct{" + strings.Join(fields, ";") + "}"
|
||||
default:
|
||||
panic("unknown type: " + t.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
|
||||
return fmt.Errorf("type %q uses global context", t.String())
|
||||
default:
|
||||
// we used some other context by accident
|
||||
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.String(), t.Context(), c.ctx)
|
||||
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.Context(), c.ctx)
|
||||
}
|
||||
|
||||
// if this is a composite type, check the components of the type
|
||||
|
||||
@@ -34,7 +34,6 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
|
||||
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||
ctx := t.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
alloca = CreateEntryBlockAlloca(builder, t, name)
|
||||
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
|
||||
@@ -47,7 +46,6 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
|
||||
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
|
||||
alloca := CreateEntryBlockAlloca(builder, t, name)
|
||||
|
||||
@@ -15,9 +15,8 @@ import (
|
||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
||||
|
||||
valueTypes := make([]llvm.Type, len(values))
|
||||
for i, value := range values {
|
||||
@@ -128,9 +127,8 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needs
|
||||
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
||||
|
||||
packedType := ctx.StructType(valueTypes, false)
|
||||
|
||||
|
||||
+1
-13
@@ -10,13 +10,6 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// constants for hashmap algorithms; must match src/runtime/hashmap.go
|
||||
const (
|
||||
hashmapAlgorithmBinary = iota
|
||||
hashmapAlgorithmString
|
||||
hashmapAlgorithmInterface
|
||||
)
|
||||
|
||||
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||
// initializing an appropriately sized object.
|
||||
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
@@ -24,27 +17,22 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
keyType := mapType.Key().Underlying()
|
||||
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||
var llvmKeyType llvm.Type
|
||||
var alg uint64 // must match values in src/runtime/hashmap.go
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// String keys.
|
||||
llvmKeyType = b.getLLVMType(keyType)
|
||||
alg = hashmapAlgorithmString
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// Trivially comparable keys.
|
||||
llvmKeyType = b.getLLVMType(keyType)
|
||||
alg = hashmapAlgorithmBinary
|
||||
} else {
|
||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
||||
// implementation.
|
||||
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
||||
alg = hashmapAlgorithmInterface
|
||||
}
|
||||
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
|
||||
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
|
||||
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
|
||||
if expr.Reserve != nil {
|
||||
sizeHint = b.getValue(expr.Reserve)
|
||||
var err error
|
||||
@@ -53,7 +41,7 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
}
|
||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
|
||||
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
||||
return hashmap, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -45,11 +45,7 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
|
||||
if t.Info()&types.IsString != 0 {
|
||||
return s.PtrSize
|
||||
}
|
||||
case *types.Signature:
|
||||
// Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned
|
||||
return s.PtrSize
|
||||
}
|
||||
|
||||
a := s.Sizeof(T) // may be 0
|
||||
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||
if a < 1 {
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'basic.go'
|
||||
source_filename = "basic.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%main.kv = type { float }
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'channel.go'
|
||||
source_filename = "channel.go"
|
||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'float.go'
|
||||
source_filename = "float.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'func.go'
|
||||
source_filename = "func.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'gc.go'
|
||||
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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'go1.17.go'
|
||||
source_filename = "go1.17.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'goroutine.go'
|
||||
source_filename = "goroutine.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||
|
||||
Vendored
+2
-2
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'interface.go'
|
||||
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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||
@@ -128,5 +128,5 @@ declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
|
||||
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) byte" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) byte" }
|
||||
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'intrinsics.go'
|
||||
source_filename = "intrinsics.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'pointer.go'
|
||||
source_filename = "pointer.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'pragma.go'
|
||||
source_filename = "pragma.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
@extern_global = external global [0 x i8], align 1
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'slice.go'
|
||||
source_filename = "slice.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
; ModuleID = 'string.go'
|
||||
source_filename = "string.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-i64:64-n32:64-S128-ni:1:10:20"
|
||||
target triple = "wasm32-unknown-wasi"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/tinygo-org/tinygo
|
||||
|
||||
go 1.16
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a
|
||||
@@ -15,5 +15,5 @@ require (
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f
|
||||
tinygo.org/x/go-llvm v0.0.0-20220121152956-4fa2ab2718f3
|
||||
)
|
||||
|
||||
@@ -80,5 +80,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f h1:9/J+NpFTpAhYcbh1mC4dr9W/aAPnwrUh8dmq21xWdSM=
|
||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
tinygo.org/x/go-llvm v0.0.0-20220121152956-4fa2ab2718f3 h1:vQSFy0kNQegAfL/F6iyWQa4bF941Xc1gyJUkGy2m448=
|
||||
tinygo.org/x/go-llvm v0.0.0-20220121152956-4fa2ab2718f3/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
|
||||
+3
-9
@@ -12,13 +12,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.24.0-dev"
|
||||
|
||||
var (
|
||||
// This variable is set at build time using -ldflags parameters.
|
||||
// See: https://stackoverflow.com/a/11355611
|
||||
GitSha1 string
|
||||
)
|
||||
const Version = "0.23.0-dev"
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
@@ -58,9 +52,9 @@ func GorootVersionString(goroot string) (string, error) {
|
||||
return string(data), nil
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(
|
||||
goroot, "src", "internal", "buildcfg", "zbootstrap.go")); err == nil {
|
||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||
|
||||
r := regexp.MustCompile("const version = `(.*)`")
|
||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||
matches := r.FindSubmatch(data)
|
||||
if len(matches) != 2 {
|
||||
return "", errors.New("Invalid go version output:\n" + string(data))
|
||||
|
||||
+3
-18
@@ -50,17 +50,10 @@ func newRunner(mod llvm.Module, debug bool) *runner {
|
||||
return &r
|
||||
}
|
||||
|
||||
// Dispose deallocates all alloated LLVM resources.
|
||||
func (r *runner) dispose() {
|
||||
r.targetData.Dispose()
|
||||
r.targetData = llvm.TargetData{}
|
||||
}
|
||||
|
||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||
// Set debug to true if it should print output while running.
|
||||
func Run(mod llvm.Module, debug bool) error {
|
||||
r := newRunner(mod, debug)
|
||||
defer r.dispose()
|
||||
|
||||
initAll := mod.NamedFunction("runtime.initAll")
|
||||
bb := initAll.EntryBasicBlock()
|
||||
@@ -128,10 +121,7 @@ func Run(mod llvm.Module, debug bool) error {
|
||||
r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
|
||||
// Make sure that any globals touched by the package
|
||||
// initializer, won't be accessed by later package initializers.
|
||||
err := r.markExternalLoad(fn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to interpret package %s: %w", r.pkgName, err)
|
||||
}
|
||||
r.markExternalLoad(fn)
|
||||
continue
|
||||
}
|
||||
return callErr
|
||||
@@ -203,7 +193,6 @@ func RunFunc(fn llvm.Value, debug bool) error {
|
||||
// Create and initialize *runner object.
|
||||
mod := fn.GlobalParent()
|
||||
r := newRunner(mod, debug)
|
||||
defer r.dispose()
|
||||
initName := fn.Name()
|
||||
if !strings.HasSuffix(initName, ".init") {
|
||||
return errorAt(fn, "interp: unexpected function name (expected *.init)")
|
||||
@@ -299,16 +288,12 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
||||
// variable. Another package initializer might read from the same global
|
||||
// variable. By marking this function as being run at runtime, that load
|
||||
// instruction will need to be run at runtime instead of at compile time.
|
||||
func (r *runner) markExternalLoad(llvmValue llvm.Value) error {
|
||||
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
|
||||
mem := memoryView{r: r}
|
||||
err := mem.markExternalLoad(llvmValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mem.markExternalLoad(llvmValue)
|
||||
for index, obj := range mem.objects {
|
||||
if obj.marked > r.objects[index].marked {
|
||||
r.objects[index].marked = obj.marked
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-13
@@ -3,7 +3,6 @@ package interp
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -11,12 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func TestInterp(t *testing.T) {
|
||||
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||
if err != nil {
|
||||
// Note: this should never happen and if it does, it will always happen
|
||||
// for a particular build because llvm.Version is a constant.
|
||||
panic(err)
|
||||
}
|
||||
for _, name := range []string{
|
||||
"basic",
|
||||
"phi",
|
||||
@@ -26,10 +19,7 @@ func TestInterp(t *testing.T) {
|
||||
"revert",
|
||||
"alloc",
|
||||
} {
|
||||
name := name // make local to this closure
|
||||
if name == "slice-copy" && llvmVersion < 14 {
|
||||
continue
|
||||
}
|
||||
name := name // make tc local to this closure
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(t, "testdata/"+name)
|
||||
@@ -40,7 +30,6 @@ func TestInterp(t *testing.T) {
|
||||
func runTest(t *testing.T, pathPrefix string) {
|
||||
// Read the input IR.
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
|
||||
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
|
||||
if err != nil {
|
||||
@@ -50,7 +39,6 @@ func runTest(t *testing.T, pathPrefix string) {
|
||||
if err != nil {
|
||||
t.Fatalf("could not load module:\n%v", err)
|
||||
}
|
||||
defer mod.Dispose()
|
||||
|
||||
// Perform the transform.
|
||||
err = Run(mod, false)
|
||||
|
||||
+7
-35
@@ -81,28 +81,12 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
if inst.opcode != llvm.PHI {
|
||||
for _, v := range inst.operands {
|
||||
if v, ok := v.(localValue); ok {
|
||||
index, ok := fn.locals[v.value]
|
||||
if !ok {
|
||||
// This is a localValue that is not local to the
|
||||
// function. An example would be an inline assembly call
|
||||
// operand.
|
||||
isRuntimeInst = true
|
||||
break
|
||||
}
|
||||
localVal := locals[index]
|
||||
if localVal == nil {
|
||||
// Trying to read a function-local value before it is
|
||||
// set.
|
||||
if localVal := locals[fn.locals[v.value]]; localVal == nil {
|
||||
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
|
||||
} else {
|
||||
operands = append(operands, localVal)
|
||||
if _, ok := localVal.(localValue); ok {
|
||||
// The function-local value is still just a
|
||||
// localValue (which can't be interpreted at compile
|
||||
// time). Not sure whether this ever happens in
|
||||
// practice.
|
||||
isRuntimeInst = true
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -235,9 +219,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
case callFn.name == "internal/task.Pause":
|
||||
// Task scheduling isn't possible at compile time.
|
||||
return nil, mem, r.errorAt(inst, errUnsupportedRuntimeInst)
|
||||
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
|
||||
// The time package contains a call to runtime.nanotime.
|
||||
// This appears to be to work around a limitation in Windows
|
||||
@@ -511,9 +492,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// Call a function with a definition available. Run it as usual,
|
||||
// possibly trying to recover from it if it failed to execute.
|
||||
if r.debug {
|
||||
argStrings := make([]string, len(operands)-1)
|
||||
for i, v := range operands[1:] {
|
||||
argStrings[i] = v.String()
|
||||
argStrings := make([]string, len(operands))
|
||||
for i := range argStrings {
|
||||
argStrings[i] = operands[i+1].String()
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, indent+"call:", callFn.name+"("+strings.Join(argStrings, ", ")+")")
|
||||
}
|
||||
@@ -945,18 +926,12 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
||||
args := operands[:len(operands)-1]
|
||||
for _, arg := range args {
|
||||
if arg.Type().TypeKind() == llvm.PointerTypeKind {
|
||||
err := mem.markExternalStore(arg)
|
||||
if err != nil {
|
||||
return r.errorAt(inst, err)
|
||||
}
|
||||
mem.markExternalStore(arg)
|
||||
}
|
||||
}
|
||||
result = r.builder.CreateCall(llvmFn, args, inst.name)
|
||||
case llvm.Load:
|
||||
err := mem.markExternalLoad(operands[0])
|
||||
if err != nil {
|
||||
return r.errorAt(inst, err)
|
||||
}
|
||||
mem.markExternalLoad(operands[0])
|
||||
result = r.builder.CreateLoad(operands[0], inst.name)
|
||||
if inst.llvmInst.IsVolatile() {
|
||||
result.SetVolatile(true)
|
||||
@@ -965,10 +940,7 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
||||
result.SetOrdering(ordering)
|
||||
}
|
||||
case llvm.Store:
|
||||
err := mem.markExternalStore(operands[1])
|
||||
if err != nil {
|
||||
return r.errorAt(inst, err)
|
||||
}
|
||||
mem.markExternalStore(operands[1])
|
||||
result = r.builder.CreateStore(operands[0], operands[1])
|
||||
if inst.llvmInst.IsVolatile() {
|
||||
result.SetVolatile(true)
|
||||
|
||||
+14
-41
@@ -17,7 +17,6 @@ package interp
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
@@ -105,28 +104,28 @@ func (mv *memoryView) revert() {
|
||||
// means that the interpreter can still read from it, but cannot write to it as
|
||||
// that would mean the external read (done at runtime) reads from a state that
|
||||
// would not exist had the whole initialization been done at runtime.
|
||||
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) error {
|
||||
return mv.markExternal(llvmValue, 1)
|
||||
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) {
|
||||
mv.markExternal(llvmValue, 1)
|
||||
}
|
||||
|
||||
// markExternalStore marks the given LLVM value as having an external write.
|
||||
// This means that the interpreter can no longer read from it or write to it, as
|
||||
// that would happen in a different order than if all initialization were
|
||||
// happening at runtime.
|
||||
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) error {
|
||||
return mv.markExternal(llvmValue, 2)
|
||||
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) {
|
||||
mv.markExternal(llvmValue, 2)
|
||||
}
|
||||
|
||||
// markExternal is a helper for markExternalLoad and markExternalStore, and
|
||||
// should not be called directly.
|
||||
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
||||
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
|
||||
if llvmValue.IsUndef() || llvmValue.IsNull() {
|
||||
// Null and undef definitely don't contain (valid) pointers.
|
||||
return nil
|
||||
return
|
||||
}
|
||||
if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
|
||||
// These are considered external by default, there is nothing to mark.
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
if !llvmValue.IsAGlobalValue().IsNil() {
|
||||
@@ -145,10 +144,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
||||
// Using mark '2' (which means read/write access) because
|
||||
// even from an object that is only read from, the resulting
|
||||
// loaded pointer can be written to.
|
||||
err := mv.markExternal(initializer, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mv.markExternal(initializer, 2)
|
||||
}
|
||||
} else {
|
||||
// This is a function. Go through all instructions and mark all
|
||||
@@ -174,10 +170,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
||||
for i := 0; i < numOperands; i++ {
|
||||
// Using mark '2' (which means read/write access)
|
||||
// because this might be a store instruction.
|
||||
err := mv.markExternal(inst.Operand(i), 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mv.markExternal(inst.Operand(i), 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,22 +179,9 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
||||
} else if !llvmValue.IsAConstantExpr().IsNil() {
|
||||
switch llvmValue.Opcode() {
|
||||
case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
|
||||
err := mv.markExternal(llvmValue.Operand(0), mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
|
||||
// Integer binary operators. Mark both operands.
|
||||
err := mv.markExternal(llvmValue.Operand(0), mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mv.markExternal(llvmValue.Operand(1), mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mv.markExternal(llvmValue.Operand(0), mark)
|
||||
default:
|
||||
return fmt.Errorf("interp: unknown constant expression '%s'", instructionNameMap[llvmValue.Opcode()])
|
||||
panic("interp: unknown constant expression")
|
||||
}
|
||||
} else if !llvmValue.IsAInlineAsm().IsNil() {
|
||||
// Inline assembly can modify globals but only exported globals. Let's
|
||||
@@ -216,25 +196,18 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
||||
numElements := llvmType.StructElementTypesCount()
|
||||
for i := 0; i < numElements; i++ {
|
||||
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
||||
err := mv.markExternal(element, mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mv.markExternal(element, mark)
|
||||
}
|
||||
case llvm.ArrayTypeKind:
|
||||
numElements := llvmType.ArrayLength()
|
||||
for i := 0; i < numElements; i++ {
|
||||
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
|
||||
err := mv.markExternal(element, mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mv.markExternal(element, mark)
|
||||
}
|
||||
default:
|
||||
return errors.New("interp: unknown type kind in markExternalValue")
|
||||
panic("interp: unknown type kind in markExternalValue")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasExternalLoadOrStore returns true if this object has an external load or
|
||||
|
||||
Vendored
+4
-1
@@ -1,6 +1,8 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@"main$alloc.1" = internal unnamed_addr constant [6 x i8] c"\05\00{\00\00\04", align 8
|
||||
|
||||
declare void @runtime.printuint8(i8) local_unnamed_addr
|
||||
|
||||
declare void @runtime.printint16(i16) local_unnamed_addr
|
||||
@@ -15,6 +17,7 @@ entry:
|
||||
call void @runtime.printuint8(i8 3)
|
||||
call void @runtime.printuint8(i8 3)
|
||||
call void @runtime.printint16(i16 5)
|
||||
call void @runtime.printint16(i16 5)
|
||||
%int16SliceDst.val = load i16, i16* bitcast ([6 x i8]* @"main$alloc.1" to i16*), align 2
|
||||
call void @runtime.printint16(i16 %int16SliceDst.val)
|
||||
ret void
|
||||
}
|
||||
|
||||
Submodule
+1
Submodule lib/compiler-rt added at 5bc79797e1
@@ -230,7 +230,6 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
|
||||
"device/": false,
|
||||
"examples/": false,
|
||||
"internal/": true,
|
||||
"internal/fuzz/": false,
|
||||
"internal/bytealg/": false,
|
||||
"internal/reflectlite/": false,
|
||||
"internal/task/": false,
|
||||
|
||||
+11
-276
@@ -7,7 +7,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
@@ -16,12 +15,10 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/tinygo-org/tinygo/cgo"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -64,9 +61,6 @@ type PackageJSON struct {
|
||||
CgoFiles []string
|
||||
CFiles []string
|
||||
|
||||
// Embedded files
|
||||
EmbedFiles []string
|
||||
|
||||
// Dependency information
|
||||
Imports []string
|
||||
ImportMap map[string]string
|
||||
@@ -83,22 +77,13 @@ type PackageJSON struct {
|
||||
type Package struct {
|
||||
PackageJSON
|
||||
|
||||
program *Program
|
||||
Files []*ast.File
|
||||
FileHashes map[string][]byte
|
||||
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||
CGoHeaders []string // text above 'import "C"' lines
|
||||
EmbedGlobals map[string][]*EmbedFile
|
||||
Pkg *types.Package
|
||||
info types.Info
|
||||
}
|
||||
|
||||
type EmbedFile struct {
|
||||
Name string
|
||||
Size uint64
|
||||
Hash string // hash of the file (as a hex string)
|
||||
NeedsData bool // true if this file is embedded as a byte slice
|
||||
Data []byte // contents of this file (only if NeedsData is set)
|
||||
program *Program
|
||||
Files []*ast.File
|
||||
FileHashes map[string][]byte
|
||||
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||
CGoHeaders []string // text above 'import "C"' lines
|
||||
Pkg *types.Package
|
||||
info types.Info
|
||||
}
|
||||
|
||||
// Load loads the given package with all dependencies (including the runtime
|
||||
@@ -152,9 +137,8 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
decoder := json.NewDecoder(buf)
|
||||
for {
|
||||
pkg := &Package{
|
||||
program: p,
|
||||
FileHashes: make(map[string][]byte),
|
||||
EmbedGlobals: make(map[string][]*EmbedFile),
|
||||
program: p,
|
||||
FileHashes: make(map[string][]byte),
|
||||
info: types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
@@ -373,15 +357,15 @@ func (p *Package) Check() error {
|
||||
return nil // already typechecked
|
||||
}
|
||||
|
||||
// Prepare some state used during type checking.
|
||||
var typeErrors []error
|
||||
checker := p.program.typeChecker // make a copy, because it will be modified
|
||||
checker.Error = func(err error) {
|
||||
typeErrors = append(typeErrors, err)
|
||||
}
|
||||
checker.Importer = p
|
||||
|
||||
// Do typechecking of the package.
|
||||
checker.Importer = p
|
||||
|
||||
packageName := p.ImportPath
|
||||
if p == p.program.MainPkg() {
|
||||
if p.Name != "main" {
|
||||
@@ -398,12 +382,6 @@ func (p *Package) Check() error {
|
||||
return Errors{p, typeErrors}
|
||||
}
|
||||
p.Pkg = typesPkg
|
||||
|
||||
p.extractEmbedLines(checker.Error)
|
||||
if len(typeErrors) != 0 {
|
||||
return Errors{p, typeErrors}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -462,249 +440,6 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// extractEmbedLines finds all //go:embed lines in the package and matches them
|
||||
// against EmbedFiles from `go list`.
|
||||
func (p *Package) extractEmbedLines(addError func(error)) {
|
||||
for _, file := range p.Files {
|
||||
// Check for an `import "embed"` line at the start of the file.
|
||||
// //go:embed lines are only valid if the given file itself imports the
|
||||
// embed package. It is not valid if it is only imported in a separate
|
||||
// Go file.
|
||||
hasEmbed := false
|
||||
for _, importSpec := range file.Imports {
|
||||
if importSpec.Path.Value == `"embed"` {
|
||||
hasEmbed = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
if decl.Tok != token.VAR {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
var doc *ast.CommentGroup
|
||||
if decl.Lparen == token.NoPos {
|
||||
// Plain 'var' declaration, like:
|
||||
// //go:embed hello.txt
|
||||
// var hello string
|
||||
doc = decl.Doc
|
||||
} else {
|
||||
// Bigger 'var' declaration like:
|
||||
// var (
|
||||
// //go:embed hello.txt
|
||||
// hello string
|
||||
// )
|
||||
doc = spec.Doc
|
||||
}
|
||||
if doc == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for //go:embed comments.
|
||||
var allPatterns []string
|
||||
for _, comment := range doc.List {
|
||||
if comment.Text != "//go:embed" && !strings.HasPrefix(comment.Text, "//go:embed ") {
|
||||
continue
|
||||
}
|
||||
if !hasEmbed {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: comment.Pos() + 2,
|
||||
Msg: "//go:embed only allowed in Go files that import \"embed\"",
|
||||
})
|
||||
// Continue, because otherwise we might run into
|
||||
// issues below.
|
||||
continue
|
||||
}
|
||||
patterns, err := p.parseGoEmbed(comment.Text[len("//go:embed"):], comment.Slash)
|
||||
if err != nil {
|
||||
addError(err)
|
||||
continue
|
||||
}
|
||||
if len(patterns) == 0 {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: comment.Pos() + 2,
|
||||
Msg: "usage: //go:embed pattern...",
|
||||
})
|
||||
continue
|
||||
}
|
||||
for _, pattern := range patterns {
|
||||
// Check that the pattern is well-formed.
|
||||
// It must be valid: the Go toolchain has already
|
||||
// checked for invalid patterns. But let's check
|
||||
// anyway to be sure.
|
||||
if _, err := path.Match(pattern, ""); err != nil {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: comment.Pos(),
|
||||
Msg: "invalid pattern syntax",
|
||||
})
|
||||
continue
|
||||
}
|
||||
allPatterns = append(allPatterns, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
if len(allPatterns) != 0 {
|
||||
// This is a //go:embed global. Do a few more checks.
|
||||
if len(spec.Names) != 1 {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: spec.Names[1].NamePos,
|
||||
Msg: "//go:embed cannot apply to multiple vars",
|
||||
})
|
||||
}
|
||||
if spec.Values != nil {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: spec.Values[0].Pos(),
|
||||
Msg: "//go:embed cannot apply to var with initializer",
|
||||
})
|
||||
}
|
||||
globalName := spec.Names[0].Name
|
||||
globalType := p.Pkg.Scope().Lookup(globalName).Type()
|
||||
valid, byteSlice := isValidEmbedType(globalType)
|
||||
if !valid {
|
||||
addError(types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: spec.Type.Pos(),
|
||||
Msg: "//go:embed cannot apply to var of type " + globalType.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// Match all //go:embed patterns against the embed files
|
||||
// provided by `go list`.
|
||||
for _, name := range p.EmbedFiles {
|
||||
for _, pattern := range allPatterns {
|
||||
if matchPattern(pattern, name) {
|
||||
p.EmbedGlobals[globalName] = append(p.EmbedGlobals[globalName], &EmbedFile{
|
||||
Name: name,
|
||||
NeedsData: byteSlice,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// matchPattern returns true if (and only if) the given pattern would match the
|
||||
// filename. The pattern could also match a parent directory of name, in which
|
||||
// case hidden files do not match.
|
||||
func matchPattern(pattern, name string) bool {
|
||||
// Match this file.
|
||||
matched, _ := path.Match(pattern, name)
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
|
||||
// Match parent directories.
|
||||
dir := name
|
||||
for {
|
||||
dir, _ = path.Split(dir)
|
||||
if dir == "" {
|
||||
return false
|
||||
}
|
||||
dir = path.Clean(dir)
|
||||
if matched, _ := path.Match(pattern, dir); matched {
|
||||
// Pattern matches the directory.
|
||||
suffix := name[len(dir):]
|
||||
if strings.Contains(suffix, "/_") || strings.Contains(suffix, "/.") {
|
||||
// Pattern matches a hidden file.
|
||||
// Hidden files are included when listed directly as a
|
||||
// pattern, but not when they are part of a directory tree.
|
||||
// Source:
|
||||
// > If a pattern names a directory, all files in the
|
||||
// > subtree rooted at that directory are embedded
|
||||
// > (recursively), except that files with names beginning
|
||||
// > with ‘.’ or ‘_’ are excluded.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseGoEmbed is like strings.Fields but for a //go:embed line. It parses
|
||||
// regular fields and quoted fields (that may contain spaces).
|
||||
func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, err error) {
|
||||
args = strings.TrimSpace(args)
|
||||
initialLen := len(args)
|
||||
for args != "" {
|
||||
patternPos := pos + token.Pos(initialLen-len(args))
|
||||
switch args[0] {
|
||||
case '`', '"', '\\':
|
||||
// Parse the next pattern using the Go scanner.
|
||||
// This is perhaps a bit overkill, but it does correctly implement
|
||||
// parsing of the various Go strings.
|
||||
var sc scanner.Scanner
|
||||
fset := &token.FileSet{}
|
||||
file := fset.AddFile("", 0, len(args))
|
||||
sc.Init(file, []byte(args), nil, 0)
|
||||
_, tok, lit := sc.Scan()
|
||||
if tok != token.STRING || sc.ErrorCount != 0 {
|
||||
// Calculate start of token
|
||||
return nil, types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: patternPos,
|
||||
Msg: "invalid quoted string in //go:embed",
|
||||
}
|
||||
}
|
||||
pattern := constant.StringVal(constant.MakeFromLiteral(lit, tok, 0))
|
||||
patterns = append(patterns, pattern)
|
||||
args = strings.TrimLeftFunc(args[len(lit):], unicode.IsSpace)
|
||||
default:
|
||||
// The value is just a regular value.
|
||||
// Split it at the first white space.
|
||||
index := strings.IndexFunc(args, unicode.IsSpace)
|
||||
if index < 0 {
|
||||
index = len(args)
|
||||
}
|
||||
pattern := args[:index]
|
||||
patterns = append(patterns, pattern)
|
||||
args = strings.TrimLeftFunc(args[len(pattern):], unicode.IsSpace)
|
||||
}
|
||||
if _, err := path.Match(patterns[len(patterns)-1], ""); err != nil {
|
||||
return nil, types.Error{
|
||||
Fset: p.program.fset,
|
||||
Pos: patternPos,
|
||||
Msg: "invalid pattern syntax",
|
||||
}
|
||||
}
|
||||
}
|
||||
return patterns, nil
|
||||
}
|
||||
|
||||
// isValidEmbedType returns whether the given Go type can be used as a
|
||||
// //go:embed type. This is only true for embed.FS, strings, and byte slices.
|
||||
// The second return value indicates that this is a byte slice, and therefore
|
||||
// the contents of the file needs to be passed to the compiler.
|
||||
func isValidEmbedType(typ types.Type) (valid, byteSlice bool) {
|
||||
if typ.Underlying() == types.Typ[types.String] {
|
||||
// string type
|
||||
return true, false
|
||||
}
|
||||
if sliceType, ok := typ.Underlying().(*types.Slice); ok {
|
||||
if elemType, ok := sliceType.Elem().Underlying().(*types.Basic); ok && elemType.Kind() == types.Byte {
|
||||
// byte slice type
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
if namedType, ok := typ.(*types.Named); ok && namedType.String() == "embed.FS" {
|
||||
// embed.FS type
|
||||
return true, false
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Import implements types.Importer. It loads and parses packages it encounters
|
||||
// along the way, if needed.
|
||||
func (p *Package) Import(to string) (*types.Package, error) {
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
@@ -37,6 +36,12 @@ import (
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
var (
|
||||
// This variable is set at build time using -ldflags parameters.
|
||||
// See: https://stackoverflow.com/a/11355611
|
||||
gitSha1 string
|
||||
)
|
||||
|
||||
// commandError is an error type to wrap os/exec.Command errors. This provides
|
||||
// some more information regarding what went wrong while running a command.
|
||||
type commandError struct {
|
||||
@@ -143,27 +148,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if options.PrintJSON {
|
||||
b, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
handleCompilerError(err)
|
||||
}
|
||||
fmt.Printf("%s\n", string(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
if outpath == "" {
|
||||
if strings.HasSuffix(pkgName, ".go") {
|
||||
// A Go file was specified directly on the command line.
|
||||
// Base the binary name off of it.
|
||||
outpath = filepath.Base(pkgName[:len(pkgName)-3]) + config.DefaultBinaryExtension()
|
||||
} else {
|
||||
// Pick a default output path based on the main directory.
|
||||
outpath = filepath.Base(result.MainDir) + config.DefaultBinaryExtension()
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Rename(result.Binary, outpath); err != nil {
|
||||
// Moving failed. Do a file copy.
|
||||
inf, err := os.Open(result.Binary)
|
||||
@@ -200,26 +185,8 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Pass test flags to the test binary.
|
||||
var flags []string
|
||||
if testVerbose {
|
||||
flags = append(flags, "-test.v")
|
||||
}
|
||||
if testShort {
|
||||
flags = append(flags, "-test.short")
|
||||
}
|
||||
if testRunRegexp != "" {
|
||||
flags = append(flags, "-test.run="+testRunRegexp)
|
||||
}
|
||||
if testBenchRegexp != "" {
|
||||
flags = append(flags, "-test.bench="+testBenchRegexp)
|
||||
}
|
||||
if testBenchTime != "" {
|
||||
flags = append(flags, "-test.benchtime="+testBenchTime)
|
||||
}
|
||||
|
||||
passed := false
|
||||
err = buildAndRun(pkgName, config, os.Stdout, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||
err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
if testCompileOnly || outpath != "" {
|
||||
// Write test binary to the specified file name.
|
||||
if outpath == "" {
|
||||
@@ -235,52 +202,27 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tests are always run in the package directory.
|
||||
cmd.Dir = result.MainDir
|
||||
|
||||
// Wasmtime needs a few extra flags to work.
|
||||
if config.EmulatorName() == "wasmtime" {
|
||||
// Add directories to the module root, but skip the current working
|
||||
// directory which is already added by buildAndRun.
|
||||
dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
|
||||
var args []string
|
||||
for _, d := range dirs[1:] {
|
||||
args = append(args, "--dir="+d)
|
||||
}
|
||||
|
||||
// create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR
|
||||
tmpdir, err := ioutil.TempDir("", "tinygotmp")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temporary directory: %w", err)
|
||||
}
|
||||
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
|
||||
// TODO: add option to not delete temp dir for debugging?
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
// Insert new argments at the front of the command line argments.
|
||||
args = append(args, cmd.Args[1:]...)
|
||||
cmd.Args = append(cmd.Args[:1:1], args...)
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
config.Options.Semaphore <- struct{}{}
|
||||
defer func() {
|
||||
<-config.Options.Semaphore
|
||||
}()
|
||||
start := time.Now()
|
||||
err = cmd.Run()
|
||||
var err error
|
||||
passed, err = runPackageTest(config, stdout, stderr, result, testVerbose, testShort, testRunRegexp, testBenchRegexp, testBenchTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
duration := time.Since(start)
|
||||
|
||||
// Print the result.
|
||||
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
||||
passed = err == nil
|
||||
if passed {
|
||||
fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
} else {
|
||||
fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
}
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
// Binary exited with a non-zero exit code, which means the test
|
||||
// failed.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
})
|
||||
if err, ok := err.(loader.NoTestFilesError); ok {
|
||||
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath)
|
||||
@@ -303,6 +245,81 @@ func dirsToModuleRoot(maindir, modroot string) []string {
|
||||
return dirs
|
||||
}
|
||||
|
||||
// runPackageTest runs a test binary that was previously built. The return
|
||||
// values are whether the test passed and any errors encountered while trying to
|
||||
// run the binary.
|
||||
func runPackageTest(config *compileopts.Config, stdout, stderr io.Writer, result builder.BuildResult, testVerbose, testShort bool, testRunRegexp string, testBenchRegexp string, testBenchTime string) (bool, error) {
|
||||
var cmd *exec.Cmd
|
||||
emulator := config.Emulator()
|
||||
if len(emulator) == 0 {
|
||||
// Run directly.
|
||||
var flags []string
|
||||
if testVerbose {
|
||||
flags = append(flags, "-test.v")
|
||||
}
|
||||
if testShort {
|
||||
flags = append(flags, "-test.short")
|
||||
}
|
||||
if testRunRegexp != "" {
|
||||
flags = append(flags, "-test.run="+testRunRegexp)
|
||||
}
|
||||
if testBenchRegexp != "" {
|
||||
flags = append(flags, "-test.bench="+testBenchRegexp)
|
||||
}
|
||||
if testBenchTime != "" {
|
||||
flags = append(flags, "-test.benchtime="+testBenchTime)
|
||||
}
|
||||
cmd = executeCommand(config.Options, result.Binary, flags...)
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
args := append(emulator[1:], result.Binary)
|
||||
if emulator[0] == "wasmtime" {
|
||||
// create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR
|
||||
tmpdir, err := ioutil.TempDir("", "tinygotmp")
|
||||
if err != nil {
|
||||
return false, &commandError{"failed to create temporary directory", "tinygotmp", err}
|
||||
}
|
||||
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
|
||||
// TODO: add option to not delete temp dir for debugging?
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
// allow reading from directories up to module root
|
||||
for _, d := range dirsToModuleRoot(result.MainDir, result.ModuleRoot) {
|
||||
args = append(args, "--dir="+d)
|
||||
}
|
||||
|
||||
// mark end of wasmtime arguments and start of program ones: --
|
||||
args = append(args, "--")
|
||||
if testVerbose {
|
||||
args = append(args, "-test.v")
|
||||
}
|
||||
if testShort {
|
||||
args = append(args, "-test.short")
|
||||
}
|
||||
if testRunRegexp != "" {
|
||||
args = append(args, "-test.run="+testRunRegexp)
|
||||
}
|
||||
if testBenchRegexp != "" {
|
||||
args = append(args, "-test.bench="+testBenchRegexp)
|
||||
}
|
||||
}
|
||||
cmd = executeCommand(config.Options, emulator[0], args...)
|
||||
}
|
||||
cmd.Dir = result.MainDir
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
// Binary exited with a non-zero exit code, which means the test
|
||||
// failed.
|
||||
return false, nil
|
||||
}
|
||||
return false, &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Flash builds and flashes the built binary to the given serial port.
|
||||
func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
@@ -422,11 +439,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exit := " reset exit"
|
||||
if config.Target.OpenOCDVerify != nil && *config.Target.OpenOCDVerify {
|
||||
exit = " verify" + exit
|
||||
}
|
||||
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+exit)
|
||||
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
|
||||
cmd := executeCommand(config.Options, "openocd", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -483,19 +496,18 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||
return err
|
||||
}
|
||||
|
||||
format, fileExt := config.EmulatorFormat()
|
||||
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
|
||||
// Find a good way to run GDB.
|
||||
gdbInterface, openocdInterface := config.Programmer()
|
||||
switch gdbInterface {
|
||||
case "msd", "command", "":
|
||||
emulator := config.EmulatorName()
|
||||
if emulator != "" {
|
||||
if emulator == "mgba" {
|
||||
emulator := config.Emulator()
|
||||
if len(emulator) != 0 {
|
||||
if emulator[0] == "mgba" {
|
||||
gdbInterface = "mgba"
|
||||
} else if emulator == "simavr" {
|
||||
} else if emulator[0] == "simavr" {
|
||||
gdbInterface = "simavr"
|
||||
} else if strings.HasPrefix(emulator, "qemu-system-") {
|
||||
} else if strings.HasPrefix(emulator[0], "qemu-system-") {
|
||||
gdbInterface = "qemu"
|
||||
} else {
|
||||
// Assume QEMU as an emulator.
|
||||
@@ -514,10 +526,6 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||
port := ""
|
||||
var gdbCommands []string
|
||||
var daemon *exec.Cmd
|
||||
emulator, err := config.Emulator(format, result.Binary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch gdbInterface {
|
||||
case "native":
|
||||
// Run GDB directly.
|
||||
@@ -567,29 +575,33 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||
}
|
||||
case "qemu":
|
||||
port = ":1234"
|
||||
emulator := config.Emulator()
|
||||
// Run in an emulator.
|
||||
args := append(emulator[1:], "-s", "-S")
|
||||
args := append(emulator[1:], result.Binary, "-s", "-S")
|
||||
daemon = executeCommand(config.Options, emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "qemu-user":
|
||||
port = ":1234"
|
||||
emulator := config.Emulator()
|
||||
// Run in an emulator.
|
||||
args := append(emulator[1:], "-g", "1234")
|
||||
args := append(emulator[1:], "-g", "1234", result.Binary)
|
||||
daemon = executeCommand(config.Options, emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "mgba":
|
||||
port = ":2345"
|
||||
emulator := config.Emulator()
|
||||
// Run in an emulator.
|
||||
args := append(emulator[1:], "-g")
|
||||
args := append(emulator[1:], result.Binary, "-g")
|
||||
daemon = executeCommand(config.Options, emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "simavr":
|
||||
port = ":1234"
|
||||
emulator := config.Emulator()
|
||||
// Run in an emulator.
|
||||
args := append(emulator[1:], "-g")
|
||||
args := append(emulator[1:], "-g", result.Binary)
|
||||
daemon = executeCommand(config.Options, emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
@@ -634,7 +646,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||
// Construct and execute a gdb or lldb command.
|
||||
// By default: gdb -ex run <binary>
|
||||
// Exit the debugger with Ctrl-D.
|
||||
params := []string{result.Executable}
|
||||
params := []string{result.Binary}
|
||||
switch debugger {
|
||||
case "gdb":
|
||||
if port != "" {
|
||||
@@ -666,9 +678,9 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return &commandError{"failed to run " + cmdName + " with", result.Executable, err}
|
||||
return &commandError{"failed to run " + cmdName + " with", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -678,134 +690,44 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
||||
// the options, it will run the program directly on the host or will run it in
|
||||
// an emulator. For example, -target=wasm will cause the binary to be run inside
|
||||
// of a WebAssembly VM.
|
||||
func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
|
||||
func Run(pkgName string, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||
return cmd.Run()
|
||||
})
|
||||
}
|
||||
|
||||
// buildAndRun builds and runs the given program, writing output to stdout and
|
||||
// errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and
|
||||
// passes command line arguments and evironment variables in a way appropriate
|
||||
// for the given emulator.
|
||||
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) error {
|
||||
// Determine whether we're on a system that supports environment variables
|
||||
// and command line parameters (operating systems, WASI) or not (baremetal,
|
||||
// WebAssembly in the browser). If we're on a system without an environment,
|
||||
// we need to pass command line arguments and environment variables through
|
||||
// global variables (built into the binary directly) instead of the
|
||||
// conventional way.
|
||||
needsEnvInVars := config.GOOS() == "js"
|
||||
for _, tag := range config.BuildTags() {
|
||||
if tag == "baremetal" {
|
||||
needsEnvInVars = true
|
||||
}
|
||||
}
|
||||
var args, env []string
|
||||
if needsEnvInVars {
|
||||
runtimeGlobals := make(map[string]string)
|
||||
if len(cmdArgs) != 0 {
|
||||
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
|
||||
}
|
||||
if len(environmentVars) != 0 {
|
||||
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
|
||||
}
|
||||
if len(runtimeGlobals) != 0 {
|
||||
// This sets the global variables like they would be set with
|
||||
// `-ldflags="-X=runtime.osArgs=first\x00second`.
|
||||
// The runtime package has two variables (osArgs and osEnv) that are
|
||||
// both strings, from which the parameters and environment variables
|
||||
// are read.
|
||||
config.Options.GlobalValues = map[string]map[string]string{
|
||||
"runtime": runtimeGlobals,
|
||||
}
|
||||
}
|
||||
} else if config.EmulatorName() == "wasmtime" {
|
||||
// Wasmtime needs some special flags to pass environment variables
|
||||
// and allow reading from the current directory.
|
||||
args = append(args, "--dir=.")
|
||||
for _, v := range environmentVars {
|
||||
args = append(args, "--env", v)
|
||||
}
|
||||
if len(cmdArgs) != 0 {
|
||||
// mark end of wasmtime arguments and start of program ones: --
|
||||
args = append(args, "--")
|
||||
args = append(args, cmdArgs...)
|
||||
}
|
||||
} else {
|
||||
// Pass environment variables and command line parameters as usual.
|
||||
// This also works on qemu-aarch64 etc.
|
||||
args = cmdArgs
|
||||
env = environmentVars
|
||||
}
|
||||
|
||||
format, fileExt := config.EmulatorFormat()
|
||||
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||
// If needed, set a timeout on the command. This is done in tests so
|
||||
// they don't waste resources on a stalled test.
|
||||
var ctx context.Context
|
||||
if timeout != 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// Set up the command.
|
||||
var name string
|
||||
if config.Target.Emulator == "" {
|
||||
name = result.Binary
|
||||
} else {
|
||||
emulator, err := config.Emulator(format, result.Binary)
|
||||
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||
emulator := config.Emulator()
|
||||
if len(emulator) == 0 {
|
||||
// Run directly.
|
||||
cmd := executeCommand(config.Options, result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
||||
// Workaround for QEMU which always exits with an error.
|
||||
return nil
|
||||
}
|
||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
name = emulator[0]
|
||||
emuArgs := append([]string(nil), emulator[1:]...)
|
||||
args = append(emuArgs, args...)
|
||||
}
|
||||
var cmd *exec.Cmd
|
||||
if ctx != nil {
|
||||
cmd = exec.CommandContext(ctx, name, args...)
|
||||
return nil
|
||||
} else {
|
||||
cmd = exec.Command(name, args...)
|
||||
}
|
||||
cmd.Env = env
|
||||
|
||||
// Configure stdout/stderr. The stdout may go to a buffer, not a real
|
||||
// stdout.
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if config.EmulatorName() == "simavr" {
|
||||
cmd.Stdout = nil // don't print initial load commands
|
||||
cmd.Stderr = stdout
|
||||
}
|
||||
|
||||
// If this is a test, reserve CPU time for it so that increased
|
||||
// parallelism doesn't blow up memory usage. If this isn't a test but
|
||||
// simply `tinygo run`, then it is practically a no-op.
|
||||
config.Options.Semaphore <- struct{}{}
|
||||
defer func() {
|
||||
<-config.Options.Semaphore
|
||||
}()
|
||||
|
||||
// Run binary.
|
||||
if config.Options.PrintCommands != nil {
|
||||
config.Options.PrintCommands(cmd.Path, cmd.Args...)
|
||||
}
|
||||
err := run(cmd, result)
|
||||
if err != nil {
|
||||
if ctx != nil && ctx.Err() == context.DeadlineExceeded {
|
||||
stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout)))
|
||||
err = ctx.Err()
|
||||
// Run in an emulator.
|
||||
args := append(emulator[1:], result.Binary)
|
||||
cmd := executeCommand(config.Options, emulator[0], args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
|
||||
// Workaround for QEMU which always exits with an error.
|
||||
return nil
|
||||
}
|
||||
return &commandError{"failed to run emulator with", result.Binary, err}
|
||||
}
|
||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1090,8 +1012,8 @@ func getBMPPorts() (gdbPort, uartPort string, err error) {
|
||||
|
||||
func usage(command string) {
|
||||
version := goenv.Version
|
||||
if strings.HasSuffix(version, "-dev") && goenv.GitSha1 != "" {
|
||||
version += "-" + goenv.GitSha1
|
||||
if strings.HasSuffix(version, "-dev") && gitSha1 != "" {
|
||||
version += "-" + gitSha1
|
||||
}
|
||||
|
||||
switch command {
|
||||
@@ -1286,13 +1208,13 @@ func main() {
|
||||
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
|
||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||
|
||||
var flagJSON, flagDeps, flagTest bool
|
||||
if command == "help" || command == "list" || command == "info" || command == "build" {
|
||||
flag.BoolVar(&flagJSON, "json", false, "print data in JSON format")
|
||||
var flagJSON, flagDeps, flagTest *bool
|
||||
if command == "help" || command == "list" || command == "info" {
|
||||
flagJSON = flag.Bool("json", false, "print data in JSON format")
|
||||
}
|
||||
if command == "help" || command == "list" {
|
||||
flag.BoolVar(&flagDeps, "deps", false, "supply -deps flag to go list")
|
||||
flag.BoolVar(&flagTest, "test", false, "supply -test flag to go list")
|
||||
flagDeps = flag.Bool("deps", false, "supply -deps flag to go list")
|
||||
flagTest = flag.Bool("test", false, "supply -test flag to go list")
|
||||
}
|
||||
var outpath string
|
||||
if command == "help" || command == "build" || command == "build-library" || command == "test" {
|
||||
@@ -1369,7 +1291,6 @@ func main() {
|
||||
Programmer: *programmer,
|
||||
OpenOCDCommands: ocdCommands,
|
||||
LLVMFeatures: *llvmFeatures,
|
||||
PrintJSON: flagJSON,
|
||||
}
|
||||
if *printCommands {
|
||||
options.PrintCommands = printCommand
|
||||
@@ -1398,6 +1319,11 @@ func main() {
|
||||
|
||||
switch command {
|
||||
case "build":
|
||||
if outpath == "" {
|
||||
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
|
||||
usage(command)
|
||||
os.Exit(1)
|
||||
}
|
||||
pkgName := "."
|
||||
if flag.NArg() == 1 {
|
||||
pkgName = filepath.ToSlash(flag.Arg(0))
|
||||
@@ -1471,13 +1397,13 @@ func main() {
|
||||
handleCompilerError(err)
|
||||
}
|
||||
case "run":
|
||||
if flag.NArg() < 1 {
|
||||
if flag.NArg() != 1 {
|
||||
fmt.Fprintln(os.Stderr, "No package specified.")
|
||||
usage(command)
|
||||
os.Exit(1)
|
||||
}
|
||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||
err := Run(pkgName, options, flag.Args()[1:])
|
||||
err := Run(pkgName, options)
|
||||
handleCompilerError(err)
|
||||
case "test":
|
||||
var pkgNames []string
|
||||
@@ -1573,7 +1499,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
|
||||
// This doesn't look like a regular target file, but rather like
|
||||
// a parent target (such as targets/cortex-m.json).
|
||||
continue
|
||||
@@ -1606,7 +1532,7 @@ func main() {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if flagJSON {
|
||||
if *flagJSON {
|
||||
json, _ := json.MarshalIndent(struct {
|
||||
GOROOT string `json:"goroot"`
|
||||
GOOS string `json:"goos"`
|
||||
@@ -1645,13 +1571,13 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
var extraArgs []string
|
||||
if flagJSON {
|
||||
if *flagJSON {
|
||||
extraArgs = append(extraArgs, "-json")
|
||||
}
|
||||
if flagDeps {
|
||||
if *flagDeps {
|
||||
extraArgs = append(extraArgs, "-deps")
|
||||
}
|
||||
if flagTest {
|
||||
if *flagTest {
|
||||
extraArgs = append(extraArgs, "-test")
|
||||
}
|
||||
cmd, err := loader.List(config, extraArgs, flag.Args())
|
||||
@@ -1688,8 +1614,8 @@ func main() {
|
||||
goversion = s
|
||||
}
|
||||
version := goenv.Version
|
||||
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
|
||||
version += "-" + goenv.GitSha1
|
||||
if strings.HasSuffix(goenv.Version, "-dev") && gitSha1 != "" {
|
||||
version += "-" + gitSha1
|
||||
}
|
||||
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
||||
case "env":
|
||||
|
||||
+143
-48
@@ -6,6 +6,7 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -48,7 +50,6 @@ func TestBuild(t *testing.T) {
|
||||
"calls.go",
|
||||
"cgo/",
|
||||
"channel.go",
|
||||
"embed/",
|
||||
"float.go",
|
||||
"gc.go",
|
||||
"goroutines.go",
|
||||
@@ -65,6 +66,7 @@ func TestBuild(t *testing.T) {
|
||||
"stdlib.go",
|
||||
"string.go",
|
||||
"structs.go",
|
||||
"testing.go",
|
||||
"zeroalloc.go",
|
||||
}
|
||||
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
@@ -74,11 +76,6 @@ func TestBuild(t *testing.T) {
|
||||
if minor >= 17 {
|
||||
tests = append(tests, "go1.17.go")
|
||||
}
|
||||
if minor >= 18 {
|
||||
tests = append(tests, "testing_go118.go")
|
||||
} else {
|
||||
tests = append(tests, "testing.go")
|
||||
}
|
||||
|
||||
if *testTarget != "" {
|
||||
// This makes it possible to run one specific test (instead of all),
|
||||
@@ -147,7 +144,46 @@ func TestBuild(t *testing.T) {
|
||||
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
|
||||
// This bug is non-deterministic.
|
||||
t.Skip("skipped due to non-deterministic backend bugs")
|
||||
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
|
||||
|
||||
var avrTests []string
|
||||
for _, t := range tests {
|
||||
switch t {
|
||||
case "atomic.go":
|
||||
// Requires GCC 11.2.0 or above for interface comparison.
|
||||
// https://github.com/gcc-mirror/gcc/commit/f30dd607669212de135dec1f1d8a93b8954c327c
|
||||
|
||||
case "reflect.go":
|
||||
// Reflect tests do not work due to type code issues.
|
||||
|
||||
case "gc.go":
|
||||
// Does not pass due to high mark false positive rate.
|
||||
|
||||
case "json.go", "stdlib.go", "testing.go":
|
||||
// Breaks interp.
|
||||
|
||||
case "map.go":
|
||||
// Reflect size calculation crashes.
|
||||
|
||||
case "binop.go":
|
||||
// Interface comparison results are inverted.
|
||||
|
||||
case "channel.go":
|
||||
// Freezes after recv from closed channel.
|
||||
|
||||
case "float.go", "math.go", "print.go":
|
||||
// Stuck in runtime.printfloat64.
|
||||
|
||||
case "interface.go":
|
||||
// Several comparison tests fail.
|
||||
|
||||
case "cgo/":
|
||||
// CGo does not work on AVR.
|
||||
|
||||
default:
|
||||
avrTests = append(avrTests, t)
|
||||
}
|
||||
}
|
||||
runPlatTests(optionsFromTarget("simavr", sema), avrTests, t)
|
||||
})
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
@@ -179,44 +215,13 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||
}
|
||||
|
||||
for _, name := range tests {
|
||||
if options.Target == "simavr" {
|
||||
// Not all tests are currently supported on AVR.
|
||||
// Skip the ones that aren't.
|
||||
switch name {
|
||||
case "reflect.go":
|
||||
// Reflect tests do not work due to type code issues.
|
||||
continue
|
||||
|
||||
case "gc.go":
|
||||
// Does not pass due to high mark false positive rate.
|
||||
continue
|
||||
|
||||
case "json.go", "stdlib.go", "testing.go", "testing_go118.go":
|
||||
// Breaks interp.
|
||||
continue
|
||||
|
||||
case "channel.go":
|
||||
// Freezes after recv from closed channel.
|
||||
continue
|
||||
|
||||
case "math.go":
|
||||
// Stuck somewhere, not sure what's happening.
|
||||
continue
|
||||
|
||||
case "cgo/":
|
||||
// CGo does not work on AVR.
|
||||
continue
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
name := name // redefine to avoid race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(name, options, t, nil, nil)
|
||||
})
|
||||
}
|
||||
if !strings.HasPrefix(spec.Emulator, "simavr ") {
|
||||
if len(spec.Emulator) == 0 || spec.Emulator[0] != "simavr" {
|
||||
t.Run("env.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||
@@ -250,8 +255,8 @@ func emuCheck(t *testing.T, options compileopts.Options) {
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target spec:", err)
|
||||
}
|
||||
if spec.Emulator != "" {
|
||||
_, err := exec.LookPath(strings.SplitN(spec.Emulator, " ", 2)[0])
|
||||
if len(spec.Emulator) != 0 {
|
||||
_, err := exec.LookPath(spec.Emulator[0])
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
t.Skipf("emulator not installed: %q", spec.Emulator[0])
|
||||
@@ -315,27 +320,117 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||
t.Fatal("could not read expected output file:", err)
|
||||
}
|
||||
|
||||
config, err := builder.NewConfig(&options)
|
||||
// Create a temporary directory for test output files.
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
// Determine whether we're on a system that supports environment variables
|
||||
// and command line parameters (operating systems, WASI) or not (baremetal,
|
||||
// WebAssembly in the browser). If we're on a system without an environment,
|
||||
// we need to pass command line arguments and environment variables through
|
||||
// global variables (built into the binary directly) instead of the
|
||||
// conventional way.
|
||||
spec, err := compileopts.LoadTarget(&options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatal("failed to load target spec:", err)
|
||||
}
|
||||
needsEnvInVars := spec.GOOS == "js"
|
||||
for _, tag := range spec.BuildTags {
|
||||
if tag == "baremetal" {
|
||||
needsEnvInVars = true
|
||||
}
|
||||
}
|
||||
if needsEnvInVars {
|
||||
runtimeGlobals := make(map[string]string)
|
||||
if len(cmdArgs) != 0 {
|
||||
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
|
||||
}
|
||||
if len(environmentVars) != 0 {
|
||||
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
|
||||
}
|
||||
if len(runtimeGlobals) != 0 {
|
||||
// This sets the global variables like they would be set with
|
||||
// `-ldflags="-X=runtime.osArgs=first\x00second`.
|
||||
// The runtime package has two variables (osArgs and osEnv) that are
|
||||
// both strings, from which the parameters and environment variables
|
||||
// are read.
|
||||
options.GlobalValues = map[string]map[string]string{
|
||||
"runtime": runtimeGlobals,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the test binary.
|
||||
stdout := &bytes.Buffer{}
|
||||
err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||
return cmd.Run()
|
||||
})
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
if spec.GOOS == "windows" {
|
||||
binary += ".exe"
|
||||
}
|
||||
err = Build("./"+path, binary, &options)
|
||||
if err != nil {
|
||||
printCompilerError(t.Log, err)
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
// Reserve CPU time for the test to run.
|
||||
// This attempts to ensure that the test is not CPU-starved.
|
||||
options.Semaphore <- struct{}{}
|
||||
defer func() { <-options.Semaphore }()
|
||||
|
||||
// Create the test command, taking care of emulators etc.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
var cmd *exec.Cmd
|
||||
|
||||
// make sure any special vars in the emulator definition are rewritten
|
||||
config := compileopts.Config{Target: spec}
|
||||
emulator := config.Emulator()
|
||||
|
||||
if len(emulator) == 0 {
|
||||
cmd = exec.CommandContext(ctx, binary)
|
||||
} else {
|
||||
args := append(emulator[1:], binary)
|
||||
cmd = exec.CommandContext(ctx, emulator[0], args...)
|
||||
}
|
||||
|
||||
if len(emulator) != 0 && emulator[0] == "wasmtime" {
|
||||
// Allow reading from the current directory.
|
||||
cmd.Args = append(cmd.Args, "--dir=.")
|
||||
for _, v := range environmentVars {
|
||||
cmd.Args = append(cmd.Args, "--env", v)
|
||||
}
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
if !needsEnvInVars {
|
||||
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
stdout := &bytes.Buffer{}
|
||||
if len(emulator) != 0 && emulator[0] == "simavr" {
|
||||
cmd.Stdout = os.Stderr
|
||||
cmd.Stderr = stdout
|
||||
} else {
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatal("failed to start:", err)
|
||||
}
|
||||
err = cmd.Wait()
|
||||
|
||||
if cerr := ctx.Err(); cerr == context.DeadlineExceeded {
|
||||
stdout.WriteString("--- test ran too long, terminating...\n")
|
||||
err = cerr
|
||||
}
|
||||
|
||||
// putchar() prints CRLF, convert it to LF.
|
||||
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
||||
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
|
||||
|
||||
if config.EmulatorName() == "simavr" {
|
||||
if len(emulator) != 0 && emulator[0] == "simavr" {
|
||||
// Strip simavr log formatting.
|
||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
|
||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
package nxp
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -379,6 +378,30 @@ func (clk Clock) setCcm(value uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func setSysPfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
func setUsb1Pfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// PLL configuration for ARM
|
||||
type ClockConfigArmPll struct {
|
||||
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
|
||||
@@ -449,178 +472,59 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
|
||||
setSysPfd(pfd...)
|
||||
}
|
||||
|
||||
func setSysPfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// PHY configuration for USB HS
|
||||
type ClockConfigUsbPhy struct {
|
||||
Instance uint8 // USB PHY number (1 or 2)
|
||||
XtalFreq uint32 // External reference clock frequency (Hz)
|
||||
DCal uint32 // Decode to trim nominal 17.78mA current source
|
||||
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
|
||||
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
|
||||
PllConfig ClockConfigUsbPll
|
||||
}
|
||||
|
||||
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
|
||||
// the USB +3V regulator (PMU), for use as either USB host or device.
|
||||
func (cfg ClockConfigUsbPhy) Configure() {
|
||||
|
||||
var (
|
||||
usb *USB_Type
|
||||
phy *USBPHY_Type
|
||||
chrgDetectReg *volatile.Register32
|
||||
chrgDetectMsk uint32
|
||||
)
|
||||
|
||||
// Select appropriate peripherals based on receiver Instance
|
||||
switch cfg.Instance {
|
||||
case 1:
|
||||
usb = USB1 // Select USB1 HS PHY/PLL
|
||||
phy = USBPHY1 //
|
||||
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
|
||||
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
|
||||
case 2:
|
||||
usb = USB2 // Select USB2 HS PHY/PLL
|
||||
phy = USBPHY2 //
|
||||
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
|
||||
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
|
||||
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
|
||||
default:
|
||||
panic("nxp: invalid USB PHY")
|
||||
}
|
||||
|
||||
// Configure and enable USB PLL clocks
|
||||
cfg.PllConfig.Configure()
|
||||
|
||||
// Release PHY from reset
|
||||
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
|
||||
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
|
||||
|
||||
// Enable power to USB PHY
|
||||
phy.PWD.Set(0)
|
||||
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
|
||||
// enable support for low-speed device connection, direct and indirect (hub)
|
||||
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
|
||||
|
||||
// Enable USB HS clocks gate
|
||||
ClockIpUsbOh3.Enable(true)
|
||||
// Reset USB peripheral
|
||||
usb.USBCMD.SetBits(USB_USBCMD_RST)
|
||||
|
||||
// Add a delay after RST to ensure there is a USB D+ pullup sequence
|
||||
nopDelay(400000)
|
||||
|
||||
// Enable USB LDO
|
||||
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
|
||||
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
|
||||
|
||||
// check whether we are connected to USB charger
|
||||
chrgDetectReg.Set(chrgDetectMsk)
|
||||
|
||||
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
|
||||
phy.TX.Set((phy.TX.Get() &
|
||||
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
|
||||
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
|
||||
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
|
||||
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
|
||||
}
|
||||
|
||||
// PLL configuration for USB
|
||||
type ClockConfigUsbPll struct {
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
|
||||
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
|
||||
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
||||
}
|
||||
|
||||
func (cfg ClockConfigUsbPll) Configure() {
|
||||
// select USB peripheral registers based on receiver's Instance
|
||||
switch cfg.Instance {
|
||||
case 1: // USB1 PLL
|
||||
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
|
||||
// PLL already configured, enable USB clocks
|
||||
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
|
||||
} else {
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS | src)
|
||||
// reconfigure PLL
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
|
||||
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
|
||||
}
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
|
||||
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
|
||||
|
||||
// update PFDs (if provided)
|
||||
if nil != cfg.Pfd {
|
||||
setUsb1Pfd(cfg.Pfd...)
|
||||
}
|
||||
switch cfg.Instance {
|
||||
case 1:
|
||||
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB1_SET.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
|
||||
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
|
||||
}
|
||||
case 2: // USB2 PLL
|
||||
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
|
||||
// PLL already configured, enable USB clocks
|
||||
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
|
||||
} else {
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS | src)
|
||||
// reconfigure PLL
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
|
||||
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
|
||||
}
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
|
||||
|
||||
// update PFDs after update
|
||||
setUsb1Pfd(pfd...)
|
||||
|
||||
case 2:
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
|
||||
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
|
||||
|
||||
default:
|
||||
panic("nxp: invalid USB PLL")
|
||||
}
|
||||
}
|
||||
|
||||
func setUsb1Pfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
||||
// an approximate method to spin CPU cycles for short periods of time.
|
||||
// go:inline
|
||||
func nopDelay(cycles uint32) {
|
||||
for i := uint32(0); i < cycles; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,14 +142,18 @@ func (mpu *MPU_Type) Enable(enable bool) {
|
||||
if enable {
|
||||
mpu.CTRL.Set(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk)
|
||||
SystemControl.SHCSR.SetBits(SCB_SHCSR_MEMFAULTENA_Msk)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
enableDcache(true)
|
||||
enableIcache(true)
|
||||
} else {
|
||||
enableIcache(false)
|
||||
enableDcache(false)
|
||||
arm.Asm("dmb 0xF")
|
||||
arm.AsmFull(`
|
||||
dmb 0xF
|
||||
`, nil)
|
||||
SystemControl.SHCSR.ClearBits(SCB_SHCSR_MEMFAULTENA_Msk)
|
||||
mpu.CTRL.ClearBits(MPU_CTRL_ENABLE_Msk)
|
||||
}
|
||||
@@ -184,21 +188,31 @@ func (mpu *MPU_Type) SetRASR(size RegionSize, access AccessPerms, ext Extension,
|
||||
func enableIcache(enable bool) {
|
||||
if enable != SystemControl.CCR.HasBits(SCB_CCR_IC_Msk) {
|
||||
if enable {
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
SystemControl.ICIALLU.Set(0)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
SystemControl.CCR.SetBits(SCB_CCR_IC_Msk)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
} else {
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
|
||||
SystemControl.ICIALLU.Set(0)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +227,9 @@ func enableDcache(enable bool) {
|
||||
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
|
||||
if enable {
|
||||
SystemControl.CSSELR.Set(0)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
ccsidr := SystemControl.CCSIDR.Get()
|
||||
sets := (ccsidr & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos
|
||||
for sets != 0 {
|
||||
@@ -226,15 +242,23 @@ func enableDcache(enable bool) {
|
||||
}
|
||||
sets--
|
||||
}
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
SystemControl.CCR.SetBits(SCB_CCR_DC_Msk)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
} else {
|
||||
SystemControl.CSSELR.Set(0)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
SystemControl.CCR.ClearBits(SCB_CCR_DC_Msk)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
dcacheCcsidr.Set(SystemControl.CCSIDR.Get())
|
||||
dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
|
||||
for dcacheSets.Get() != 0 {
|
||||
@@ -247,88 +271,10 @@ func enableDcache(enable bool) {
|
||||
}
|
||||
dcacheSets.Set(dcacheSets.Get() - 1)
|
||||
}
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FlushDcache flushes data from cache to memory
|
||||
//
|
||||
// Normally FlushDcache is used when metadata written to memory will be used by
|
||||
// a DMA or a bus-controller peripheral. Any data in the cache is written to
|
||||
// memory. A copy remains in the cache, so this is typically used with special
|
||||
// fields you will want to quickly access in the future. For data transmission,
|
||||
// use FlushDeleteDcache.
|
||||
//go:inline
|
||||
func FlushDcache(addr, size uintptr) {
|
||||
location := addr & 0xFFFFFFE0
|
||||
endAddr := addr + size
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
for {
|
||||
SystemControl.DCCMVAC.Set(uint32(location))
|
||||
location += 32
|
||||
if location >= endAddr {
|
||||
break
|
||||
}
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
|
||||
// DeleteDcache deletes data from the cache, without touching memory.
|
||||
//
|
||||
// Normally DeleteDcache is used before receiving data via DMA or from
|
||||
// bus-controller peripherals which write to memory. You want to delete anything
|
||||
// the cache may have stored, so your next read is certain to access the
|
||||
// physical memory.
|
||||
//go:inline
|
||||
func DeleteDcache(addr, size uintptr) {
|
||||
location := addr & 0xFFFFFFE0
|
||||
endAddr := addr + size
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
for {
|
||||
SystemControl.DCIMVAC.Set(uint32(location))
|
||||
location += 32
|
||||
if location >= endAddr {
|
||||
break
|
||||
}
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
|
||||
// FlushDeleteDcache flushes data from cache to memory, and delete it from the
|
||||
// cache
|
||||
//
|
||||
// Normally FlushDeleteDcache is used when transmitting data via DMA or
|
||||
// bus-controller peripherals which read from memory. You want any cached data
|
||||
// written to memory, and then removed from the cache, because you no longer
|
||||
// need to access the data after transmission.
|
||||
//go:inline
|
||||
func FlushDeleteDcache(addr, size uintptr) {
|
||||
location := addr & 0xFFFFFFE0
|
||||
endAddr := addr + size
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
for {
|
||||
SystemControl.DCCIMVAC.Set(uint32(location))
|
||||
location += 32
|
||||
if location >= endAddr {
|
||||
break
|
||||
}
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Reads multiple rp2040 ADC channels concurrently. Including the internal temperature sensor
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
type celsius float32
|
||||
|
||||
func (c celsius) String() string {
|
||||
return fmt.Sprintf("%4.1f℃", c)
|
||||
}
|
||||
|
||||
// rp2040 ADC is 12 bits. Reading are shifted <<4 to fill the 16-bit range.
|
||||
var adcReading [3]uint16
|
||||
|
||||
func readADC(a machine.ADC, w time.Duration, i int) {
|
||||
for {
|
||||
adcReading[i] = a.Get()
|
||||
time.Sleep(w)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
machine.InitADC()
|
||||
a0 := machine.ADC{machine.ADC0} // GPIO26 input
|
||||
a1 := machine.ADC{machine.ADC1} // GPIO27 input
|
||||
a2 := machine.ADC{machine.ADC2} // GPIO28 input
|
||||
t := machine.ADC_TEMP_SENSOR // Internal Temperature sensor
|
||||
// Configure sets the GPIOs to PinAnalog mode
|
||||
a0.Configure(machine.ADCConfig{})
|
||||
a1.Configure(machine.ADCConfig{})
|
||||
a2.Configure(machine.ADCConfig{})
|
||||
// Configure powers on the temperature sensor
|
||||
t.Configure(machine.ADCConfig{})
|
||||
|
||||
// Safe to read concurrently
|
||||
go readADC(a0, 10*time.Millisecond, 0)
|
||||
go readADC(a1, 17*time.Millisecond, 1)
|
||||
go readADC(a2, 29*time.Millisecond, 2)
|
||||
|
||||
for {
|
||||
fmt.Printf("ADC0: %5d ADC1: %5d ADC2: %5d Temp: %v\n\r", adcReading[0], adcReading[1], adcReading[2], celsius(float32(t.ReadTemperature())/1000))
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb"
|
||||
"time"
|
||||
)
|
||||
|
||||
var keyboard = machine.USB.Keyboard()
|
||||
|
||||
func main() {
|
||||
|
||||
for !machine.USB.Ready() {
|
||||
}
|
||||
|
||||
println("USB HID keyboard demo")
|
||||
|
||||
for {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Open a new text editor
|
||||
keyboard.Down(usb.KeyModifierAlt)
|
||||
keyboard.Press(usb.KeySpace)
|
||||
keyboard.Up(usb.KeyModifierAlt)
|
||||
time.Sleep(2 * time.Second)
|
||||
keyboard.Write([]byte("kate"))
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Use the io.Writer interface
|
||||
keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n"))
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Or manually specify keycodes and Unicode codepoints
|
||||
testKeys([]Key{
|
||||
// Print alphabet out-of-order
|
||||
{Press: usb.KeyX},
|
||||
{Press: usb.KeyY},
|
||||
{Press: usb.KeyZ},
|
||||
{Press: usb.KeyG},
|
||||
{Press: usb.KeyH},
|
||||
{Press: usb.KeyI},
|
||||
{Press: usb.KeyJ},
|
||||
{Press: usb.KeyK},
|
||||
{Press: usb.KeyL},
|
||||
{Press: usb.KeyM},
|
||||
{Press: usb.KeyN},
|
||||
{Press: usb.KeyO},
|
||||
{Press: usb.KeyP},
|
||||
{Press: usb.KeyQ},
|
||||
{Press: usb.KeyR},
|
||||
{Press: usb.KeyS},
|
||||
{Press: usb.KeyT},
|
||||
{Press: usb.KeyA},
|
||||
{Press: usb.KeyB},
|
||||
{Press: usb.KeyC},
|
||||
{Press: usb.KeyD},
|
||||
{Press: usb.KeyE},
|
||||
{Press: usb.KeyF},
|
||||
{Press: usb.KeyU},
|
||||
{Press: usb.KeyV},
|
||||
{Press: usb.KeyW},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Move cursor left x3
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Highlight 6 symbols to the left
|
||||
{Down: usb.KeyModifierShift},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Press: usb.KeyLeft},
|
||||
{Up: usb.KeyModifierShift},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Use Ctrl-X to cut
|
||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Move to beginning of line
|
||||
{Press: usb.KeyHome},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Use Ctrl-V to paste
|
||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Highlight 3 symbols to the right
|
||||
{Down: usb.KeyModifierShift},
|
||||
{Press: usb.KeyRight},
|
||||
{Press: usb.KeyRight},
|
||||
{Press: usb.KeyRight},
|
||||
{Up: usb.KeyModifierShift},
|
||||
// Use Ctrl-X to cut
|
||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Move to end of line
|
||||
{Press: usb.KeyEnd},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Use Ctrl-V to paste
|
||||
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
|
||||
// Pause 1 second
|
||||
{Time: time.Second},
|
||||
// Newline
|
||||
{Press: usb.KeyEnter},
|
||||
{Press: usb.KeyEnter},
|
||||
}, 150*time.Millisecond)
|
||||
|
||||
// Highlight all text and delete
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
keyboard.Press(usb.KeyA)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyDelete)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Close window
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
keyboard.Press(usb.KeyQ)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
time.Sleep(2 * time.Second)
|
||||
// Confirm discard file
|
||||
keyboard.Down(usb.KeyModifierAlt)
|
||||
keyboard.Press(usb.KeyD)
|
||||
keyboard.Up(usb.KeyModifierAlt)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Open a new terminal
|
||||
keyboard.Down(usb.KeyModifierAlt)
|
||||
keyboard.Press(usb.KeySpace)
|
||||
keyboard.Up(usb.KeyModifierAlt)
|
||||
time.Sleep(2 * time.Second)
|
||||
keyboard.Write([]byte("konsole"))
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// Open serial connection (GNU screen)
|
||||
keyboard.Write([]byte("screen /dev/ttyACM0 115200"))
|
||||
time.Sleep(2 * time.Second)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(4 * time.Second)
|
||||
|
||||
// Write to UART
|
||||
keyboard.Write([]byte("hello!"))
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(2 * time.Second)
|
||||
keyboard.Write([]byte("NO U"))
|
||||
time.Sleep(time.Second)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Close serial connection (GNU screen)
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
// Ctrl-X is the prefix sequence in my GNU screen configuration
|
||||
keyboard.Press(usb.KeyX)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
time.Sleep(time.Second)
|
||||
// Backslash (Prefix-\) is the GNU screen command to kill window
|
||||
keyboard.Press(usb.KeyBackslash)
|
||||
time.Sleep(time.Second)
|
||||
// Confirm "Kill window (Y/N)?" prompt
|
||||
keyboard.Press(usb.KeyY)
|
||||
keyboard.Press(usb.KeyEnter)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Close terminal
|
||||
keyboard.Down(usb.KeyModifierCtrl)
|
||||
// Ctrl-D exits the shell (sends a resemblance of EOF, I believe?)
|
||||
keyboard.Press(usb.KeyD)
|
||||
keyboard.Up(usb.KeyModifierCtrl)
|
||||
|
||||
time.Sleep(25 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
Press usb.Keycode
|
||||
Down usb.Keycode
|
||||
Up usb.Keycode
|
||||
Time time.Duration
|
||||
}
|
||||
|
||||
func testKeys(key []Key, delay time.Duration) {
|
||||
for _, k := range key {
|
||||
if 0 != k.Down {
|
||||
keyboard.Down(k.Down)
|
||||
}
|
||||
if 0 != k.Press {
|
||||
keyboard.Press(k.Press)
|
||||
}
|
||||
if 0 != k.Up {
|
||||
keyboard.Up(k.Up)
|
||||
}
|
||||
if 0 != k.Time {
|
||||
time.Sleep(k.Time)
|
||||
} else {
|
||||
time.Sleep(delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testInternationalLayout() {
|
||||
// International keyboard layouts also supported
|
||||
keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Numbers: 0123456789\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬®¯°±\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Cedilla: Çç\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Ring Above: Åå\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("AE: Ææ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Thorn: Þþ\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Sharp S: ß\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("O-Stroke: Øø\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Eth: Ðð\n"))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
keyboard.Write([]byte("Euro: €\n"))
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// This is a echo console running on the device UART.
|
||||
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
uart := machine.Serial
|
||||
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
|
||||
|
||||
input := make([]byte, 4096)
|
||||
i := 0
|
||||
for {
|
||||
if uart.Buffered() > 0 {
|
||||
data, _ := uart.ReadByte()
|
||||
|
||||
switch data {
|
||||
case 13:
|
||||
// return key
|
||||
uart.Write([]byte("\r\n"))
|
||||
uart.Write([]byte("You typed: "))
|
||||
uart.Write(input[:i])
|
||||
uart.Write([]byte("\r\n"))
|
||||
i = 0
|
||||
default:
|
||||
// just echo the character
|
||||
uart.WriteByte(data)
|
||||
input[i] = data
|
||||
i++
|
||||
}
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
// Package fuzz is a shim to allow compilation against Go 1.18.
|
||||
// It only defines a single type to work with testing/internal/testdeps,
|
||||
// and hide all the other new dependencies from this package.
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CorpusEntry represents an individual input for fuzzing.
|
||||
//
|
||||
// We must use an equivalent type in the testing and testing/internal/testdeps
|
||||
// packages, but testing can't import this package directly, and we don't want
|
||||
// to export this type from testing. Instead, we use the same struct type and
|
||||
// use a type alias (not a defined type) for convenience.
|
||||
type CorpusEntry = struct {
|
||||
Parent string
|
||||
|
||||
// Path is the path of the corpus file, if the entry was loaded from disk.
|
||||
// For other entries, including seed values provided by f.Add, Path is the
|
||||
// name of the test, e.g. seed#0 or its hash.
|
||||
Path string
|
||||
|
||||
// Data is the raw input data. Data should only be populated for seed
|
||||
// values. For on-disk corpus files, Data will be nil, as it will be loaded
|
||||
// from disk using Path.
|
||||
Data []byte
|
||||
|
||||
// Values is the unmarshaled values from a corpus file.
|
||||
Values []any
|
||||
|
||||
Generation int
|
||||
|
||||
// IsSeed indicates whether this entry is part of the seed corpus.
|
||||
IsSeed bool
|
||||
}
|
||||
|
||||
// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing.
|
||||
// The zero value is valid for each field unless specified otherwise.
|
||||
type CoordinateFuzzingOpts struct {
|
||||
// Log is a writer for logging progress messages and warnings.
|
||||
// If nil, io.Discard will be used instead.
|
||||
Log io.Writer
|
||||
|
||||
// Timeout is the amount of wall clock time to spend fuzzing after the corpus
|
||||
// has loaded. If zero, there will be no time limit.
|
||||
Timeout time.Duration
|
||||
|
||||
// Limit is the number of random values to generate and test. If zero,
|
||||
// there will be no limit on the number of generated values.
|
||||
Limit int64
|
||||
|
||||
// MinimizeTimeout is the amount of wall clock time to spend minimizing
|
||||
// after discovering a crasher. If zero, there will be no time limit. If
|
||||
// MinimizeTimeout and MinimizeLimit are both zero, then minimization will
|
||||
// be disabled.
|
||||
MinimizeTimeout time.Duration
|
||||
|
||||
// MinimizeLimit is the maximum number of calls to the fuzz function to be
|
||||
// made while minimizing after finding a crash. If zero, there will be no
|
||||
// limit. Calls to the fuzz function made when minimizing also count toward
|
||||
// Limit. If MinimizeTimeout and MinimizeLimit are both zero, then
|
||||
// minimization will be disabled.
|
||||
MinimizeLimit int64
|
||||
|
||||
// parallel is the number of worker processes to run in parallel. If zero,
|
||||
// CoordinateFuzzing will run GOMAXPROCS workers.
|
||||
Parallel int
|
||||
|
||||
// Seed is a list of seed values added by the fuzz target with testing.F.Add
|
||||
// and in testdata.
|
||||
Seed []CorpusEntry
|
||||
|
||||
// Types is the list of types which make up a corpus entry.
|
||||
// Types must be set and must match values in Seed.
|
||||
Types []reflect.Type
|
||||
|
||||
// CorpusDir is a directory where files containing values that crash the
|
||||
// code being tested may be written. CorpusDir must be set.
|
||||
CorpusDir string
|
||||
|
||||
// CacheDir is a directory containing additional "interesting" values.
|
||||
// The fuzzer may derive new values from these, and may write new values here.
|
||||
CacheDir string
|
||||
}
|
||||
|
||||
// CoordinateFuzzing creates several worker processes and communicates with
|
||||
// them to test random inputs that could trigger crashes and expose bugs.
|
||||
// The worker processes run the same binary in the same directory with the
|
||||
// same environment variables as the coordinator process. Workers also run
|
||||
// with the same arguments as the coordinator, except with the -test.fuzzworker
|
||||
// flag prepended to the argument list.
|
||||
//
|
||||
// If a crash occurs, the function will return an error containing information
|
||||
// about the crash, which can be reported to the user.
|
||||
func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// ReadCorpus reads the corpus from the provided dir. The returned corpus
|
||||
// entries are guaranteed to match the given types. Any malformed files will
|
||||
// be saved in a MalformedCorpusError and returned, along with the most recent
|
||||
// error.
|
||||
func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// CheckCorpus verifies that the types in vals match the expected types
|
||||
// provided.
|
||||
func CheckCorpus(vals []any, types []reflect.Type) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func ResetCoverage() {}
|
||||
func SnapshotCoverage() {}
|
||||
|
||||
// RunFuzzWorker is called in a worker process to communicate with the
|
||||
// coordinator process in order to fuzz random inputs. RunFuzzWorker loops
|
||||
// until the coordinator tells it to stop.
|
||||
//
|
||||
// fn is a wrapper on the fuzz function. It may return an error to indicate
|
||||
// a given input "crashed". The coordinator will also record a crasher if
|
||||
// the function times out or terminates the process.
|
||||
//
|
||||
// RunFuzzWorker returns an error if it could not communicate with the
|
||||
// coordinator process.
|
||||
func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
#ifdef __MACH__
|
||||
.global _tinygo_startTask
|
||||
_tinygo_startTask:
|
||||
#else
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
#endif
|
||||
.cfi_startproc
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
@@ -28,25 +23,13 @@ tinygo_startTask:
|
||||
blr x19
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
#ifdef __MACH__
|
||||
b _tinygo_pause
|
||||
#else
|
||||
b tinygo_pause
|
||||
#endif
|
||||
.cfi_endproc
|
||||
#ifndef __MACH__
|
||||
.size tinygo_startTask, .-tinygo_startTask
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __MACH__
|
||||
.global _tinygo_swapTask
|
||||
_tinygo_swapTask:
|
||||
#else
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
#endif
|
||||
// This function gets the following parameters:
|
||||
// x0 = newStack uintptr
|
||||
// x1 = oldStack *uintptr
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
//go:build badger2040
|
||||
// +build badger2040
|
||||
|
||||
// This contains the pin mappings for the Badger 2040 Connect board.
|
||||
//
|
||||
// For more information, see: https://shop.pimoroni.com/products/badger-2040
|
||||
// Also
|
||||
// - Badger 2040 schematic: https://cdn.shopify.com/s/files/1/0174/1800/files/badger_2040_schematic.pdf?v=1645702148
|
||||
//
|
||||
package machine
|
||||
|
||||
const (
|
||||
LED Pin = GPIO25
|
||||
|
||||
BUTTON_A Pin = GPIO12
|
||||
BUTTON_B Pin = GPIO13
|
||||
BUTTON_C Pin = GPIO14
|
||||
BUTTON_UP Pin = GPIO15
|
||||
BUTTON_DOWN Pin = GPIO11
|
||||
BUTTON_USER Pin = GPIO23
|
||||
|
||||
EPD_BUSY_PIN Pin = GPIO26
|
||||
EPD_RESET_PIN Pin = GPIO21
|
||||
EPD_DC_PIN Pin = GPIO20
|
||||
EPD_CS_PIN Pin = GPIO17
|
||||
EPD_SCK_PIN Pin = GPIO18
|
||||
EPD_SDO_PIN Pin = GPIO19
|
||||
|
||||
VBUS_DETECT Pin = GPIO24
|
||||
BATTERY Pin = GPIO29
|
||||
ENABLE_3V3 Pin = GPIO10
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
I2C0_SDA_PIN Pin = GPIO4
|
||||
I2C0_SCL_PIN Pin = GPIO5
|
||||
|
||||
I2C1_SDA_PIN Pin = NoPin
|
||||
I2C1_SCL_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// SPI pins.
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = GPIO18
|
||||
SPI0_SDO_PIN Pin = GPIO19
|
||||
SPI0_SDI_PIN Pin = GPIO16
|
||||
|
||||
SPI1_SCK_PIN Pin = NoPin
|
||||
SPI1_SDO_PIN Pin = NoPin
|
||||
SPI1_SDI_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// QSPI pins¿?
|
||||
const (
|
||||
/* TODO
|
||||
SPI0_SD0_PIN Pin = QSPI_SD0
|
||||
SPI0_SD1_PIN Pin = QSPI_SD1
|
||||
SPI0_SD2_PIN Pin = QSPI_SD2
|
||||
SPI0_SD3_PIN Pin = QSPI_SD3
|
||||
SPI0_SCK_PIN Pin = QSPI_SCLKGPIO6
|
||||
SPI0_CS_PIN Pin = QSPI_CS
|
||||
|
||||
*/
|
||||
)
|
||||
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
const (
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Badger 2040"
|
||||
usb_STRING_MANUFACTURER = "Pimoroni"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2e8a
|
||||
usb_PID uint16 = 0x0003
|
||||
)
|
||||
@@ -1,49 +0,0 @@
|
||||
//go:build esp32c312f
|
||||
// +build esp32c312f
|
||||
|
||||
package machine
|
||||
|
||||
// Built-in RGB LED
|
||||
const (
|
||||
LED_RED = IO3
|
||||
LED_GREEN = IO4
|
||||
LED_BLUE = IO5
|
||||
LED = LED_RED
|
||||
)
|
||||
|
||||
const (
|
||||
IO0 Pin = 0
|
||||
IO1 Pin = 1
|
||||
IO10 Pin = 10
|
||||
IO18 Pin = 18
|
||||
IO19 Pin = 19
|
||||
IO2 Pin = 2
|
||||
IO3 Pin = 3
|
||||
IO4 Pin = 4
|
||||
IO5 Pin = 5
|
||||
IO6 Pin = 6
|
||||
IO7 Pin = 7
|
||||
IO8 Pin = 8
|
||||
IO9 Pin = 9
|
||||
RXD Pin = 20
|
||||
TXD Pin = 21
|
||||
)
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC0 Pin = ADC1_0
|
||||
ADC1 Pin = ADC2_0
|
||||
|
||||
ADC1_0 Pin = IO0
|
||||
ADC1_1 Pin = IO1
|
||||
ADC1_2 Pin = IO2
|
||||
ADC1_3 Pin = IO3
|
||||
ADC1_4 Pin = IO4
|
||||
ADC2_0 Pin = IO5
|
||||
)
|
||||
|
||||
// UART0 pins
|
||||
const (
|
||||
UART_TX_PIN = TXD
|
||||
UART_RX_PIN = RXD
|
||||
)
|
||||
@@ -42,9 +42,6 @@ const (
|
||||
SPIQ = IO17
|
||||
U0RXD = IO20
|
||||
U0TXD = IO21
|
||||
|
||||
UART_TX_PIN = U0TXD
|
||||
UART_RX_PIN = U0RXD
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
//go:build nrf52840 || circuitplay_bluefruit || reelboard || clue || itsybitsy_nrf52840
|
||||
// +build nrf52840 circuitplay_bluefruit reelboard clue itsybitsy_nrf52840
|
||||
|
||||
package machine
|
||||
|
||||
// Hardware pins
|
||||
const (
|
||||
P0_00 Pin = 0
|
||||
P0_01 Pin = 1
|
||||
P0_02 Pin = 2
|
||||
P0_03 Pin = 3
|
||||
P0_04 Pin = 4
|
||||
P0_05 Pin = 5
|
||||
P0_06 Pin = 6
|
||||
P0_07 Pin = 7
|
||||
P0_08 Pin = 8
|
||||
P0_09 Pin = 9
|
||||
P0_10 Pin = 10
|
||||
P0_11 Pin = 11
|
||||
P0_12 Pin = 12
|
||||
P0_13 Pin = 13
|
||||
P0_14 Pin = 14
|
||||
P0_15 Pin = 15
|
||||
P0_16 Pin = 16
|
||||
P0_17 Pin = 17
|
||||
P0_18 Pin = 18
|
||||
P0_19 Pin = 19
|
||||
P0_20 Pin = 20
|
||||
P0_21 Pin = 21
|
||||
P0_22 Pin = 22
|
||||
P0_23 Pin = 23
|
||||
P0_24 Pin = 24
|
||||
P0_25 Pin = 25
|
||||
P0_26 Pin = 26
|
||||
P0_27 Pin = 27
|
||||
P0_28 Pin = 28
|
||||
P0_29 Pin = 29
|
||||
P0_30 Pin = 30
|
||||
P0_31 Pin = 31
|
||||
P1_00 Pin = 32
|
||||
P1_01 Pin = 33
|
||||
P1_02 Pin = 34
|
||||
P1_03 Pin = 35
|
||||
P1_04 Pin = 36
|
||||
P1_05 Pin = 37
|
||||
P1_06 Pin = 38
|
||||
P1_07 Pin = 39
|
||||
P1_08 Pin = 40
|
||||
P1_09 Pin = 41
|
||||
P1_10 Pin = 42
|
||||
P1_11 Pin = 43
|
||||
P1_12 Pin = 44
|
||||
P1_13 Pin = 45
|
||||
P1_14 Pin = 46
|
||||
P1_15 Pin = 47
|
||||
)
|
||||
@@ -5,7 +5,6 @@ package machine
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"machine/usb"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
@@ -57,21 +56,21 @@ const (
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
// = Pin // Dig [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
|
||||
// = Pin // Dig | [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
|
||||
)
|
||||
|
||||
// Default peripheral pins
|
||||
@@ -106,15 +105,6 @@ func init() {
|
||||
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
|
||||
}
|
||||
|
||||
// #=====================================================#
|
||||
// | USB |
|
||||
// #=====================================================#
|
||||
var (
|
||||
UART0 = usb.UART{Port: 0}
|
||||
// HID0 = usb.HID{Port: 0}
|
||||
// UART0 = &UART1
|
||||
)
|
||||
|
||||
// #=====================================================#
|
||||
// | UART |
|
||||
// #===========#===========#=============#===============#
|
||||
@@ -247,8 +237,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// #==================================================================#
|
||||
// | SPI |
|
||||
// #===========#==========#===============#===========================#
|
||||
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
|
||||
// #===========#==========#===============#=================-=========#
|
||||
@@ -273,70 +261,6 @@ const (
|
||||
SPI3_CS_PIN = D36
|
||||
)
|
||||
|
||||
var (
|
||||
SPI0 = SPI1 // SPI0 is an alias of SPI1 (LPSPI4)
|
||||
SPI1 = &_SPI1
|
||||
_SPI1 = SPI{
|
||||
Bus: nxp.LPSPI4,
|
||||
muxSDI: muxSelect{ // D12 (PB1 [B0_01])
|
||||
mux: nxp.IOMUXC_LPSPI4_SDI_SELECT_INPUT_DAISY_GPIO_B0_01_ALT3,
|
||||
sel: &nxp.IOMUXC.LPSPI4_SDI_SELECT_INPUT,
|
||||
},
|
||||
muxSDO: muxSelect{ // D11 (PB2 [B0_02])
|
||||
mux: nxp.IOMUXC_LPSPI4_SDO_SELECT_INPUT_DAISY_GPIO_B0_02_ALT3,
|
||||
sel: &nxp.IOMUXC.LPSPI4_SDO_SELECT_INPUT,
|
||||
},
|
||||
muxSCK: muxSelect{ // D13 (PB3 [B0_03])
|
||||
mux: nxp.IOMUXC_LPSPI4_SCK_SELECT_INPUT_DAISY_GPIO_B0_03_ALT3,
|
||||
sel: &nxp.IOMUXC.LPSPI4_SCK_SELECT_INPUT,
|
||||
},
|
||||
muxCS: muxSelect{ // D10 (PB0 [B0_00])
|
||||
mux: nxp.IOMUXC_LPSPI4_PCS0_SELECT_INPUT_DAISY_GPIO_B0_00_ALT3,
|
||||
sel: &nxp.IOMUXC.LPSPI4_PCS0_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
SPI2 = &_SPI2
|
||||
_SPI2 = SPI{
|
||||
Bus: nxp.LPSPI3,
|
||||
muxSDI: muxSelect{ // D1 (PA2 [AD_B0_02])
|
||||
mux: nxp.IOMUXC_LPSPI3_SDI_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT7,
|
||||
sel: &nxp.IOMUXC.LPSPI3_SDI_SELECT_INPUT,
|
||||
},
|
||||
muxSDO: muxSelect{ // D26 (PA30 [AD_B1_14])
|
||||
mux: nxp.IOMUXC_LPSPI3_SDO_SELECT_INPUT_DAISY_GPIO_AD_B1_14_ALT2,
|
||||
sel: &nxp.IOMUXC.LPSPI3_SDO_SELECT_INPUT,
|
||||
},
|
||||
muxSCK: muxSelect{ // D27 (PA31 [AD_B1_15])
|
||||
mux: nxp.IOMUXC_LPSPI3_SCK_SELECT_INPUT_DAISY_GPIO_AD_B1_15,
|
||||
sel: &nxp.IOMUXC.LPSPI3_SCK_SELECT_INPUT,
|
||||
},
|
||||
muxCS: muxSelect{ // D0 (PA3 [AD_B0_03])
|
||||
mux: nxp.IOMUXC_LPSPI3_PCS0_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT7,
|
||||
sel: &nxp.IOMUXC.LPSPI3_PCS0_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
SPI3 = &_SPI3
|
||||
_SPI3 = SPI{
|
||||
Bus: nxp.LPSPI1,
|
||||
muxSDI: muxSelect{ // D34 (PC15 [SD_B0_03])
|
||||
mux: nxp.IOMUXC_LPSPI1_SDI_SELECT_INPUT_DAISY_GPIO_SD_B0_03_ALT4,
|
||||
sel: &nxp.IOMUXC.LPSPI1_SDI_SELECT_INPUT,
|
||||
},
|
||||
muxSDO: muxSelect{ // D35 (PC14 [SD_B0_02])
|
||||
mux: nxp.IOMUXC_LPSPI1_SDO_SELECT_INPUT_DAISY_GPIO_SD_B0_02_ALT4,
|
||||
sel: &nxp.IOMUXC.LPSPI1_SDO_SELECT_INPUT,
|
||||
},
|
||||
muxSCK: muxSelect{ // D37 (PC12 [SD_B0_00])
|
||||
mux: nxp.IOMUXC_LPSPI1_SCK_SELECT_INPUT_DAISY_GPIO_SD_B0_00_ALT4,
|
||||
sel: &nxp.IOMUXC.LPSPI1_SCK_SELECT_INPUT,
|
||||
},
|
||||
muxCS: muxSelect{ // D36 (PC13 [SD_B0_01])
|
||||
mux: nxp.IOMUXC_LPSPI1_PCS0_SELECT_INPUT_DAISY_GPIO_SD_B0_01_ALT4,
|
||||
sel: &nxp.IOMUXC.LPSPI1_PCS0_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// #====================================================#
|
||||
// | I2C |
|
||||
// #===========#==========#=============#===============#
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
//go:build thingplus_rp2040
|
||||
// +build thingplus_rp2040
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
LED = GPIO25
|
||||
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
GP0 Pin = GPIO0 // TX
|
||||
GP1 Pin = GPIO1 // RX
|
||||
GP2 Pin = GPIO2 // SCK
|
||||
GP3 Pin = GPIO3 // COPI
|
||||
GP4 Pin = GPIO4 // CIPO
|
||||
|
||||
GP6 Pin = GPIO6 // SDA
|
||||
GP7 Pin = GPIO7 // SCL (connected to GPIO23 as well)
|
||||
GP8 Pin = GPIO8 // WS2812 RGB LED
|
||||
GP9 Pin = GPIO9 // muSDcard DATA3 / CS
|
||||
GP10 Pin = GPIO10 // muSDcard DATA2
|
||||
GP11 Pin = GPIO11 // muSDcard DATA1
|
||||
GP12 Pin = GPIO12 // muSDcard DATA0 / CIPO
|
||||
|
||||
GP14 Pin = GPIO14 // muSDcard CLK /SCLK
|
||||
GP15 Pin = GPIO15 // muSDcard CMD / COPI
|
||||
GP16 Pin = GPIO16 // 16
|
||||
GP17 Pin = GPIO17 // 17
|
||||
GP18 Pin = GPIO18 // 18
|
||||
GP19 Pin = GPIO19 // 19
|
||||
GP20 Pin = GPIO20 // 20
|
||||
GP21 Pin = GPIO21 // 21
|
||||
GP22 Pin = GPIO22 // 22
|
||||
GP23 Pin = GPIO23 // Connected to GPIO7
|
||||
GP25 Pin = GPIO25 // Status blue LED
|
||||
GP26 Pin = GPIO26 // ADC0
|
||||
GP27 Pin = GPIO27 // ADC1
|
||||
GP28 Pin = GPIO28 // ADC2
|
||||
GP29 Pin = GPIO29 // ADC3
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = GPIO26
|
||||
A1 = GPIO27
|
||||
A2 = GPIO28
|
||||
A3 = GPIO29
|
||||
)
|
||||
|
||||
// I2C Pins.
|
||||
const (
|
||||
I2C0_SCL_PIN = GPIO6 // N/A
|
||||
I2C0_SDA_PIN = GPIO7 // N/A
|
||||
|
||||
I2C1_SDA_PIN = GPIO6
|
||||
I2C1_SCL_PIN = GPIO7
|
||||
|
||||
SDA_PIN = I2C1_SDA_PIN
|
||||
SCL_PIN = I2C1_SCL_PIN
|
||||
)
|
||||
|
||||
// SPI default pins
|
||||
const (
|
||||
// Default Serial Clock Bus 0 for SPI communications
|
||||
SPI0_SCK_PIN = GPIO2
|
||||
// Default Serial Out Bus 0 for SPI communications
|
||||
SPI0_SDO_PIN = GPIO3 // Tx
|
||||
// Default Serial In Bus 0 for SPI communications
|
||||
SPI0_SDI_PIN = GPIO4 // Rx
|
||||
|
||||
// Default Serial Clock Bus 1 for SPI communications to muSDcard
|
||||
SPI1_SCK_PIN = GPIO14
|
||||
// Default Serial Out Bus 1 for SPI communications to muSDcard
|
||||
SPI1_SDO_PIN = GPIO15 // Tx
|
||||
// Default Serial In Bus 1 for SPI communications to muSDcard
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
@@ -1,113 +0,0 @@
|
||||
//go:build xiao_ble
|
||||
// +build xiao_ble
|
||||
|
||||
// This file contains the pin mappings for the Seeed XIAO BLE nRF52840 [Sense] boards.
|
||||
//
|
||||
// Seeed XIAO BLE is an ultra-small size, ultra-low power Bluetooth development board based on the Nordic nRF52840.
|
||||
// It features an onboard Bluetooth antenna, onboard battery charging chip, and 21*17.5mm thumb size, which makes it ideal for IoT projects.
|
||||
//
|
||||
// Seeed XIAO BLE nRF52840 Sense is a tiny Bluetooth LE development board designed for IoT and AI applications.
|
||||
// It features an onboard antenna, 6 Dof IMU, microphone, all of which make it an ideal board to run AI using TinyML and TensorFlow Lite.
|
||||
//
|
||||
// SoftDevice (s140v7) is pre-flashed on this board already.
|
||||
// See https://github.com/tinygo-org/bluetooth
|
||||
//
|
||||
// - https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html
|
||||
// - https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html
|
||||
//
|
||||
// - https://wiki.seeedstudio.com/XIAO_BLE/
|
||||
// - https://github.com/Seeed-Studio/ArduinoCore-mbed/tree/master/variants/SEEED_XIAO_NRF52840_SENSE
|
||||
//
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
|
||||
// Digital Pins
|
||||
const (
|
||||
D0 Pin = P0_02
|
||||
D1 Pin = P0_03
|
||||
D2 Pin = P0_28
|
||||
D3 Pin = P0_29
|
||||
D4 Pin = P0_04
|
||||
D5 Pin = P0_05
|
||||
D6 Pin = P1_11
|
||||
D7 Pin = P1_12
|
||||
D8 Pin = P1_13
|
||||
D9 Pin = P1_14
|
||||
D10 Pin = P1_15
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 Pin = P0_02
|
||||
A1 Pin = P0_03
|
||||
A2 Pin = P0_28
|
||||
A3 Pin = P0_29
|
||||
A4 Pin = P0_04
|
||||
A5 Pin = P0_05
|
||||
)
|
||||
|
||||
// Onboard LEDs
|
||||
const (
|
||||
LED = LED_CHG
|
||||
LED1 = LED_RED
|
||||
LED2 = LED_GREEN
|
||||
LED3 = LED_BLUE
|
||||
LED_CHG = P0_17
|
||||
LED_RED = P0_26
|
||||
LED_GREEN = P0_30
|
||||
LED_BLUE = P0_06
|
||||
)
|
||||
|
||||
// UART0 pins
|
||||
const (
|
||||
UART_RX_PIN = P1_12
|
||||
UART_TX_PIN = P1_11
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
// Defaults to internal
|
||||
SDA_PIN = SDA1_PIN
|
||||
SCL_PIN = SCL1_PIN
|
||||
|
||||
// I2C0 (external) pins
|
||||
SDA0_PIN = P0_04
|
||||
SCL0_PIN = P0_05
|
||||
|
||||
// I2C1 (internal) pins
|
||||
SDA1_PIN = P0_07
|
||||
SCL1_PIN = P0_27
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = P1_13
|
||||
SPI0_SDO_PIN = P1_14
|
||||
SPI0_SDI_PIN = P1_15
|
||||
)
|
||||
|
||||
// Peripherals
|
||||
const (
|
||||
LSM_PWR = P1_08 // IMU (LSM6DS3TR) power
|
||||
LSM_INT = P0_11 // IMU (LSM6DS3TR) interrupt
|
||||
|
||||
MIC_PWR = P1_10 // Microphone (MSM261D3526H1CPM) power
|
||||
MIC_CLK = P1_00
|
||||
MIC_DIN = P0_16
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "XIAO nRF52840 Sense"
|
||||
usb_STRING_MANUFACTURER = "Seeed"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2886
|
||||
usb_PID uint16 = 0x0045
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultUART = UART0
|
||||
)
|
||||
@@ -1742,7 +1742,6 @@ type USBCDC struct {
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
configured bool
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1924,13 +1923,6 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
// enable IRQ
|
||||
intr := interrupt.New(sam.IRQ_USB, handleUSB)
|
||||
intr.Enable()
|
||||
|
||||
usbcdc.configured = true
|
||||
}
|
||||
|
||||
// Configured returns whether usbcdc is configured or not.
|
||||
func (usbcdc *USBCDC) Configured() bool {
|
||||
return usbcdc.configured
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
|
||||
+706
-21
@@ -13,6 +13,7 @@ import (
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -1032,9 +1033,6 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
uart.Bus.CTRLA.Set((1 << sam.SERCOM_USART_INT_CTRLA_MODE_Pos) |
|
||||
(1 << sam.SERCOM_USART_INT_CTRLA_SAMPR_Pos)) // sample rate of 16x
|
||||
|
||||
// set clock
|
||||
setSERCOMClockGenerator(uart.SERCOM, sam.GCLK_PCHCTRL_GEN_GCLK1)
|
||||
|
||||
// Set baud rate
|
||||
uart.SetBaudRate(config.BaudRate)
|
||||
|
||||
@@ -1126,8 +1124,7 @@ type I2CConfig struct {
|
||||
|
||||
const (
|
||||
// SERCOM_FREQ_REF is always reference frequency on SAMD51 regardless of CPU speed.
|
||||
SERCOM_FREQ_REF = 48000000
|
||||
SERCOM_FREQ_REF_GCLK0 = 120000000
|
||||
SERCOM_FREQ_REF = 48000000
|
||||
|
||||
// Default rise time in nanoseconds, based on 4.7K ohm pull up resistors
|
||||
riseTimeNanoseconds = 125
|
||||
@@ -1181,9 +1178,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SWRST) {
|
||||
}
|
||||
|
||||
// set clock
|
||||
setSERCOMClockGenerator(i2c.SERCOM, sam.GCLK_PCHCTRL_GEN_GCLK1)
|
||||
|
||||
// Set i2c controller mode
|
||||
//SERCOM_I2CM_CTRLA_MODE( I2C_MASTER_OPERATION )
|
||||
// sam.SERCOM_I2CM_CTRLA_MODE_I2C_MASTER = 5?
|
||||
@@ -1489,18 +1483,8 @@ func (spi SPI) Configure(config SPIConfig) error {
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_CPOL)
|
||||
}
|
||||
|
||||
// set clock
|
||||
freqRef := uint32(0)
|
||||
if config.Frequency > SERCOM_FREQ_REF/2 {
|
||||
setSERCOMClockGenerator(spi.SERCOM, sam.GCLK_PCHCTRL_GEN_GCLK0)
|
||||
freqRef = uint32(SERCOM_FREQ_REF_GCLK0)
|
||||
} else {
|
||||
setSERCOMClockGenerator(spi.SERCOM, sam.GCLK_PCHCTRL_GEN_GCLK1)
|
||||
freqRef = uint32(SERCOM_FREQ_REF)
|
||||
}
|
||||
|
||||
// Set synch speed for SPI
|
||||
baudRate := freqRef / (2 * config.Frequency)
|
||||
baudRate := SERCOM_FREQ_REF / (2 * config.Frequency)
|
||||
if baudRate > 0 {
|
||||
baudRate--
|
||||
}
|
||||
@@ -1974,10 +1958,711 @@ func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func initUSB() {
|
||||
// Configure USB D+/D- pins.
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
TxIdx volatile.Register8
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
}
|
||||
|
||||
var (
|
||||
// USB is a USB CDC interface.
|
||||
USB = &USBCDC{Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
const (
|
||||
usbcdcTxSizeMask uint8 = 0x3F
|
||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
||||
usbcdcTxBank1st uint8 = 0x00
|
||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
||||
)
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() error {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
sz := idx & usbcdcTxSizeMask
|
||||
bk := idx & usbcdcTxBankMask
|
||||
if 0 < sz {
|
||||
|
||||
if usbcdc.waitTxc {
|
||||
// waiting for the next flush(), because the transmission is not complete
|
||||
usbcdc.waitTxcRetryCount++
|
||||
return nil
|
||||
}
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
|
||||
// set the data
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
|
||||
if bk == usbcdcTxBank1st {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
||||
} else {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
||||
}
|
||||
|
||||
// clean multi packet size of bytes already sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set count of bytes to be sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
usbcdc.sent = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
// Supposedly to handle problem with Windows USB serial ports?
|
||||
if usbLineInfo.lineState > 0 {
|
||||
ok := false
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
||||
usbcdc.TxIdx.Set(idx + 1)
|
||||
ok = true
|
||||
}
|
||||
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if ok {
|
||||
break
|
||||
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
usbcdc.TxIdx.Set(0)
|
||||
usbLineInfo.lineState = 0
|
||||
interrupt.Restore(mask)
|
||||
break
|
||||
} else {
|
||||
mask := interrupt.Disable()
|
||||
if usbcdc.sent {
|
||||
if usbcdc.waitTxc {
|
||||
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) != 0 {
|
||||
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.Flush()
|
||||
}
|
||||
} else {
|
||||
usbcdc.Flush()
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
const (
|
||||
// these are SAMD51 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
var (
|
||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
||||
|
||||
udd_ep_in_cache_buffer [7][128]uint8
|
||||
udd_ep_out_cache_buffer [7][128]uint8
|
||||
|
||||
isEndpointHalt = false
|
||||
isRemoteWakeUpEnabled = false
|
||||
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
|
||||
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
|
||||
|
||||
usbConfiguration uint8
|
||||
usbSetInterface uint8
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
)
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ at highest priority
|
||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
USB.Flush()
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.wIndex == usb_CDC_ACM_INTERFACE {
|
||||
ok = cdcSetup(setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
if (epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 ||
|
||||
(epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
||||
switch i {
|
||||
case usb_CDC_ENDPOINT_OUT:
|
||||
handleEndpoint(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
||||
setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
if i == usb_CDC_ENDPOINT_IN {
|
||||
USB.waitTxc = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK0RDY)
|
||||
}
|
||||
}
|
||||
|
||||
func handleStandardSetup(setup usbSetup) bool {
|
||||
switch setup.bRequest {
|
||||
case usb_GET_STATUS:
|
||||
buf := []byte{0, 0}
|
||||
|
||||
if setup.bmRequestType != 0 { // endpoint
|
||||
// TODO: actually check if the endpoint in question is currently halted
|
||||
if isEndpointHalt {
|
||||
buf[0] = 1
|
||||
}
|
||||
}
|
||||
|
||||
sendUSBPacket(0, buf)
|
||||
return true
|
||||
|
||||
case usb_CLEAR_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = false
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = false
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = true
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = true
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_ADDRESS:
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.wValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
|
||||
case usb_GET_DESCRIPTOR:
|
||||
sendDescriptor(setup)
|
||||
return true
|
||||
|
||||
case usb_SET_DESCRIPTOR:
|
||||
return false
|
||||
|
||||
case usb_GET_CONFIGURATION:
|
||||
buff := []byte{usbConfiguration}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_CONFIGURATION:
|
||||
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
||||
for i := 1; i < len(endPoints); i++ {
|
||||
initEndpoint(uint32(i), endPoints[i])
|
||||
}
|
||||
|
||||
usbConfiguration = setup.wValueL
|
||||
|
||||
// Enable interrupt for CDC control messages from host (OUT packet)
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
// Enable interrupt for CDC data messages from host
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
case usb_GET_INTERFACE:
|
||||
buff := []byte{usbSetInterface}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_INTERFACE:
|
||||
usbSetInterface = setup.wValueL
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.wValueL
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data)
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
for i := 0; i < count; i++ {
|
||||
USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
|
||||
}
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get()
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val)
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get()
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preparation
|
||||
|
||||
@@ -28,42 +28,6 @@ var (
|
||||
sercomSPIM5 = SPI{Bus: sam.SERCOM5_SPIM, SERCOM: 5}
|
||||
)
|
||||
|
||||
// setSERCOMClockGenerator sets the GCLK for sercom
|
||||
func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
switch sercom {
|
||||
case 0:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 1:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 2:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 3:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 4:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 5:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has three TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
@@ -28,42 +28,6 @@ var (
|
||||
sercomSPIM5 = SPI{Bus: sam.SERCOM5_SPIM, SERCOM: 5}
|
||||
)
|
||||
|
||||
// setSERCOMClockGenerator sets the GCLK for sercom
|
||||
func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
switch sercom {
|
||||
case 0:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 1:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 2:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 3:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 4:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
case 5:
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].ClearBits(sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((gclk << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user