mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-07 20:43:40 +00:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 733a17d470 | |||
| 330f8c7c50 | |||
| e238eb2242 | |||
| 5625f68d51 | |||
| f7d8502572 | |||
| e395c94e5f | |||
| c08b2dfe06 | |||
| 9c3b706533 | |||
| 60f8a62978 | |||
| 0c7c2926f9 | |||
| 3a2188fc7b | |||
| 64a30424da | |||
| 525c41bac9 | |||
| 83de8ff77d | |||
| 170a069132 | |||
| f19b288717 | |||
| 7c54dbc61f | |||
| 1976d9e3fe | |||
| 87153e9a02 | |||
| ba274008d5 | |||
| b059de857c | |||
| 1880e82da6 | |||
| 888d957f60 | |||
| f69c579005 | |||
| 2e043c7fbc | |||
| 3f4f4e0ead | |||
| 5c35bad590 | |||
| c1bff3dad0 | |||
| 3eeffca841 | |||
| cb5f7ad3fd | |||
| 704489ed6d | |||
| 9ca6f72583 | |||
| 584098dfda | |||
| 180662f038 | |||
| 2da9d26e21 | |||
| d840971c42 | |||
| ba3b3e8938 | |||
| c4ef38fbf9 | |||
| edaf877056 | |||
| 45743406d0 | |||
| 5428bfd270 | |||
| 72b19555dc | |||
| 612a38e363 | |||
| 0c2ab895b7 | |||
| 8d5b7710cf | |||
| de532643b1 | |||
| c2765e9eca | |||
| aa63f26f36 | |||
| f542717992 | |||
| 922ba6a4a3 | |||
| febb3906db | |||
| 0f06f59c6e | |||
| 0fdf08d14f | |||
| 133e32c1c4 | |||
| 41e501aaf4 | |||
| abc373dc73 | |||
| 02c5c4213c | |||
| 25b83920b6 | |||
| 3f8a110e4a | |||
| 95ee572b4d | |||
| 120d17c124 | |||
| 193f91b870 | |||
| f9ed15f857 | |||
| 870f00222d | |||
| 5b243f652c | |||
| 789b5c6b78 | |||
| dcf609defb | |||
| 4bce85dc09 | |||
| 8a73502f11 | |||
| 632357ffb9 | |||
| 4c54aa20da | |||
| 8486e07377 | |||
| 91e20a53e0 | |||
| 9e42c8459e | |||
| 627743432a | |||
| 5d92d4dc98 | |||
| 682b3a9523 |
+7
-17
@@ -73,14 +73,6 @@ commands:
|
|||||||
- go-cache-v4-{{ checksum "go.mod" }}
|
- go-cache-v4-{{ checksum "go.mod" }}
|
||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- wasi-libc-sysroot-systemclang-v7
|
|
||||||
- run: make wasi-libc
|
|
||||||
- save_cache:
|
|
||||||
key: wasi-libc-sysroot-systemclang-v7
|
|
||||||
paths:
|
|
||||||
- lib/wasi-libc/sysroot
|
|
||||||
- when:
|
- when:
|
||||||
condition: <<parameters.fmt-check>>
|
condition: <<parameters.fmt-check>>
|
||||||
steps:
|
steps:
|
||||||
@@ -100,21 +92,19 @@ commands:
|
|||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-llvm15-go119:
|
test-llvm15-go122:
|
||||||
docker:
|
docker:
|
||||||
- image: golang:1.19-bullseye
|
- image: golang:1.22-bullseye
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "15"
|
llvm: "15"
|
||||||
# "make lint" fails before go 1.21 because internal/tools/go.mod specifies packages that require go 1.21
|
|
||||||
fmt-check: false
|
|
||||||
resource_class: large
|
resource_class: large
|
||||||
test-llvm19-go124:
|
test-llvm20-go124:
|
||||||
docker:
|
docker:
|
||||||
- image: golang:1.24-bullseye
|
- image: golang:1.24-bullseye
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "19"
|
llvm: "20"
|
||||||
resource_class: large
|
resource_class: large
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
@@ -122,6 +112,6 @@ workflows:
|
|||||||
jobs:
|
jobs:
|
||||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||||
# least the smoke tests still pass.
|
# least the smoke tests still pass.
|
||||||
- test-llvm15-go119
|
- test-llvm15-go122
|
||||||
# This tests LLVM 19 support when linking against system libraries.
|
# This tests LLVM 20 support when linking against system libraries.
|
||||||
- test-llvm19-go124
|
- test-llvm20-go124
|
||||||
|
|||||||
@@ -89,19 +89,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache wasi-libc sysroot
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: cache-wasi-libc
|
|
||||||
with:
|
|
||||||
key: wasi-libc-sysroot-${{ matrix.os }}-v1
|
|
||||||
path: lib/wasi-libc/sysroot
|
|
||||||
- name: Build wasi-libc
|
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
|
||||||
run: make wasi-libc
|
|
||||||
- name: make gen-device
|
- name: make gen-device
|
||||||
run: make -j3 gen-device
|
run: make -j3 gen-device
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
run: make test GOTESTFLAGS="-short"
|
run: make test GOTESTFLAGS="-only-current-os"
|
||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
run: make release -j3
|
run: make release -j3
|
||||||
- name: Test stdlib packages
|
- name: Test stdlib packages
|
||||||
@@ -126,7 +117,7 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
version: [16, 17, 18, 19]
|
version: [16, 17, 18, 19, 20]
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Homebrew
|
- name: Set up Homebrew
|
||||||
uses: Homebrew/actions/setup-homebrew@master
|
uses: Homebrew/actions/setup-homebrew@master
|
||||||
@@ -150,8 +141,8 @@ jobs:
|
|||||||
- name: Check binary
|
- name: Check binary
|
||||||
run: tinygo version
|
run: tinygo version
|
||||||
- name: Build TinyGo (default LLVM)
|
- name: Build TinyGo (default LLVM)
|
||||||
if: matrix.version == 19
|
if: matrix.version == 20
|
||||||
run: go install
|
run: go install
|
||||||
- name: Check binary
|
- name: Check binary
|
||||||
if: matrix.version == 19
|
if: matrix.version == 20
|
||||||
run: tinygo version
|
run: tinygo version
|
||||||
|
|||||||
@@ -104,15 +104,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apk add cmake samurai python3
|
apk add cmake samurai python3
|
||||||
make binaryen STATIC=1
|
make binaryen STATIC=1
|
||||||
- name: Cache wasi-libc
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: cache-wasi-libc
|
|
||||||
with:
|
|
||||||
key: wasi-libc-sysroot-linux-alpine-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 fpm
|
- name: Install fpm
|
||||||
run: |
|
run: |
|
||||||
gem install --version 4.0.7 public_suffix
|
gem install --version 4.0.7 public_suffix
|
||||||
@@ -258,15 +249,6 @@ jobs:
|
|||||||
- name: Build Binaryen
|
- name: Build Binaryen
|
||||||
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
if: steps.cache-binaryen.outputs.cache-hit != 'true'
|
||||||
run: make binaryen
|
run: make binaryen
|
||||||
- name: Cache wasi-libc
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: cache-wasi-libc
|
|
||||||
with:
|
|
||||||
key: wasi-libc-sysroot-linux-asserts-v6
|
|
||||||
path: lib/wasi-libc/sysroot
|
|
||||||
- name: Build wasi-libc
|
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
|
||||||
run: make wasi-libc
|
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
run: make ASSERT=1 test
|
run: make ASSERT=1 test
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
# still works after checking out the dev branch (that is, when going from LLVM
|
# still works after checking out the dev branch (that is, when going from LLVM
|
||||||
# 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed).
|
# 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed).
|
||||||
|
|
||||||
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-19 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-20 main' | sudo tee /etc/apt/sources.list.d/llvm.list
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends -y \
|
sudo apt-get install --no-install-recommends -y \
|
||||||
llvm-19-dev \
|
llvm-20-dev \
|
||||||
clang-19 \
|
clang-20 \
|
||||||
libclang-19-dev \
|
libclang-20-dev \
|
||||||
lld-19
|
lld-20
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
disk-root: "C:"
|
disk-root: "C:"
|
||||||
- uses: brechtm/setup-scoop@v2
|
- uses: MinoruSekine/setup-scoop@v4
|
||||||
with:
|
with:
|
||||||
scoop_update: 'false'
|
scoop_update: 'false'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
@@ -91,15 +91,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }}
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Cache wasi-libc sysroot
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: cache-wasi-libc
|
|
||||||
with:
|
|
||||||
key: wasi-libc-sysroot-v5
|
|
||||||
path: lib/wasi-libc/sysroot
|
|
||||||
- name: Build wasi-libc
|
|
||||||
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
|
|
||||||
run: make wasi-libc
|
|
||||||
- name: Cache Go cache
|
- name: Cache Go cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@@ -114,7 +105,7 @@ jobs:
|
|||||||
run: make -j3 gen-device
|
run: make -j3 gen-device
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make test GOTESTFLAGS="-short"
|
run: make test GOTESTFLAGS="-only-current-os"
|
||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make build/release -j4
|
run: make build/release -j4
|
||||||
@@ -144,7 +135,7 @@ jobs:
|
|||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
disk-root: "C:"
|
disk-root: "C:"
|
||||||
- uses: brechtm/setup-scoop@v2
|
- uses: MinoruSekine/setup-scoop@v4
|
||||||
with:
|
with:
|
||||||
scoop_update: 'false'
|
scoop_update: 'false'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
@@ -210,7 +201,7 @@ jobs:
|
|||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
maximum-size: 24GB
|
maximum-size: 24GB
|
||||||
disk-root: "C:"
|
disk-root: "C:"
|
||||||
- uses: brechtm/setup-scoop@v2
|
- uses: MinoruSekine/setup-scoop@v4
|
||||||
with:
|
with:
|
||||||
scoop_update: 'false'
|
scoop_update: 'false'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
[submodule "src/net"]
|
[submodule "src/net"]
|
||||||
path = src/net
|
path = src/net
|
||||||
url = https://github.com/tinygo-org/net.git
|
url = https://github.com/tinygo-org/net.git
|
||||||
branch = dev
|
|
||||||
[submodule "lib/wasi-cli"]
|
[submodule "lib/wasi-cli"]
|
||||||
path = lib/wasi-cli
|
path = lib/wasi-cli
|
||||||
url = https://github.com/WebAssembly/wasi-cli
|
url = https://github.com/WebAssembly/wasi-cli
|
||||||
|
|||||||
@@ -1,3 +1,101 @@
|
|||||||
|
0.38.0
|
||||||
|
---
|
||||||
|
* **general**
|
||||||
|
- `go.*`: upgrade `golang.org/x/tools` to v0.30.0
|
||||||
|
- `all`: add support for LLVM 20
|
||||||
|
* **build**
|
||||||
|
- go back to using MinoruSekine/setup-scoop for Windows CI builds
|
||||||
|
- `flake.*`: upgrade to nixpkgs 25.05, LLVM 20
|
||||||
|
- `Makefile`: only detect ccache command when needed
|
||||||
|
- `Makefile`: create random filename inside rule
|
||||||
|
- `Makefile`: don't set GOROOT
|
||||||
|
- `Makefile`: call uname at most once
|
||||||
|
- `Makefile`: only read NodeJS version when it is needed
|
||||||
|
* **compiler**
|
||||||
|
- add support for `GODEBUG=gotypesalias=1`
|
||||||
|
- `interp`: fix `copy()` from/to external buffers
|
||||||
|
- add `-nobounds` (similar to `-gcflags=-B`)
|
||||||
|
- `compileopts`: add library version to cached library path
|
||||||
|
- `builder`: build wasi-libc inside TinyGo
|
||||||
|
- `builder`: simplify bdwgc libc dependency
|
||||||
|
- `builder`: don't use precompiled libraries
|
||||||
|
- `compileopts`: enable support for `GOARCH=wasm` in `tinygo test`
|
||||||
|
* **fixes**
|
||||||
|
- `rp2350`: Fix DMA to SPI transmits on RP2350 (#4903)
|
||||||
|
- `microbit v2`: use OpenOCD flash method on microbit v2 when using Nordic Semi SoftDevice
|
||||||
|
- `main`: display all of the current GC options for the `-gc` flag
|
||||||
|
- Remove duplicated error handling
|
||||||
|
- `sync`: fix `TestMutexConcurrent` test
|
||||||
|
- fix race condition in `testdata/goroutines.go`
|
||||||
|
- fix build warnings on Windows ARM
|
||||||
|
* **machine**
|
||||||
|
- `usb`: add USB mass storage class support
|
||||||
|
- implement usb receive message throttling
|
||||||
|
- declare usb endpoints per-platform
|
||||||
|
- `samd21`: implement watchdog
|
||||||
|
- `samd51`: write to flash memory in 512 byte long chunks
|
||||||
|
- `samd21`: write to flash memory in 64 byte long chunks
|
||||||
|
- don't inline RTT `WriteByte` everywhere
|
||||||
|
- `rp2`: unexport machine-specific errors
|
||||||
|
- `rp2`: discount scheduling delays in I2C timeouts (#4876)
|
||||||
|
- use pointer receiver in simulated PWM peripherals
|
||||||
|
- add simulated PWM/timer peripherals
|
||||||
|
- `rp2`: expose usb endpoint stall handling
|
||||||
|
- `arm`: clear pending interrupts before enabling them
|
||||||
|
- `rp2`: merge common usb code (#4856)
|
||||||
|
* **main**
|
||||||
|
- add "cores" and "threads" schedulers to help text
|
||||||
|
- add `StartPos` and `EndPos` to `-json` build output
|
||||||
|
- change `-json` flag to match upstream Go
|
||||||
|
* **runtime**
|
||||||
|
- don't lock the print output inside interrupts
|
||||||
|
- don't try to interrupt other cores before they are started
|
||||||
|
- implement `NumCPU` for the multicore scheduler
|
||||||
|
- add support for multicore scheduler
|
||||||
|
- refactor obtaining the system stack
|
||||||
|
- `interrupt`: add `Checkpoint` type
|
||||||
|
- add `exportedFuncPtr`
|
||||||
|
- avoid an allocation in `(*time.Timer).Reset`
|
||||||
|
- stub runtime signal functions for `os/signal` on wasip1
|
||||||
|
- move `timeUnit` to a single place
|
||||||
|
- implement `NumCPU` for `-scheduler=threads`
|
||||||
|
- move `mainExited` boolean
|
||||||
|
- `internal/task`: rename `tinygo_pause` to `tinygo_task_exit`
|
||||||
|
- map every goroutine to a new OS thread
|
||||||
|
- refactor `timerQueue`
|
||||||
|
- make conservative and precise GC MT-safe
|
||||||
|
- `internal/task`: implement atomic primitives for preemptive scheduling
|
||||||
|
- Use diskutil on macOS to extract volume name and path for FAT mounts #4928
|
||||||
|
* **standard library**
|
||||||
|
- `net`: update submodule to latest commits
|
||||||
|
- `runtime/debug`: add GC related stubs
|
||||||
|
- `metrics`: flesh out some of the metric types
|
||||||
|
- `reflect`: Chan related stubs
|
||||||
|
- `os`: handle relative and abs paths in `Executable()`
|
||||||
|
- `os`: add `os.Executable()` for Darwin
|
||||||
|
- `sync`: implement `RWMutex` using futexes
|
||||||
|
- `reflect`: Add `SliceOf`, `ArrayOf`, `StructOf`, `MapOf`, `FuncOf`
|
||||||
|
* **targets**
|
||||||
|
- `rp2040`: add multicore support
|
||||||
|
- `riscv32`: use `gdb` binary as a fallback
|
||||||
|
- add target for Microbit v2 with SoftDevice S140 support for both peripheral and central
|
||||||
|
- `windows`: use MSVCRT.DLL instead of UCRT on i386
|
||||||
|
- `windows`: add windows/386 support
|
||||||
|
- `arm64`: remove unnecessary `.section` directive
|
||||||
|
- `riscv-qemu`: actually sleep in `time.Sleep()`
|
||||||
|
- `riscv`: define CSR constants and use them where possible
|
||||||
|
- `darwin`: support Boehm GC (and use by default)
|
||||||
|
- `windows`: add support for the Boehm-Demers-Weiser GC
|
||||||
|
- `windows`: fix wrong register for first parameter
|
||||||
|
* **wasm**
|
||||||
|
- add Boehm GC support
|
||||||
|
- refactor/modify stub signal handling
|
||||||
|
- don't block `//go:wasmexport` because of running goroutines
|
||||||
|
- use `int64` instead of `float64` for the `timeUnit`
|
||||||
|
* **boards**
|
||||||
|
- Add board support for BigTreeTech SKR Pico (#4842)
|
||||||
|
|
||||||
|
|
||||||
0.37.0
|
0.37.0
|
||||||
---
|
---
|
||||||
* **general**
|
* **general**
|
||||||
|
|||||||
+64
-31
@@ -8,13 +8,20 @@ LLVM_PROJECTDIR ?= llvm-project
|
|||||||
CLANG_SRC ?= $(LLVM_PROJECTDIR)/clang
|
CLANG_SRC ?= $(LLVM_PROJECTDIR)/clang
|
||||||
LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
# avoid calling uname on Windows
|
||||||
|
uname := Windows_NT
|
||||||
|
else
|
||||||
|
uname := $(shell uname -s)
|
||||||
|
endif
|
||||||
|
|
||||||
# Try to autodetect LLVM build tools.
|
# Try to autodetect LLVM build tools.
|
||||||
# Versions are listed here in descending priority order.
|
# Versions are listed here in descending priority order.
|
||||||
LLVM_VERSIONS = 19 18 17 16 15
|
LLVM_VERSIONS = 19 18 17 16 15
|
||||||
errifempty = $(if $(1),$(1),$(error $(2)))
|
errifempty = $(if $(1),$(1),$(error $(2)))
|
||||||
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
|
||||||
toolSearchPathsVersion = $(1)-$(2)
|
toolSearchPathsVersion = $(1)-$(2)
|
||||||
ifeq ($(shell uname -s),Darwin)
|
ifeq ($(uname),Darwin)
|
||||||
# Also explicitly search Brew's copy, which is not in PATH by default.
|
# Also explicitly search Brew's copy, which is not in PATH by default.
|
||||||
BREW_PREFIX := $(shell brew --prefix)
|
BREW_PREFIX := $(shell brew --prefix)
|
||||||
toolSearchPathsVersion += $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)-$(2) $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)
|
toolSearchPathsVersion += $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)-$(2) $(BREW_PREFIX)/opt/llvm@$(2)/bin/$(1)
|
||||||
@@ -27,7 +34,6 @@ LLVM_NM ?= $(call findLLVMTool,llvm-nm)
|
|||||||
|
|
||||||
# Go binary and GOROOT to select
|
# Go binary and GOROOT to select
|
||||||
GO ?= go
|
GO ?= go
|
||||||
export GOROOT = $(shell $(GO) env GOROOT)
|
|
||||||
|
|
||||||
# Flags to pass to go test.
|
# Flags to pass to go test.
|
||||||
GOTESTFLAGS ?=
|
GOTESTFLAGS ?=
|
||||||
@@ -38,14 +44,10 @@ TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
|||||||
|
|
||||||
# Check for ccache if the user hasn't set it to on or off.
|
# Check for ccache if the user hasn't set it to on or off.
|
||||||
ifeq (, $(CCACHE))
|
ifeq (, $(CCACHE))
|
||||||
# Use CCACHE for LLVM if possible
|
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(if $(shell command -v ccache 2> /dev/null),ON,OFF)'
|
||||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
else
|
||||||
CCACHE := ON
|
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
|
||||||
else
|
|
||||||
CCACHE := OFF
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
LLVM_OPTION += '-DLLVM_CCACHE_BUILD=$(CCACHE)'
|
|
||||||
|
|
||||||
# Allow enabling LLVM assertions
|
# Allow enabling LLVM assertions
|
||||||
ifeq (1, $(ASSERT))
|
ifeq (1, $(ASSERT))
|
||||||
@@ -127,14 +129,14 @@ ifeq ($(OS),Windows_NT)
|
|||||||
|
|
||||||
USE_SYSTEM_BINARYEN ?= 1
|
USE_SYSTEM_BINARYEN ?= 1
|
||||||
|
|
||||||
else ifeq ($(shell uname -s),Darwin)
|
else ifeq ($(uname),Darwin)
|
||||||
MD5SUM ?= md5
|
MD5SUM ?= md5
|
||||||
|
|
||||||
CGO_LDFLAGS += -lxar
|
CGO_LDFLAGS += -lxar
|
||||||
|
|
||||||
USE_SYSTEM_BINARYEN ?= 1
|
USE_SYSTEM_BINARYEN ?= 1
|
||||||
|
|
||||||
else ifeq ($(shell uname -s),FreeBSD)
|
else ifeq ($(uname),FreeBSD)
|
||||||
MD5SUM ?= md5
|
MD5SUM ?= md5
|
||||||
START_GROUP = -Wl,--start-group
|
START_GROUP = -Wl,--start-group
|
||||||
END_GROUP = -Wl,--end-group
|
END_GROUP = -Wl,--end-group
|
||||||
@@ -259,13 +261,6 @@ build/wasm-opt$(EXE):
|
|||||||
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
|
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Build wasi-libc sysroot
|
|
||||||
.PHONY: wasi-libc
|
|
||||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
|
||||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
|
||||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
|
||||||
cd lib/wasi-libc && $(MAKE) -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC="$(CLANG)" AR=$(LLVM_AR) NM=$(LLVM_NM)
|
|
||||||
|
|
||||||
# Generate WASI syscall bindings
|
# Generate WASI syscall bindings
|
||||||
WASM_TOOLS_MODULE=go.bytecodealliance.org
|
WASM_TOOLS_MODULE=go.bytecodealliance.org
|
||||||
.PHONY: wasi-syscall
|
.PHONY: wasi-syscall
|
||||||
@@ -293,7 +288,7 @@ check-nodejs-version:
|
|||||||
tinygo: ## Build the TinyGo compiler
|
tinygo: ## Build the TinyGo compiler
|
||||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
|
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " $(MAKE) llvm-source"; echo " $(MAKE) $(LLVM_BUILDDIR)"; exit 1; fi
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" .
|
||||||
test: wasi-libc check-nodejs-version
|
test: check-nodejs-version
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS)
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags "byollvm osusergo" $(GOTESTPKGS)
|
||||||
|
|
||||||
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
|
||||||
@@ -439,8 +434,8 @@ TEST_PACKAGES_NONBAREMETAL = \
|
|||||||
$(nil)
|
$(nil)
|
||||||
|
|
||||||
# Report platforms on which each standard library package is known to pass tests
|
# Report platforms on which each standard library package is known to pass tests
|
||||||
jointmp := $(shell echo /tmp/join.$$$$)
|
|
||||||
report-stdlib-tests-pass:
|
report-stdlib-tests-pass:
|
||||||
|
$(eval jointmp := $(shell echo /tmp/join.$$$$))
|
||||||
@for t in $(TEST_PACKAGES_DARWIN); do echo "$$t darwin"; done | sort > $(jointmp).darwin
|
@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_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
|
@for t in $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW); do echo "$$t darwin linux wasi windows"; done | sort > $(jointmp).portable
|
||||||
@@ -449,11 +444,11 @@ report-stdlib-tests-pass:
|
|||||||
@rm $(jointmp).*
|
@rm $(jointmp).*
|
||||||
|
|
||||||
# Standard library packages that pass tests quickly on the current platform
|
# Standard library packages that pass tests quickly on the current platform
|
||||||
ifeq ($(shell uname),Darwin)
|
ifeq ($(uname),Darwin)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
||||||
TEST_IOFS := true
|
TEST_IOFS := true
|
||||||
endif
|
endif
|
||||||
ifeq ($(shell uname),Linux)
|
ifeq ($(uname),Linux)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||||
TEST_IOFS := true
|
TEST_IOFS := true
|
||||||
endif
|
endif
|
||||||
@@ -526,9 +521,9 @@ test-corpus:
|
|||||||
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
|
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
|
||||||
test-corpus-fast:
|
test-corpus-fast:
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus -short . -corpus=testdata/corpus.yaml
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus -short . -corpus=testdata/corpus.yaml
|
||||||
test-corpus-wasi: wasi-libc
|
test-corpus-wasi:
|
||||||
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=wasip1
|
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=wasip1
|
||||||
test-corpus-wasip2: wasi-libc
|
test-corpus-wasip2:
|
||||||
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=wasip2
|
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=wasip2
|
||||||
|
|
||||||
.PHONY: testchdir
|
.PHONY: testchdir
|
||||||
@@ -631,8 +626,12 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s113v7 examples/microbit-blink
|
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s113v7 examples/microbit-blink
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s140v7 examples/microbit-blink
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=btt-skr-pico examples/uart
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1
|
||||||
@@ -795,6 +794,10 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pico examples/usb-storage
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pico2 examples/usb-storage
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/machinetest
|
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/machinetest
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
ifneq ($(STM32), 0)
|
ifneq ($(STM32), 0)
|
||||||
@@ -939,14 +942,15 @@ endif
|
|||||||
wasmtest:
|
wasmtest:
|
||||||
$(GO) test ./tests/wasm
|
$(GO) test ./tests/wasm
|
||||||
|
|
||||||
build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binaryen)
|
build/release: tinygo gen-device $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binaryen)
|
||||||
@mkdir -p build/release/tinygo/bin
|
@mkdir -p build/release/tinygo/bin
|
||||||
@mkdir -p build/release/tinygo/lib/bdwgc
|
@mkdir -p build/release/tinygo/lib/bdwgc
|
||||||
@mkdir -p build/release/tinygo/lib/clang/include
|
@mkdir -p build/release/tinygo/lib/clang/include
|
||||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||||
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
|
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
|
||||||
|
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/crt
|
||||||
|
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/math
|
||||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
|
|
||||||
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||||
@mkdir -p build/release/tinygo/lib/musl/arch
|
@mkdir -p build/release/tinygo/lib/musl/arch
|
||||||
@mkdir -p build/release/tinygo/lib/musl/crt
|
@mkdir -p build/release/tinygo/lib/musl/crt
|
||||||
@@ -954,7 +958,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
|
|||||||
@mkdir -p build/release/tinygo/lib/nrfx
|
@mkdir -p build/release/tinygo/lib/nrfx
|
||||||
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
|
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libm
|
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libm
|
||||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
|
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
||||||
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
@mkdir -p build/release/tinygo/lib/wasi-cli/
|
@mkdir -p build/release/tinygo/lib/wasi-cli/
|
||||||
@@ -977,6 +981,7 @@ endif
|
|||||||
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
||||||
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
||||||
@cp -rp lib/musl/include build/release/tinygo/lib/musl
|
@cp -rp lib/musl/include build/release/tinygo/lib/musl
|
||||||
|
@cp -rp lib/musl/src/conf build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/ctype build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/ctype build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src
|
||||||
@@ -1001,10 +1006,17 @@ endif
|
|||||||
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/process build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/process build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/crt/pseudo-reloc.c build/release/tinygo/lib/mingw-w64/mingw-w64-crt/crt
|
||||||
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/gdtoa build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||||
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/advapi32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio/ucrt_* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
|
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/msvcrt.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/math/x86 build/release/tinygo/lib/mingw-w64/mingw-w64-crt/math
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/misc build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||||
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
|
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
|
||||||
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||||
@cp -rp lib/mingw-w64/mingw-w64-headers/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers
|
@cp -rp lib/mingw-w64/mingw-w64-headers/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers
|
||||||
@@ -1017,15 +1029,36 @@ endif
|
|||||||
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
|
||||||
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
|
@cp -rp lib/picolibc/newlib/libm/math build/release/tinygo/lib/picolibc/newlib/libm
|
||||||
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
|
||||||
@cp -rp lib/wasi-libc/libc-bottom-half/headers/public build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
|
@cp -rp lib/wasi-libc/libc-bottom-half/cloudlibc build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||||
|
@cp -rp lib/wasi-libc/libc-bottom-half/headers build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||||
|
@cp -rp lib/wasi-libc/libc-bottom-half/sources build/release/tinygo/lib/wasi-libc/libc-bottom-half
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/headers build/release/tinygo/lib/wasi-libc/libc-top-half
|
||||||
@cp -rp lib/wasi-libc/libc-top-half/musl/arch/generic build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
@cp -rp lib/wasi-libc/libc-top-half/musl/arch/generic build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
||||||
@cp -rp lib/wasi-libc/libc-top-half/musl/arch/wasm32 build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
@cp -rp lib/wasi-libc/libc-top-half/musl/arch/wasm32 build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/conf build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/dirent build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/env build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/errno build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/exit build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/fcntl build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/fenv build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/internal build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/internal build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/legacy build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/locale build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/math build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/math build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/misc build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/multibyte build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/network build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stat build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stdio build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/stdlib build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
@cp -rp lib/wasi-libc/libc-top-half/musl/src/string build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/string build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
@cp -rp lib/wasi-libc/libc-top-half/musl/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/thread build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/time build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/musl/src/unistd build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
|
||||||
|
@cp -rp lib/wasi-libc/libc-top-half/sources build/release/tinygo/lib/wasi-libc/libc-top-half
|
||||||
@cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
|
@cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
|
||||||
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
|
@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 llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
|
||||||
|
|||||||
+7
-3
@@ -14,7 +14,7 @@ var BoehmGC = Library{
|
|||||||
name: "bdwgc",
|
name: "bdwgc",
|
||||||
cflags: func(target, headerPath string) []string {
|
cflags: func(target, headerPath string) []string {
|
||||||
libdir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
|
libdir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
|
||||||
return []string{
|
flags := []string{
|
||||||
// use a modern environment
|
// use a modern environment
|
||||||
"-DUSE_MMAP", // mmap is available
|
"-DUSE_MMAP", // mmap is available
|
||||||
"-DUSE_MUNMAP", // return memory to the OS using munmap
|
"-DUSE_MUNMAP", // return memory to the OS using munmap
|
||||||
@@ -30,6 +30,7 @@ var BoehmGC = Library{
|
|||||||
// Use a minimal environment.
|
// Use a minimal environment.
|
||||||
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
|
"-DNO_MSGBOX_ON_ERROR", // don't call MessageBoxA on Windows
|
||||||
"-DDONT_USE_ATEXIT",
|
"-DDONT_USE_ATEXIT",
|
||||||
|
"-DNO_GETENV",
|
||||||
|
|
||||||
// Special flag to work around the lack of __data_start in ld.lld.
|
// Special flag to work around the lack of __data_start in ld.lld.
|
||||||
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
// TODO: try to fix this in LLVM/lld directly so we don't have to
|
||||||
@@ -42,17 +43,20 @@ var BoehmGC = Library{
|
|||||||
// Assertions can be enabled while debugging GC issues.
|
// Assertions can be enabled while debugging GC issues.
|
||||||
//"-DGC_ASSERTIONS",
|
//"-DGC_ASSERTIONS",
|
||||||
|
|
||||||
// Threading is not yet supported, so these are disabled.
|
// We use our own way of dealing with threads (that is a bit hacky).
|
||||||
|
// See src/runtime/gc_boehm.go.
|
||||||
//"-DGC_THREADS",
|
//"-DGC_THREADS",
|
||||||
//"-DTHREAD_LOCAL_ALLOC",
|
//"-DTHREAD_LOCAL_ALLOC",
|
||||||
|
|
||||||
"-I" + libdir + "/include",
|
"-I" + libdir + "/include",
|
||||||
}
|
}
|
||||||
|
return flags
|
||||||
},
|
},
|
||||||
|
needsLibc: true,
|
||||||
sourceDir: func() string {
|
sourceDir: func() string {
|
||||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
|
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/bdwgc")
|
||||||
},
|
},
|
||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string, _ bool) ([]string, error) {
|
||||||
sources := []string{
|
sources := []string{
|
||||||
"allchblk.c",
|
"allchblk.c",
|
||||||
"alloc.c",
|
"alloc.c",
|
||||||
|
|||||||
+13
-17
@@ -14,7 +14,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/types"
|
"go/types"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"io/fs"
|
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -147,14 +146,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
// the libc needs them.
|
// the libc needs them.
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
var libcDependencies []*compileJob
|
var libcDependencies []*compileJob
|
||||||
var libcJob *compileJob
|
|
||||||
switch config.Target.Libc {
|
switch config.Target.Libc {
|
||||||
case "darwin-libSystem":
|
case "darwin-libSystem":
|
||||||
libcJob = makeDarwinLibSystemJob(config, tmpdir)
|
libcJob := makeDarwinLibSystemJob(config, tmpdir)
|
||||||
libcDependencies = append(libcDependencies, libcJob)
|
libcDependencies = append(libcDependencies, libcJob)
|
||||||
case "musl":
|
case "musl":
|
||||||
var unlock func()
|
var unlock func()
|
||||||
libcJob, unlock, err = libMusl.load(config, tmpdir, nil)
|
libcJob, unlock, err := libMusl.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BuildResult{}, err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
@@ -162,28 +160,28 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(libcJob.result), "crt1.o")))
|
libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(libcJob.result), "crt1.o")))
|
||||||
libcDependencies = append(libcDependencies, libcJob)
|
libcDependencies = append(libcDependencies, libcJob)
|
||||||
case "picolibc":
|
case "picolibc":
|
||||||
libcJob, unlock, err := libPicolibc.load(config, tmpdir, nil)
|
libcJob, unlock, err := libPicolibc.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BuildResult{}, err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
defer unlock()
|
defer unlock()
|
||||||
libcDependencies = append(libcDependencies, libcJob)
|
libcDependencies = append(libcDependencies, libcJob)
|
||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
libcJob, unlock, err := libWasiLibc.load(config, tmpdir)
|
||||||
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
|
if err != nil {
|
||||||
return BuildResult{}, errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
defer unlock()
|
||||||
|
libcDependencies = append(libcDependencies, libcJob)
|
||||||
case "wasmbuiltins":
|
case "wasmbuiltins":
|
||||||
libcJob, unlock, err := libWasmBuiltins.load(config, tmpdir, nil)
|
libcJob, unlock, err := libWasmBuiltins.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BuildResult{}, err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
defer unlock()
|
defer unlock()
|
||||||
libcDependencies = append(libcDependencies, libcJob)
|
libcDependencies = append(libcDependencies, libcJob)
|
||||||
case "mingw-w64":
|
case "mingw-w64":
|
||||||
var unlock func()
|
libcJob, unlock, err := libMinGW.load(config, tmpdir)
|
||||||
libcJob, unlock, err = libMinGW.load(config, tmpdir, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BuildResult{}, err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
@@ -216,6 +214,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
MaxStackAlloc: config.MaxStackAlloc(),
|
MaxStackAlloc: config.MaxStackAlloc(),
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
|
Debug: !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
|
||||||
|
Nobounds: config.Options.Nobounds,
|
||||||
PanicStrategy: config.PanicStrategy(),
|
PanicStrategy: config.PanicStrategy(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,7 +703,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
||||||
// a cache.
|
// a cache.
|
||||||
if config.Target.RTLib == "compiler-rt" {
|
if config.Target.RTLib == "compiler-rt" {
|
||||||
job, unlock, err := libCompilerRT.load(config, tmpdir, nil)
|
job, unlock, err := libCompilerRT.load(config, tmpdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
@@ -714,10 +713,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
// The Boehm collector is stored in a separate C library.
|
// The Boehm collector is stored in a separate C library.
|
||||||
if config.GC() == "boehm" {
|
if config.GC() == "boehm" {
|
||||||
if libcJob == nil {
|
job, unlock, err := BoehmGC.load(config, tmpdir)
|
||||||
return BuildResult{}, fmt.Errorf("boehm GC isn't supported with libc %s", config.Target.Libc)
|
|
||||||
}
|
|
||||||
job, unlock, err := BoehmGC.load(config, tmpdir, libcJob)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BuildResult{}, err
|
return BuildResult{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "softfloat"},
|
{GOOS: "linux", GOARCH: "mipsle", GOMIPS: "softfloat"},
|
||||||
{GOOS: "darwin", GOARCH: "amd64"},
|
{GOOS: "darwin", GOARCH: "amd64"},
|
||||||
{GOOS: "darwin", GOARCH: "arm64"},
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
|
{GOOS: "windows", GOARCH: "386"},
|
||||||
{GOOS: "windows", GOARCH: "amd64"},
|
{GOOS: "windows", GOARCH: "amd64"},
|
||||||
{GOOS: "windows", GOARCH: "arm64"},
|
{GOOS: "windows", GOARCH: "arm64"},
|
||||||
} {
|
} {
|
||||||
|
|||||||
+11
-1
@@ -3,6 +3,7 @@ package builder
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
@@ -201,6 +202,11 @@ var avrBuiltins = []string{
|
|||||||
"avr/udivmodqi4.S",
|
"avr/udivmodqi4.S",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Builtins needed specifically for windows/386.
|
||||||
|
var windowsI386Builtins = []string{
|
||||||
|
"i386/chkstk.S", // also _alloca
|
||||||
|
}
|
||||||
|
|
||||||
// libCompilerRT is a library with symbols required by programs compiled with
|
// libCompilerRT is a library with symbols required by programs compiled with
|
||||||
// LLVM. These symbols are for operations that cannot be emitted with a single
|
// LLVM. These symbols are for operations that cannot be emitted with a single
|
||||||
// instruction or a short sequence of instructions for that target.
|
// instruction or a short sequence of instructions for that target.
|
||||||
@@ -220,7 +226,7 @@ var libCompilerRT = Library{
|
|||||||
// Development build.
|
// Development build.
|
||||||
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
|
||||||
},
|
},
|
||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string, _ bool) ([]string, error) {
|
||||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||||
switch compileopts.CanonicalArchName(target) {
|
switch compileopts.CanonicalArchName(target) {
|
||||||
case "arm":
|
case "arm":
|
||||||
@@ -229,6 +235,10 @@ var libCompilerRT = Library{
|
|||||||
builtins = append(builtins, avrBuiltins...)
|
builtins = append(builtins, avrBuiltins...)
|
||||||
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
|
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
|
||||||
builtins = append(builtins, genericBuiltins128...)
|
builtins = append(builtins, genericBuiltins128...)
|
||||||
|
case "i386":
|
||||||
|
if strings.Split(target, "-")[2] == "windows" {
|
||||||
|
builtins = append(builtins, windowsI386Builtins...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return builtins, nil
|
return builtins, nil
|
||||||
},
|
},
|
||||||
|
|||||||
+18
-20
@@ -15,6 +15,9 @@ import (
|
|||||||
|
|
||||||
// Library is a container for information about a single C library, such as a
|
// Library is a container for information about a single C library, such as a
|
||||||
// compiler runtime or libc.
|
// compiler runtime or libc.
|
||||||
|
//
|
||||||
|
// Note: whenever a library gets changed, the version in compileopts/config.go
|
||||||
|
// probably also needs to be incremented.
|
||||||
type Library struct {
|
type Library struct {
|
||||||
// The library name, such as compiler-rt or picolibc.
|
// The library name, such as compiler-rt or picolibc.
|
||||||
name string
|
name string
|
||||||
@@ -25,11 +28,17 @@ type Library struct {
|
|||||||
// cflags returns the C flags specific to this library
|
// cflags returns the C flags specific to this library
|
||||||
cflags func(target, headerPath string) []string
|
cflags func(target, headerPath string) []string
|
||||||
|
|
||||||
|
// cflagsForFile returns additional C flags for a particular source file.
|
||||||
|
cflagsForFile func(path string) []string
|
||||||
|
|
||||||
|
// needsLibc is set to true if this library needs libc headers.
|
||||||
|
needsLibc bool
|
||||||
|
|
||||||
// The source directory.
|
// The source directory.
|
||||||
sourceDir func() string
|
sourceDir func() string
|
||||||
|
|
||||||
// The source files, relative to sourceDir.
|
// The source files, relative to sourceDir.
|
||||||
librarySources func(target string) ([]string, error)
|
librarySources func(target string, libcNeedsMalloc bool) ([]string, error)
|
||||||
|
|
||||||
// The source code for the crt1.o file, relative to sourceDir.
|
// The source code for the crt1.o file, relative to sourceDir.
|
||||||
crt1Source string
|
crt1Source string
|
||||||
@@ -43,18 +52,9 @@ type Library struct {
|
|||||||
// output archive file, it is expected to be removed after use.
|
// output archive file, it is expected to be removed after use.
|
||||||
// As a side effect, this call creates the library header files if they didn't
|
// As a side effect, this call creates the library header files if they didn't
|
||||||
// exist yet.
|
// exist yet.
|
||||||
// The provided libc job (if not null) will cause this libc to be added as a
|
func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJob, abortLock func(), err error) {
|
||||||
// dependency for all C compiler jobs, and adds libc headers for the given
|
outdir := config.LibraryPath(l.name)
|
||||||
// target config. In other words, pass this libc if the library needs a libc to
|
|
||||||
// compile.
|
|
||||||
func (l *Library) load(config *compileopts.Config, tmpdir string, libc *compileJob) (job *compileJob, abortLock func(), err error) {
|
|
||||||
outdir, precompiled := config.LibcPath(l.name)
|
|
||||||
archiveFilePath := filepath.Join(outdir, "lib.a")
|
archiveFilePath := filepath.Join(outdir, "lib.a")
|
||||||
if precompiled {
|
|
||||||
// Found a precompiled library for this OS/architecture. Return the path
|
|
||||||
// directly.
|
|
||||||
return dummyCompileJob(archiveFilePath), func() {}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a lock on the output (if supported).
|
// Create a lock on the output (if supported).
|
||||||
// This is a bit messy, but avoids a deadlock because it is ordered consistently with other library loads within a build.
|
// This is a bit messy, but avoids a deadlock because it is ordered consistently with other library loads within a build.
|
||||||
@@ -185,7 +185,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string, libc *compileJ
|
|||||||
args = append(args, "-mfpu=vfpv2")
|
args = append(args, "-mfpu=vfpv2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if libc != nil {
|
if l.needsLibc {
|
||||||
args = append(args, config.LibcCFlags()...)
|
args = append(args, config.LibcCFlags()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,12 +226,13 @@ func (l *Library) load(config *compileopts.Config, tmpdir string, libc *compileJ
|
|||||||
|
|
||||||
// Create jobs to compile all sources. These jobs are depended upon by the
|
// Create jobs to compile all sources. These jobs are depended upon by the
|
||||||
// archive job above, so must be run first.
|
// archive job above, so must be run first.
|
||||||
paths, err := l.librarySources(target)
|
paths, err := l.librarySources(target, config.LibcNeedsMalloc())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
// Strip leading "../" parts off the path.
|
// Strip leading "../" parts off the path.
|
||||||
|
path := path
|
||||||
cleanpath := path
|
cleanpath := path
|
||||||
for strings.HasPrefix(cleanpath, "../") {
|
for strings.HasPrefix(cleanpath, "../") {
|
||||||
cleanpath = cleanpath[3:]
|
cleanpath = cleanpath[3:]
|
||||||
@@ -245,6 +246,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string, libc *compileJ
|
|||||||
run: func(*compileJob) error {
|
run: func(*compileJob) error {
|
||||||
var compileArgs []string
|
var compileArgs []string
|
||||||
compileArgs = append(compileArgs, args...)
|
compileArgs = append(compileArgs, args...)
|
||||||
|
if l.cflagsForFile != nil {
|
||||||
|
compileArgs = append(compileArgs, l.cflagsForFile(path)...)
|
||||||
|
}
|
||||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||||
if config.Options.PrintCommands != nil {
|
if config.Options.PrintCommands != nil {
|
||||||
config.Options.PrintCommands("clang", compileArgs...)
|
config.Options.PrintCommands("clang", compileArgs...)
|
||||||
@@ -256,9 +260,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string, libc *compileJ
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if libc != nil {
|
|
||||||
objfile.dependencies = append(objfile.dependencies, libc)
|
|
||||||
}
|
|
||||||
job.dependencies = append(job.dependencies, objfile)
|
job.dependencies = append(job.dependencies, objfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,9 +290,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string, libc *compileJ
|
|||||||
return os.Rename(tmpfile.Name(), filepath.Join(outdir, "crt1.o"))
|
return os.Rename(tmpfile.Name(), filepath.Join(outdir, "crt1.o"))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if libc != nil {
|
|
||||||
crt1Job.dependencies = append(crt1Job.dependencies, libc)
|
|
||||||
}
|
|
||||||
job.dependencies = append(job.dependencies, crt1Job)
|
job.dependencies = append(job.dependencies, crt1Job)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+87
-33
@@ -30,25 +30,62 @@ var libMinGW = Library{
|
|||||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") },
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") },
|
||||||
cflags: func(target, headerPath string) []string {
|
cflags: func(target, headerPath string) []string {
|
||||||
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
|
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
|
||||||
return []string{
|
flags := []string{
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
|
"-isystem", mingwDir + "/mingw-w64-crt/include",
|
||||||
"-isystem", mingwDir + "/mingw-w64-headers/crt",
|
"-isystem", mingwDir + "/mingw-w64-headers/crt",
|
||||||
|
"-isystem", mingwDir + "/mingw-w64-headers/include",
|
||||||
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
|
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
|
||||||
"-I" + headerPath,
|
"-I" + headerPath,
|
||||||
}
|
}
|
||||||
|
if strings.Split(target, "-")[0] == "i386" {
|
||||||
|
flags = append(flags,
|
||||||
|
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
|
||||||
|
"-D_WIN32_WINNT=0x0501", // target Windows XP
|
||||||
|
"-D_CRTBLD",
|
||||||
|
"-Wno-pragma-pack",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return flags
|
||||||
},
|
},
|
||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string, _ bool) ([]string, error) {
|
||||||
// These files are needed so that printf and the like are supported.
|
// These files are needed so that printf and the like are supported.
|
||||||
sources := []string{
|
var sources []string
|
||||||
"mingw-w64-crt/stdio/ucrt_fprintf.c",
|
if strings.Split(target, "-")[0] == "i386" {
|
||||||
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
|
// Old 32-bit x86 systems use msvcrt.dll.
|
||||||
"mingw-w64-crt/stdio/ucrt_printf.c",
|
sources = []string{
|
||||||
"mingw-w64-crt/stdio/ucrt_snprintf.c",
|
"mingw-w64-crt/crt/pseudo-reloc.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_sprintf.c",
|
"mingw-w64-crt/gdtoa/dmisc.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
|
"mingw-w64-crt/gdtoa/gdtoa.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_vprintf.c",
|
"mingw-w64-crt/gdtoa/gmisc.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
|
"mingw-w64-crt/gdtoa/misc.c",
|
||||||
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
|
"mingw-w64-crt/math/x86/exp2.S",
|
||||||
|
"mingw-w64-crt/math/x86/trunc.S",
|
||||||
|
"mingw-w64-crt/misc/___mb_cur_max_func.c",
|
||||||
|
"mingw-w64-crt/misc/lc_locale_func.c",
|
||||||
|
"mingw-w64-crt/misc/mbrtowc.c",
|
||||||
|
"mingw-w64-crt/misc/strnlen.c",
|
||||||
|
"mingw-w64-crt/misc/wcrtomb.c",
|
||||||
|
"mingw-w64-crt/misc/wcsnlen.c",
|
||||||
|
"mingw-w64-crt/stdio/acrt_iob_func.c",
|
||||||
|
"mingw-w64-crt/stdio/mingw_lock.c",
|
||||||
|
"mingw-w64-crt/stdio/mingw_pformat.c",
|
||||||
|
"mingw-w64-crt/stdio/mingw_vfprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/mingw_vsnprintf.c",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Anything somewhat modern (amd64, arm64) uses UCRT.
|
||||||
|
sources = []string{
|
||||||
|
"mingw-w64-crt/stdio/ucrt_fprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_printf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_snprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_sprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_vprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
|
||||||
|
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sources, nil
|
return sources, nil
|
||||||
},
|
},
|
||||||
@@ -63,27 +100,41 @@ var libMinGW = Library{
|
|||||||
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
||||||
var jobs []*compileJob
|
var jobs []*compileJob
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
|
var libs []string
|
||||||
// .lib file. But to simplify things, we're going to leave them as separate
|
if goarch == "386" {
|
||||||
// files.
|
libs = []string{
|
||||||
for _, name := range []string{
|
// x86 uses msvcrt.dll instead of UCRT for compatibility with old
|
||||||
"kernel32.def.in",
|
// Windows versions.
|
||||||
"api-ms-win-crt-conio-l1-1-0.def",
|
"advapi32.def.in",
|
||||||
"api-ms-win-crt-convert-l1-1-0.def.in",
|
"kernel32.def.in",
|
||||||
"api-ms-win-crt-environment-l1-1-0.def",
|
"msvcrt.def.in",
|
||||||
"api-ms-win-crt-filesystem-l1-1-0.def",
|
}
|
||||||
"api-ms-win-crt-heap-l1-1-0.def",
|
} else {
|
||||||
"api-ms-win-crt-locale-l1-1-0.def",
|
// Use the modernized UCRT on new systems.
|
||||||
"api-ms-win-crt-math-l1-1-0.def.in",
|
// Normally all the api-ms-win-crt-*.def files are all compiled to a
|
||||||
"api-ms-win-crt-multibyte-l1-1-0.def",
|
// single .lib file. But to simplify things, we're going to leave them
|
||||||
"api-ms-win-crt-private-l1-1-0.def.in",
|
// as separate files.
|
||||||
"api-ms-win-crt-process-l1-1-0.def",
|
libs = []string{
|
||||||
"api-ms-win-crt-runtime-l1-1-0.def.in",
|
"advapi32.def.in",
|
||||||
"api-ms-win-crt-stdio-l1-1-0.def",
|
"kernel32.def.in",
|
||||||
"api-ms-win-crt-string-l1-1-0.def",
|
"api-ms-win-crt-conio-l1-1-0.def",
|
||||||
"api-ms-win-crt-time-l1-1-0.def",
|
"api-ms-win-crt-convert-l1-1-0.def.in",
|
||||||
"api-ms-win-crt-utility-l1-1-0.def",
|
"api-ms-win-crt-environment-l1-1-0.def",
|
||||||
} {
|
"api-ms-win-crt-filesystem-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-heap-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-locale-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-math-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-multibyte-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-private-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-process-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-runtime-l1-1-0.def.in",
|
||||||
|
"api-ms-win-crt-stdio-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-string-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-time-l1-1-0.def",
|
||||||
|
"api-ms-win-crt-utility-l1-1-0.def",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, name := range libs {
|
||||||
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
|
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
|
||||||
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
|
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
@@ -93,6 +144,9 @@ func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
|
|||||||
defpath := inpath
|
defpath := inpath
|
||||||
var archDef, emulation string
|
var archDef, emulation string
|
||||||
switch goarch {
|
switch goarch {
|
||||||
|
case "386":
|
||||||
|
archDef = "-DDEF_I386"
|
||||||
|
emulation = "i386pe"
|
||||||
case "amd64":
|
case "amd64":
|
||||||
archDef = "-DDEF_X64"
|
archDef = "-DDEF_X64"
|
||||||
emulation = "i386pep"
|
emulation = "i386pep"
|
||||||
|
|||||||
+43
-31
@@ -12,6 +12,45 @@ import (
|
|||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create the alltypes.h file from the appropriate alltypes.h.in files.
|
||||||
|
func buildMuslAllTypes(arch, muslDir, outputBitsDir string) error {
|
||||||
|
// Create the file alltypes.h.
|
||||||
|
f, err := os.Create(filepath.Join(outputBitsDir, "alltypes.h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
infiles := []string{
|
||||||
|
filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"),
|
||||||
|
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
||||||
|
}
|
||||||
|
for _, infile := range infiles {
|
||||||
|
data, err := os.ReadFile(infile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "TYPEDEF ") {
|
||||||
|
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
||||||
|
value := matches[1]
|
||||||
|
name := matches[2]
|
||||||
|
line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(line, "STRUCT ") {
|
||||||
|
matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line)
|
||||||
|
name := matches[1]
|
||||||
|
value := matches[2]
|
||||||
|
line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name)
|
||||||
|
}
|
||||||
|
_, err := f.WriteString(line + "\n")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
var libMusl = Library{
|
var libMusl = Library{
|
||||||
name: "musl",
|
name: "musl",
|
||||||
makeHeaders: func(target, includeDir string) error {
|
makeHeaders: func(target, includeDir string) error {
|
||||||
@@ -24,41 +63,13 @@ var libMusl = Library{
|
|||||||
arch := compileopts.MuslArchitecture(target)
|
arch := compileopts.MuslArchitecture(target)
|
||||||
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "musl")
|
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "musl")
|
||||||
|
|
||||||
// Create the file alltypes.h.
|
err = buildMuslAllTypes(arch, muslDir, bits)
|
||||||
f, err := os.Create(filepath.Join(bits, "alltypes.h"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
infiles := []string{
|
|
||||||
filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"),
|
|
||||||
filepath.Join(muslDir, "include", "alltypes.h.in"),
|
|
||||||
}
|
|
||||||
for _, infile := range infiles {
|
|
||||||
data, err := os.ReadFile(infile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lines := strings.Split(string(data), "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
if strings.HasPrefix(line, "TYPEDEF ") {
|
|
||||||
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
|
||||||
value := matches[1]
|
|
||||||
name := matches[2]
|
|
||||||
line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(line, "STRUCT ") {
|
|
||||||
matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line)
|
|
||||||
name := matches[1]
|
|
||||||
value := matches[2]
|
|
||||||
line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name)
|
|
||||||
}
|
|
||||||
f.WriteString(line + "\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
// Create the file syscall.h.
|
// Create the file syscall.h.
|
||||||
f, err = os.Create(filepath.Join(bits, "syscall.h"))
|
f, err := os.Create(filepath.Join(bits, "syscall.h"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -110,9 +121,10 @@ var libMusl = Library{
|
|||||||
return cflags
|
return cflags
|
||||||
},
|
},
|
||||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
|
||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string, _ bool) ([]string, error) {
|
||||||
arch := compileopts.MuslArchitecture(target)
|
arch := compileopts.MuslArchitecture(target)
|
||||||
globs := []string{
|
globs := []string{
|
||||||
|
"conf/*.c",
|
||||||
"ctype/*.c",
|
"ctype/*.c",
|
||||||
"env/*.c",
|
"env/*.c",
|
||||||
"errno/*.c",
|
"errno/*.c",
|
||||||
|
|||||||
+1
-1
@@ -43,7 +43,7 @@ var libPicolibc = Library{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string, _ bool) ([]string, error) {
|
||||||
sources := append([]string(nil), picolibcSources...)
|
sources := append([]string(nil), picolibcSources...)
|
||||||
if !strings.HasPrefix(target, "avr") {
|
if !strings.HasPrefix(target, "avr") {
|
||||||
// Small chips without long jumps can't compile many files (printf,
|
// Small chips without long jumps can't compile many files (printf,
|
||||||
|
|||||||
+1
-1
@@ -490,7 +490,7 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if section.Type == elf.SHT_NOBITS {
|
if section.Type == elf.SHT_NOBITS {
|
||||||
if section.Name == ".stack" {
|
if strings.HasPrefix(section.Name, ".stack") {
|
||||||
// TinyGo emits stack sections on microcontroller using the
|
// TinyGo emits stack sections on microcontroller using the
|
||||||
// ".stack" name.
|
// ".stack" name.
|
||||||
// This is a bit ugly, but I don't think there is a way to
|
// This is a bit ugly, but I don't think there is a way to
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
|
|||||||
// This is a small number of very diverse targets that we want to test.
|
// This is a small number of very diverse targets that we want to test.
|
||||||
tests := []sizeTest{
|
tests := []sizeTest{
|
||||||
// microcontrollers
|
// microcontrollers
|
||||||
{"hifive1b", "examples/echo", 4560, 280, 0, 2268},
|
{"hifive1b", "examples/echo", 4556, 280, 0, 2264},
|
||||||
{"microbit", "examples/serial", 2916, 388, 8, 2272},
|
{"microbit", "examples/serial", 2920, 388, 8, 2272},
|
||||||
{"wioterminal", "examples/pininterrupt", 7359, 1489, 116, 6912},
|
{"wioterminal", "examples/pininterrupt", 7379, 1489, 116, 6912},
|
||||||
|
|
||||||
// TODO: also check wasm. Right now this is difficult, because
|
// TODO: also check wasm. Right now this is difficult, because
|
||||||
// wasm binaries are run through wasm-opt and therefore the
|
// wasm binaries are run through wasm-opt and therefore the
|
||||||
|
|||||||
@@ -0,0 +1,284 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var libWasiLibc = Library{
|
||||||
|
name: "wasi-libc",
|
||||||
|
makeHeaders: func(target, includeDir string) error {
|
||||||
|
bits := filepath.Join(includeDir, "bits")
|
||||||
|
err := os.Mkdir(bits, 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "wasi-libc/libc-top-half/musl")
|
||||||
|
err = buildMuslAllTypes("wasm32", muslDir, bits)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// See MUSL_OMIT_HEADERS in the Makefile.
|
||||||
|
omitHeaders := map[string]struct{}{
|
||||||
|
"syslog.h": {},
|
||||||
|
"wait.h": {},
|
||||||
|
"ucontext.h": {},
|
||||||
|
"paths.h": {},
|
||||||
|
"utmp.h": {},
|
||||||
|
"utmpx.h": {},
|
||||||
|
"lastlog.h": {},
|
||||||
|
"elf.h": {},
|
||||||
|
"link.h": {},
|
||||||
|
"pwd.h": {},
|
||||||
|
"shadow.h": {},
|
||||||
|
"grp.h": {},
|
||||||
|
"mntent.h": {},
|
||||||
|
"netdb.h": {},
|
||||||
|
"resolv.h": {},
|
||||||
|
"pty.h": {},
|
||||||
|
"dlfcn.h": {},
|
||||||
|
"setjmp.h": {},
|
||||||
|
"ulimit.h": {},
|
||||||
|
"wordexp.h": {},
|
||||||
|
"spawn.h": {},
|
||||||
|
"termios.h": {},
|
||||||
|
"libintl.h": {},
|
||||||
|
"aio.h": {},
|
||||||
|
|
||||||
|
"stdarg.h": {},
|
||||||
|
"stddef.h": {},
|
||||||
|
|
||||||
|
"pthread.h": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, glob := range [][2]string{
|
||||||
|
{"libc-bottom-half/headers/public/*.h", ""},
|
||||||
|
{"libc-bottom-half/headers/public/wasi/*.h", "wasi"},
|
||||||
|
{"libc-top-half/musl/arch/wasm32/bits/*.h", "bits"},
|
||||||
|
{"libc-top-half/musl/include/*.h", ""},
|
||||||
|
{"libc-top-half/musl/include/netinet/*.h", "netinet"},
|
||||||
|
{"libc-top-half/musl/include/sys/*.h", "sys"},
|
||||||
|
} {
|
||||||
|
matches, _ := filepath.Glob(filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc", glob[0]))
|
||||||
|
outDir := filepath.Join(includeDir, glob[1])
|
||||||
|
os.MkdirAll(outDir, 0o777)
|
||||||
|
for _, match := range matches {
|
||||||
|
name := filepath.Base(match)
|
||||||
|
if _, ok := omitHeaders[name]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile(match)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.WriteFile(filepath.Join(outDir, name), data, 0o666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
cflags: func(target, headerPath string) []string {
|
||||||
|
libcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc")
|
||||||
|
return []string{
|
||||||
|
"-Werror",
|
||||||
|
"-Wall",
|
||||||
|
"-std=gnu11",
|
||||||
|
"-nostdlibinc",
|
||||||
|
"-mnontrapping-fptoint", "-msign-ext", "-mbulk-memory",
|
||||||
|
"-Wno-null-pointer-arithmetic", "-Wno-unused-parameter", "-Wno-sign-compare", "-Wno-unused-variable", "-Wno-unused-function", "-Wno-ignored-attributes", "-Wno-missing-braces", "-Wno-ignored-pragmas", "-Wno-unused-but-set-variable", "-Wno-unknown-warning-option",
|
||||||
|
"-Wno-parentheses", "-Wno-shift-op-parentheses", "-Wno-bitwise-op-parentheses", "-Wno-logical-op-parentheses", "-Wno-string-plus-int", "-Wno-dangling-else", "-Wno-unknown-pragmas",
|
||||||
|
"-DNDEBUG",
|
||||||
|
"-D__wasilibc_printscan_no_long_double",
|
||||||
|
"-D__wasilibc_printscan_full_support_option=\"long double support is disabled\"",
|
||||||
|
"-DBULK_MEMORY_THRESHOLD=32", // default threshold in wasi-libc
|
||||||
|
"-isystem", headerPath,
|
||||||
|
"-I" + libcDir + "/libc-top-half/musl/src/include",
|
||||||
|
"-I" + libcDir + "/libc-top-half/musl/src/internal",
|
||||||
|
"-I" + libcDir + "/libc-top-half/musl/arch/wasm32",
|
||||||
|
"-I" + libcDir + "/libc-top-half/musl/arch/generic",
|
||||||
|
"-I" + libcDir + "/libc-top-half/headers/private",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cflagsForFile: func(path string) []string {
|
||||||
|
if strings.HasPrefix(path, "libc-bottom-half"+string(os.PathSeparator)) {
|
||||||
|
libcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc")
|
||||||
|
return []string{
|
||||||
|
"-I" + libcDir + "/libc-bottom-half/headers/private",
|
||||||
|
"-I" + libcDir + "/libc-bottom-half/cloudlibc/src/include",
|
||||||
|
"-I" + libcDir + "/libc-bottom-half/cloudlibc/src",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
|
||||||
|
librarySources: func(target string, libcNeedsMalloc bool) ([]string, error) {
|
||||||
|
type filePattern struct {
|
||||||
|
glob string
|
||||||
|
exclude []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: LIBC_TOP_HALF_MUSL_SOURCES in the Makefile
|
||||||
|
globs := []filePattern{
|
||||||
|
// Top half: mostly musl sources.
|
||||||
|
{glob: "libc-top-half/sources/*.c"},
|
||||||
|
{glob: "libc-top-half/musl/src/conf/*.c"},
|
||||||
|
{glob: "libc-top-half/musl/src/internal/*.c", exclude: []string{
|
||||||
|
"procfdname.c", "syscall.c", "syscall_ret.c", "vdso.c", "version.c",
|
||||||
|
}},
|
||||||
|
{glob: "libc-top-half/musl/src/locale/*.c", exclude: []string{
|
||||||
|
"dcngettext.c", "textdomain.c", "bind_textdomain_codeset.c"}},
|
||||||
|
{glob: "libc-top-half/musl/src/math/*.c", exclude: []string{
|
||||||
|
"__signbit.c", "__signbitf.c", "__signbitl.c",
|
||||||
|
"__fpclassify.c", "__fpclassifyf.c", "__fpclassifyl.c",
|
||||||
|
"ceilf.c", "ceil.c",
|
||||||
|
"floorf.c", "floor.c",
|
||||||
|
"truncf.c", "trunc.c",
|
||||||
|
"rintf.c", "rint.c",
|
||||||
|
"nearbyintf.c", "nearbyint.c",
|
||||||
|
"sqrtf.c", "sqrt.c",
|
||||||
|
"fabsf.c", "fabs.c",
|
||||||
|
"copysignf.c", "copysign.c",
|
||||||
|
"fminf.c", "fmaxf.c",
|
||||||
|
"fmin.c", "fmax.c,",
|
||||||
|
}},
|
||||||
|
{glob: "libc-top-half/musl/src/multibyte/*.c"},
|
||||||
|
{glob: "libc-top-half/musl/src/stdio/*.c", exclude: []string{
|
||||||
|
"vfwscanf.c", "vfwprintf.c", // long double is unsupported
|
||||||
|
"__lockfile.c", "flockfile.c", "funlockfile.c", "ftrylockfile.c",
|
||||||
|
"rename.c",
|
||||||
|
"tmpnam.c", "tmpfile.c", "tempnam.c",
|
||||||
|
"popen.c", "pclose.c",
|
||||||
|
"remove.c",
|
||||||
|
"gets.c"}},
|
||||||
|
{glob: "libc-top-half/musl/src/stdlib/*.c"},
|
||||||
|
{glob: "libc-top-half/musl/src/string/*.c", exclude: []string{
|
||||||
|
"strsignal.c"}},
|
||||||
|
|
||||||
|
// Bottom half: connect top half to WASI equivalents.
|
||||||
|
{glob: "libc-bottom-half/cloudlibc/src/libc/*/*.c"},
|
||||||
|
{glob: "libc-bottom-half/cloudlibc/src/libc/sys/*/*.c"},
|
||||||
|
{glob: "libc-bottom-half/sources/*.c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're using the Boehm GC, so we need a heap implementation in the libc.
|
||||||
|
if libcNeedsMalloc {
|
||||||
|
globs = append(globs, filePattern{glob: "dlmalloc/src/dlmalloc.c"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: LIBC_TOP_HALF_MUSL_SOURCES in the Makefile
|
||||||
|
sources := []string{
|
||||||
|
"libc-top-half/musl/src/misc/a64l.c",
|
||||||
|
"libc-top-half/musl/src/misc/basename.c",
|
||||||
|
"libc-top-half/musl/src/misc/dirname.c",
|
||||||
|
"libc-top-half/musl/src/misc/ffs.c",
|
||||||
|
"libc-top-half/musl/src/misc/ffsl.c",
|
||||||
|
"libc-top-half/musl/src/misc/ffsll.c",
|
||||||
|
"libc-top-half/musl/src/misc/fmtmsg.c",
|
||||||
|
"libc-top-half/musl/src/misc/getdomainname.c",
|
||||||
|
"libc-top-half/musl/src/misc/gethostid.c",
|
||||||
|
"libc-top-half/musl/src/misc/getopt.c",
|
||||||
|
"libc-top-half/musl/src/misc/getopt_long.c",
|
||||||
|
"libc-top-half/musl/src/misc/getsubopt.c",
|
||||||
|
"libc-top-half/musl/src/misc/uname.c",
|
||||||
|
"libc-top-half/musl/src/misc/nftw.c",
|
||||||
|
"libc-top-half/musl/src/errno/strerror.c",
|
||||||
|
"libc-top-half/musl/src/network/htonl.c",
|
||||||
|
"libc-top-half/musl/src/network/htons.c",
|
||||||
|
"libc-top-half/musl/src/network/ntohl.c",
|
||||||
|
"libc-top-half/musl/src/network/ntohs.c",
|
||||||
|
"libc-top-half/musl/src/network/inet_ntop.c",
|
||||||
|
"libc-top-half/musl/src/network/inet_pton.c",
|
||||||
|
"libc-top-half/musl/src/network/inet_aton.c",
|
||||||
|
"libc-top-half/musl/src/network/in6addr_any.c",
|
||||||
|
"libc-top-half/musl/src/network/in6addr_loopback.c",
|
||||||
|
"libc-top-half/musl/src/fenv/fenv.c",
|
||||||
|
"libc-top-half/musl/src/fenv/fesetround.c",
|
||||||
|
"libc-top-half/musl/src/fenv/feupdateenv.c",
|
||||||
|
"libc-top-half/musl/src/fenv/fesetexceptflag.c",
|
||||||
|
"libc-top-half/musl/src/fenv/fegetexceptflag.c",
|
||||||
|
"libc-top-half/musl/src/fenv/feholdexcept.c",
|
||||||
|
"libc-top-half/musl/src/exit/exit.c",
|
||||||
|
"libc-top-half/musl/src/exit/atexit.c",
|
||||||
|
"libc-top-half/musl/src/exit/assert.c",
|
||||||
|
"libc-top-half/musl/src/exit/quick_exit.c",
|
||||||
|
"libc-top-half/musl/src/exit/at_quick_exit.c",
|
||||||
|
"libc-top-half/musl/src/time/strftime.c",
|
||||||
|
"libc-top-half/musl/src/time/asctime.c",
|
||||||
|
"libc-top-half/musl/src/time/asctime_r.c",
|
||||||
|
"libc-top-half/musl/src/time/ctime.c",
|
||||||
|
"libc-top-half/musl/src/time/ctime_r.c",
|
||||||
|
"libc-top-half/musl/src/time/wcsftime.c",
|
||||||
|
"libc-top-half/musl/src/time/strptime.c",
|
||||||
|
"libc-top-half/musl/src/time/difftime.c",
|
||||||
|
"libc-top-half/musl/src/time/timegm.c",
|
||||||
|
"libc-top-half/musl/src/time/ftime.c",
|
||||||
|
"libc-top-half/musl/src/time/gmtime.c",
|
||||||
|
"libc-top-half/musl/src/time/gmtime_r.c",
|
||||||
|
"libc-top-half/musl/src/time/timespec_get.c",
|
||||||
|
"libc-top-half/musl/src/time/getdate.c",
|
||||||
|
"libc-top-half/musl/src/time/localtime.c",
|
||||||
|
"libc-top-half/musl/src/time/localtime_r.c",
|
||||||
|
"libc-top-half/musl/src/time/mktime.c",
|
||||||
|
"libc-top-half/musl/src/time/__tm_to_secs.c",
|
||||||
|
"libc-top-half/musl/src/time/__month_to_secs.c",
|
||||||
|
"libc-top-half/musl/src/time/__secs_to_tm.c",
|
||||||
|
"libc-top-half/musl/src/time/__year_to_secs.c",
|
||||||
|
"libc-top-half/musl/src/time/__tz.c",
|
||||||
|
"libc-top-half/musl/src/fcntl/creat.c",
|
||||||
|
"libc-top-half/musl/src/dirent/alphasort.c",
|
||||||
|
"libc-top-half/musl/src/dirent/versionsort.c",
|
||||||
|
"libc-top-half/musl/src/env/__stack_chk_fail.c",
|
||||||
|
"libc-top-half/musl/src/env/clearenv.c",
|
||||||
|
"libc-top-half/musl/src/env/getenv.c",
|
||||||
|
"libc-top-half/musl/src/env/putenv.c",
|
||||||
|
"libc-top-half/musl/src/env/setenv.c",
|
||||||
|
"libc-top-half/musl/src/env/unsetenv.c",
|
||||||
|
"libc-top-half/musl/src/unistd/posix_close.c",
|
||||||
|
"libc-top-half/musl/src/stat/futimesat.c",
|
||||||
|
"libc-top-half/musl/src/legacy/getpagesize.c",
|
||||||
|
"libc-top-half/musl/src/thread/thrd_sleep.c",
|
||||||
|
}
|
||||||
|
|
||||||
|
basepath := goenv.Get("TINYGOROOT") + "/lib/wasi-libc/"
|
||||||
|
for _, pattern := range globs {
|
||||||
|
matches, err := filepath.Glob(basepath + pattern.glob)
|
||||||
|
if err != nil {
|
||||||
|
// From the documentation:
|
||||||
|
// > Glob ignores file system errors such as I/O errors reading
|
||||||
|
// > directories. The only possible returned error is
|
||||||
|
// > ErrBadPattern, when pattern is malformed.
|
||||||
|
// So the only possible error is when the (statically defined)
|
||||||
|
// pattern is wrong. In other words, a programming bug.
|
||||||
|
return nil, fmt.Errorf("wasi-libc: could not glob source dirs: %w", err)
|
||||||
|
}
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return nil, fmt.Errorf("wasi-libc: did not find any files for pattern %#v", pattern)
|
||||||
|
}
|
||||||
|
excludeSet := map[string]struct{}{}
|
||||||
|
for _, exclude := range pattern.exclude {
|
||||||
|
excludeSet[exclude] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, match := range matches {
|
||||||
|
if _, ok := excludeSet[filepath.Base(match)]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
relpath, err := filepath.Rel(basepath, match)
|
||||||
|
if err != nil {
|
||||||
|
// Not sure if this is even possible.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sources = append(sources, relpath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -29,6 +29,8 @@ var libWasmBuiltins = Library{
|
|||||||
"-Wall",
|
"-Wall",
|
||||||
"-std=gnu11",
|
"-std=gnu11",
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
|
"-mnontrapping-fptoint", // match wasm-unknown (default on in LLVM 20)
|
||||||
|
"-mno-bulk-memory", // same here
|
||||||
"-isystem", libcDir + "/libc-top-half/musl/arch/wasm32",
|
"-isystem", libcDir + "/libc-top-half/musl/arch/wasm32",
|
||||||
"-isystem", libcDir + "/libc-top-half/musl/arch/generic",
|
"-isystem", libcDir + "/libc-top-half/musl/arch/generic",
|
||||||
"-isystem", libcDir + "/libc-top-half/musl/src/internal",
|
"-isystem", libcDir + "/libc-top-half/musl/src/internal",
|
||||||
@@ -39,7 +41,7 @@ var libWasmBuiltins = Library{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
|
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/wasi-libc") },
|
||||||
librarySources: func(target string) ([]string, error) {
|
librarySources: func(target string, _ bool) ([]string, error) {
|
||||||
return []string{
|
return []string{
|
||||||
// memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and
|
// memory builtins needed for llvm.memcpy.*, llvm.memmove.*, and
|
||||||
// llvm.memset.* LLVM intrinsics.
|
// llvm.memset.* LLVM intrinsics.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18
|
//go:build !byollvm && llvm19
|
||||||
|
|
||||||
package cgo
|
package cgo
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19
|
||||||
|
|
||||||
|
package cgo
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux CFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib/llvm-20/include
|
||||||
|
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@20/include
|
||||||
|
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@20/include
|
||||||
|
#cgo freebsd CFLAGS: -I/usr/local/llvm20/include
|
||||||
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-20/lib -lclang
|
||||||
|
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@20/lib -lclang
|
||||||
|
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@20/lib -lclang
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm20/lib -lclang
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
+57
-20
@@ -8,12 +8,24 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/shlex"
|
"github.com/google/shlex"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Library versions. Whenever an existing library is changed, this number should
|
||||||
|
// be added/increased so that existing caches are invalidated.
|
||||||
|
//
|
||||||
|
// (This is a bit of a layering violation, this should really be part of the
|
||||||
|
// builder.Library struct but that's hard to do since we want to know the
|
||||||
|
// library path in advance in several places).
|
||||||
|
var libVersions = map[string]int{
|
||||||
|
"musl": 3,
|
||||||
|
"bdwgc": 2,
|
||||||
|
}
|
||||||
|
|
||||||
// Config keeps all configuration affecting the build in a single struct.
|
// Config keeps all configuration affecting the build in a single struct.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Options *Options
|
Options *Options
|
||||||
@@ -99,6 +111,11 @@ func (c *Config) BuildTags() []string {
|
|||||||
"math_big_pure_go", // to get math/big to work
|
"math_big_pure_go", // to get math/big to work
|
||||||
"gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package
|
"gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package
|
||||||
"serial." + c.Serial()}...) // used inside the machine package
|
"serial." + c.Serial()}...) // used inside the machine package
|
||||||
|
switch c.Scheduler() {
|
||||||
|
case "threads", "cores":
|
||||||
|
default:
|
||||||
|
tags = append(tags, "tinygo.unicore")
|
||||||
|
}
|
||||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||||
}
|
}
|
||||||
@@ -122,7 +139,7 @@ func (c *Config) GC() string {
|
|||||||
// that can be traced by the garbage collector.
|
// that can be traced by the garbage collector.
|
||||||
func (c *Config) NeedsStackObjects() bool {
|
func (c *Config) NeedsStackObjects() bool {
|
||||||
switch c.GC() {
|
switch c.GC() {
|
||||||
case "conservative", "custom", "precise":
|
case "conservative", "custom", "precise", "boehm":
|
||||||
for _, tag := range c.BuildTags() {
|
for _, tag := range c.BuildTags() {
|
||||||
if tag == "tinygo.wasm" {
|
if tag == "tinygo.wasm" {
|
||||||
return true
|
return true
|
||||||
@@ -247,10 +264,18 @@ func MuslArchitecture(triple string) string {
|
|||||||
return CanonicalArchName(triple)
|
return CanonicalArchName(triple)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LibcPath returns the path to the libc directory. The libc path will be either
|
// Returns true if the libc needs to include malloc, for the libcs where this
|
||||||
// a precompiled libc shipped with a TinyGo build, or a libc path in the cache
|
// matters.
|
||||||
// directory (which might not yet be built).
|
func (c *Config) LibcNeedsMalloc() bool {
|
||||||
func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
if c.GC() == "boehm" && c.Target.Libc == "wasi-libc" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// LibraryPath returns the path to the library build directory. The path will be
|
||||||
|
// a library path in the cache directory (which might not yet be built).
|
||||||
|
func (c *Config) LibraryPath(name string) string {
|
||||||
archname := c.Triple()
|
archname := c.Triple()
|
||||||
if c.CPU() != "" {
|
if c.CPU() != "" {
|
||||||
archname += "-" + c.CPU()
|
archname += "-" + c.CPU()
|
||||||
@@ -266,17 +291,19 @@ func (c *Config) LibcPath(name string) (path string, precompiled bool) {
|
|||||||
archname += "-" + c.Target.Libc
|
archname += "-" + c.Target.Libc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to load a precompiled library.
|
// Append a version string, if this library has a version.
|
||||||
precompiledDir := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", archname, name)
|
if v, ok := libVersions[name]; ok {
|
||||||
if _, err := os.Stat(precompiledDir); err == nil {
|
archname += "-v" + strconv.Itoa(v)
|
||||||
// Found a precompiled library for this OS/architecture. Return the path
|
}
|
||||||
// directly.
|
|
||||||
return precompiledDir, true
|
options := ""
|
||||||
|
if c.LibcNeedsMalloc() {
|
||||||
|
options += "+malloc"
|
||||||
}
|
}
|
||||||
|
|
||||||
// No precompiled library found. Determine the path name that will be used
|
// No precompiled library found. Determine the path name that will be used
|
||||||
// in the build cache.
|
// in the build cache.
|
||||||
return filepath.Join(goenv.Get("GOCACHE"), name+"-"+archname), false
|
return filepath.Join(goenv.Get("GOCACHE"), name+options+"-"+archname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultBinaryExtension returns the default extension for binaries, such as
|
// DefaultBinaryExtension returns the default extension for binaries, such as
|
||||||
@@ -360,7 +387,7 @@ func (c *Config) LibcCFlags() []string {
|
|||||||
case "picolibc":
|
case "picolibc":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||||
path, _ := c.LibcPath("picolibc")
|
path := c.LibraryPath("picolibc")
|
||||||
return []string{
|
return []string{
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", filepath.Join(path, "include"),
|
"-isystem", filepath.Join(path, "include"),
|
||||||
@@ -370,7 +397,7 @@ func (c *Config) LibcCFlags() []string {
|
|||||||
}
|
}
|
||||||
case "musl":
|
case "musl":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
path, _ := c.LibcPath("musl")
|
path := c.LibraryPath("musl")
|
||||||
arch := MuslArchitecture(c.Triple())
|
arch := MuslArchitecture(c.Triple())
|
||||||
return []string{
|
return []string{
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
@@ -380,26 +407,36 @@ func (c *Config) LibcCFlags() []string {
|
|||||||
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
||||||
}
|
}
|
||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
root := goenv.Get("TINYGOROOT")
|
path := c.LibraryPath("wasi-libc")
|
||||||
return []string{
|
return []string{
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", root + "/lib/wasi-libc/sysroot/include",
|
"-isystem", filepath.Join(path, "include"),
|
||||||
}
|
}
|
||||||
case "wasmbuiltins":
|
case "wasmbuiltins":
|
||||||
// nothing to add (library is purely for builtins)
|
// nothing to add (library is purely for builtins)
|
||||||
return nil
|
return nil
|
||||||
case "mingw-w64":
|
case "mingw-w64":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
path, _ := c.LibcPath("mingw-w64")
|
path := c.LibraryPath("mingw-w64")
|
||||||
return []string{
|
cflags := []string{
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", filepath.Join(path, "include"),
|
"-isystem", filepath.Join(path, "include"),
|
||||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "include"),
|
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "include"),
|
||||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||||
"-D_UCRT",
|
|
||||||
"-D_WIN32_WINNT=0x0a00", // target Windows 10
|
|
||||||
}
|
}
|
||||||
|
if c.GOARCH() == "386" {
|
||||||
|
cflags = append(cflags,
|
||||||
|
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
|
||||||
|
"-D_WIN32_WINNT=0x0501", // target Windows XP
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
cflags = append(cflags,
|
||||||
|
"-D_UCRT",
|
||||||
|
"-D_WIN32_WINNT=0x0a00", // target Windows 10
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return cflags
|
||||||
case "":
|
case "":
|
||||||
// No libc specified, nothing to add.
|
// No libc specified, nothing to add.
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
validBuildModeOptions = []string{"default", "c-shared", "wasi-legacy"}
|
validBuildModeOptions = []string{"default", "c-shared", "wasi-legacy"}
|
||||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise", "boehm"}
|
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise", "boehm"}
|
||||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
validSchedulerOptions = []string{"none", "tasks", "asyncify", "threads", "cores"}
|
||||||
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||||
validPrintSizeOptions = []string{"none", "short", "full", "html"}
|
validPrintSizeOptions = []string{"none", "short", "full", "html"}
|
||||||
validPanicStrategyOptions = []string{"print", "trap"}
|
validPanicStrategyOptions = []string{"print", "trap"}
|
||||||
@@ -43,6 +43,7 @@ type Options struct {
|
|||||||
PrintCommands func(cmd string, args ...string) `json:"-"`
|
PrintCommands func(cmd string, args ...string) `json:"-"`
|
||||||
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
Semaphore chan struct{} `json:"-"` // -p flag controls cap
|
||||||
Debug bool
|
Debug bool
|
||||||
|
Nobounds bool
|
||||||
PrintSizes string
|
PrintSizes string
|
||||||
PrintAllocs *regexp.Regexp // regexp string
|
PrintAllocs *regexp.Regexp // regexp string
|
||||||
PrintStacks bool
|
PrintStacks bool
|
||||||
@@ -52,7 +53,6 @@ type Options struct {
|
|||||||
Programmer string
|
Programmer string
|
||||||
OpenOCDCommands []string
|
OpenOCDCommands []string
|
||||||
LLVMFeatures string
|
LLVMFeatures string
|
||||||
PrintJSON bool
|
|
||||||
Monitor bool
|
Monitor bool
|
||||||
BaudRate int
|
BaudRate int
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
func TestVerifyOptions(t *testing.T) {
|
func TestVerifyOptions(t *testing.T) {
|
||||||
|
|
||||||
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise, boehm`)
|
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise, boehm`)
|
||||||
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
|
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify, threads, cores`)
|
||||||
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full, html`)
|
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full, html`)
|
||||||
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
|
||||||
|
|
||||||
|
|||||||
+24
-7
@@ -262,7 +262,6 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
GOOS: options.GOOS,
|
GOOS: options.GOOS,
|
||||||
GOARCH: options.GOARCH,
|
GOARCH: options.GOARCH,
|
||||||
BuildTags: []string{options.GOOS, options.GOARCH},
|
BuildTags: []string{options.GOOS, options.GOARCH},
|
||||||
Scheduler: "tasks",
|
|
||||||
Linker: "cc",
|
Linker: "cc",
|
||||||
DefaultStackSize: 1024 * 64, // 64kB
|
DefaultStackSize: 1024 * 64, // 64kB
|
||||||
GDB: []string{"gdb"},
|
GDB: []string{"gdb"},
|
||||||
@@ -386,6 +385,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
platformVersion = "11.0.0" // first macosx platform with arm64 support
|
platformVersion = "11.0.0" // first macosx platform with arm64 support
|
||||||
}
|
}
|
||||||
llvmvendor = "apple"
|
llvmvendor = "apple"
|
||||||
|
spec.Scheduler = "tasks"
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
spec.Libc = "darwin-libSystem"
|
spec.Libc = "darwin-libSystem"
|
||||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer to
|
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer to
|
||||||
@@ -404,6 +404,7 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
"src/runtime/signal.c")
|
"src/runtime/signal.c")
|
||||||
case "linux":
|
case "linux":
|
||||||
spec.GC = "boehm"
|
spec.GC = "boehm"
|
||||||
|
spec.Scheduler = "threads"
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
spec.RTLib = "compiler-rt"
|
spec.RTLib = "compiler-rt"
|
||||||
spec.Libc = "musl"
|
spec.Libc = "musl"
|
||||||
@@ -424,20 +425,30 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
}
|
}
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||||
"src/internal/futex/futex_linux.c",
|
"src/internal/futex/futex_linux.c",
|
||||||
|
"src/internal/task/task_threads.c",
|
||||||
"src/runtime/runtime_unix.c",
|
"src/runtime/runtime_unix.c",
|
||||||
"src/runtime/signal.c")
|
"src/runtime/signal.c")
|
||||||
case "windows":
|
case "windows":
|
||||||
spec.GC = "boehm"
|
spec.GC = "boehm"
|
||||||
|
spec.Scheduler = "tasks"
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
spec.Libc = "mingw-w64"
|
spec.Libc = "mingw-w64"
|
||||||
// Note: using a medium code model, low image base and no ASLR
|
|
||||||
// because Go doesn't really need those features. ASLR patches
|
|
||||||
// around issues for unsafe languages like C/C++ that are not
|
|
||||||
// normally present in Go (without explicitly opting in).
|
|
||||||
// For more discussion:
|
|
||||||
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
|
|
||||||
switch options.GOARCH {
|
switch options.GOARCH {
|
||||||
|
case "386":
|
||||||
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
|
"-m", "i386pe",
|
||||||
|
"--major-os-version", "4",
|
||||||
|
"--major-subsystem-version", "4",
|
||||||
|
)
|
||||||
|
// __udivdi3 is not present in ucrt it seems.
|
||||||
|
spec.RTLib = "compiler-rt"
|
||||||
case "amd64":
|
case "amd64":
|
||||||
|
// Note: using a medium code model, low image base and no ASLR
|
||||||
|
// because Go doesn't really need those features. ASLR patches
|
||||||
|
// around issues for unsafe languages like C/C++ that are not
|
||||||
|
// normally present in Go (without explicitly opting in).
|
||||||
|
// For more discussion:
|
||||||
|
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
|
||||||
spec.LDFlags = append(spec.LDFlags,
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
"-m", "i386pep",
|
"-m", "i386pep",
|
||||||
"--image-base", "0x400000",
|
"--image-base", "0x400000",
|
||||||
@@ -459,6 +470,12 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
|
|||||||
return nil, fmt.Errorf("unknown GOOS=%s", options.GOOS)
|
return nil, fmt.Errorf("unknown GOOS=%s", options.GOOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if spec.GC == "boehm" {
|
||||||
|
// Add this file only when needed. This fixes a build failure on
|
||||||
|
// Windows.
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_boehm.c")
|
||||||
|
}
|
||||||
|
|
||||||
// Target triples (which actually have four components, but are called
|
// Target triples (which actually have four components, but are called
|
||||||
// triples for historical reasons) have the form:
|
// triples for historical reasons) have the form:
|
||||||
// arch-vendor-os-environment
|
// arch-vendor-os-environment
|
||||||
|
|||||||
+9
-1
@@ -76,7 +76,15 @@ func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value,
|
|||||||
fragments := b.expandFormalParam(arg)
|
fragments := b.expandFormalParam(arg)
|
||||||
expanded = append(expanded, fragments...)
|
expanded = append(expanded, fragments...)
|
||||||
}
|
}
|
||||||
return b.CreateCall(fnType, fn, expanded, name)
|
call := b.CreateCall(fnType, fn, expanded, name)
|
||||||
|
if !fn.IsAFunction().IsNil() {
|
||||||
|
if cc := fn.FunctionCallConv(); cc != llvm.CCallConv {
|
||||||
|
// Set a different calling convention if needed.
|
||||||
|
// This is needed for GetModuleHandleExA on Windows, for example.
|
||||||
|
call.SetInstructionCallConv(cc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return call
|
||||||
}
|
}
|
||||||
|
|
||||||
// createInvoke is like createCall but continues execution at the landing pad if
|
// createInvoke is like createCall but continues execution at the landing pad if
|
||||||
|
|||||||
+39
-10
@@ -58,6 +58,7 @@ type Config struct {
|
|||||||
MaxStackAlloc uint64
|
MaxStackAlloc uint64
|
||||||
NeedsStackObjects bool
|
NeedsStackObjects bool
|
||||||
Debug bool // Whether to emit debug information in the LLVM module.
|
Debug bool // Whether to emit debug information in the LLVM module.
|
||||||
|
Nobounds bool // Whether to skip bounds checks
|
||||||
PanicStrategy string
|
PanicStrategy string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +389,7 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
|||||||
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||||
// getLLVMType instead.
|
// getLLVMType instead.
|
||||||
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||||
switch typ := goType.(type) {
|
switch typ := types.Unalias(goType).(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
elemType := c.getLLVMType(typ.Elem())
|
||||||
return llvm.ArrayType(elemType, int(typ.Len()))
|
return llvm.ArrayType(elemType, int(typ.Len()))
|
||||||
@@ -496,6 +497,21 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
|||||||
llvmType := c.getLLVMType(typ)
|
llvmType := c.getLLVMType(typ)
|
||||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
|
case *types.Alias:
|
||||||
|
// Implement types.Alias just like types.Named: by treating them like a
|
||||||
|
// C typedef.
|
||||||
|
temporaryMDNode := c.dibuilder.CreateReplaceableCompositeType(llvm.Metadata{}, llvm.DIReplaceableCompositeType{
|
||||||
|
Tag: dwarf.TagTypedef,
|
||||||
|
SizeInBits: sizeInBytes * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
|
})
|
||||||
|
c.ditypes[typ] = temporaryMDNode
|
||||||
|
md := c.dibuilder.CreateTypedef(llvm.DITypedef{
|
||||||
|
Type: c.getDIType(types.Unalias(typ)), // TODO: use typ.Rhs in Go 1.23
|
||||||
|
Name: typ.String(),
|
||||||
|
})
|
||||||
|
temporaryMDNode.ReplaceAllUsesWith(md)
|
||||||
|
return md
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
return c.dibuilder.CreateArrayType(llvm.DIArrayType{
|
return c.dibuilder.CreateArrayType(llvm.DIArrayType{
|
||||||
SizeInBits: sizeInBytes * 8,
|
SizeInBits: sizeInBytes * 8,
|
||||||
@@ -858,6 +874,11 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
// Interfaces don't have concrete methods.
|
// Interfaces don't have concrete methods.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if _, isalias := member.Type().(*types.Alias); isalias {
|
||||||
|
// Aliases don't need to be redefined, since they just refer to
|
||||||
|
// an already existing type whose methods will be defined.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Named type. We should make sure all methods are created.
|
// Named type. We should make sure all methods are created.
|
||||||
// This includes both functions with pointer receivers and those
|
// This includes both functions with pointer receivers and those
|
||||||
@@ -1835,15 +1856,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
//
|
//
|
||||||
// This is also where compiler intrinsics are implemented.
|
// This is also where compiler intrinsics are implemented.
|
||||||
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
var params []llvm.Value
|
// See if this is an intrinsic function that is handled specially.
|
||||||
for _, param := range instr.Args {
|
|
||||||
params = append(params, b.getValue(param, getPos(instr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to call the function directly for trivially static calls.
|
|
||||||
var callee, context llvm.Value
|
|
||||||
var calleeType llvm.Type
|
|
||||||
exported := false
|
|
||||||
if fn := instr.StaticCallee(); fn != nil {
|
if fn := instr.StaticCallee(); fn != nil {
|
||||||
// Direct function call, either to a named or anonymous (directly
|
// Direct function call, either to a named or anonymous (directly
|
||||||
// applied) function call. If it is anonymous, it may be a closure.
|
// applied) function call. If it is anonymous, it may be a closure.
|
||||||
@@ -1879,13 +1892,29 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
return llvm.ConstInt(b.ctx.Int8Type(), panicStrategy, false), nil
|
return llvm.ConstInt(b.ctx.Int8Type(), panicStrategy, false), nil
|
||||||
case name == "runtime/interrupt.New":
|
case name == "runtime/interrupt.New":
|
||||||
return b.createInterruptGlobal(instr)
|
return b.createInterruptGlobal(instr)
|
||||||
|
case name == "runtime.exportedFuncPtr":
|
||||||
|
_, ptr := b.getFunction(instr.Args[0].(*ssa.Function))
|
||||||
|
return b.CreatePtrToInt(ptr, b.uintptrType, ""), nil
|
||||||
|
case name == "(*runtime/interrupt.Checkpoint).Save":
|
||||||
|
return b.createInterruptCheckpoint(instr.Args[0]), nil
|
||||||
case name == "internal/abi.FuncPCABI0":
|
case name == "internal/abi.FuncPCABI0":
|
||||||
retval := b.createDarwinFuncPCABI0Call(instr)
|
retval := b.createDarwinFuncPCABI0Call(instr)
|
||||||
if !retval.IsNil() {
|
if !retval.IsNil() {
|
||||||
return retval, nil
|
return retval, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var params []llvm.Value
|
||||||
|
for _, param := range instr.Args {
|
||||||
|
params = append(params, b.getValue(param, getPos(instr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to call the function directly for trivially static calls.
|
||||||
|
var callee, context llvm.Value
|
||||||
|
var calleeType llvm.Type
|
||||||
|
exported := false
|
||||||
|
if fn := instr.StaticCallee(); fn != nil {
|
||||||
calleeType, callee = b.getFunction(fn)
|
calleeType, callee = b.getFunction(fn)
|
||||||
info := b.getFunctionInfo(fn)
|
info := b.getFunctionInfo(fn)
|
||||||
if callee.IsNil() {
|
if callee.IsNil() {
|
||||||
|
|||||||
+15
-6
@@ -103,10 +103,11 @@ func (b *builder) createLandingPad() {
|
|||||||
b.CreateBr(b.blockEntries[b.fn.Recover])
|
b.CreateBr(b.blockEntries[b.fn.Recover])
|
||||||
}
|
}
|
||||||
|
|
||||||
// createInvokeCheckpoint saves the function state at the given point, to
|
// Create a checkpoint (similar to setjmp). This emits inline assembly that
|
||||||
// continue at the landing pad if a panic happened. This is implemented using a
|
// stores the current program counter inside the ptr address (actually
|
||||||
// setjmp-like construct.
|
// ptr+sizeof(ptr)) and then returns a boolean indicating whether this is the
|
||||||
func (b *builder) createInvokeCheckpoint() {
|
// normal flow (false) or we jumped here from somewhere else (true).
|
||||||
|
func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
||||||
// Construct inline assembly equivalents of setjmp.
|
// Construct inline assembly equivalents of setjmp.
|
||||||
// The assembly works as follows:
|
// The assembly works as follows:
|
||||||
// * All registers (both callee-saved and caller saved) are clobbered
|
// * All registers (both callee-saved and caller saved) are clobbered
|
||||||
@@ -217,11 +218,19 @@ li a0, 0
|
|||||||
// This case should have been handled by b.supportsRecover().
|
// This case should have been handled by b.supportsRecover().
|
||||||
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
|
b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
|
||||||
}
|
}
|
||||||
asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
|
asmType := llvm.FunctionType(resultType, []llvm.Type{b.dataPtrType}, false)
|
||||||
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
|
||||||
result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp")
|
result := b.CreateCall(asmType, asm, []llvm.Value{ptr}, "setjmp")
|
||||||
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
|
||||||
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
|
||||||
|
return isZero
|
||||||
|
}
|
||||||
|
|
||||||
|
// createInvokeCheckpoint saves the function state at the given point, to
|
||||||
|
// continue at the landing pad if a panic happened. This is implemented using a
|
||||||
|
// setjmp-like construct.
|
||||||
|
func (b *builder) createInvokeCheckpoint() {
|
||||||
|
isZero := b.createCheckpoint(b.deferFrame)
|
||||||
continueBB := b.insertBasicBlock("")
|
continueBB := b.insertBasicBlock("")
|
||||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||||
b.SetInsertPointAtEnd(continueBB)
|
b.SetInsertPointAtEnd(continueBB)
|
||||||
|
|||||||
@@ -249,3 +249,15 @@ func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement runtime/interrupt.Checkpoint.Save. It needs to be implemented
|
||||||
|
// directly at the call site. If it isn't implemented directly at the call site
|
||||||
|
// (but instead through a function call), it might result in an overwritten
|
||||||
|
// stack in the non-jump return case.
|
||||||
|
func (b *builder) createInterruptCheckpoint(ptr ssa.Value) llvm.Value {
|
||||||
|
addr := b.getValue(ptr, ptr.Pos())
|
||||||
|
b.createNilCheck(ptr, addr, "deref")
|
||||||
|
stackPointer := b.readStackPointer()
|
||||||
|
b.CreateStore(stackPointer, addr)
|
||||||
|
return b.createCheckpoint(addr)
|
||||||
|
}
|
||||||
|
|||||||
@@ -122,6 +122,9 @@ func (c *compilerContext) pkgPathPtr(pkgpath string) llvm.Value {
|
|||||||
// This function returns a pointer to the 'kind' field (which might not be the
|
// This function returns a pointer to the 'kind' field (which might not be the
|
||||||
// first field in the struct).
|
// first field in the struct).
|
||||||
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||||
|
// Resolve alias types: alias types are resolved at compile time.
|
||||||
|
typ = types.Unalias(typ)
|
||||||
|
|
||||||
ms := c.program.MethodSets.MethodSet(typ)
|
ms := c.program.MethodSets.MethodSet(typ)
|
||||||
hasMethodSet := ms.Len() != 0
|
hasMethodSet := ms.Len() != 0
|
||||||
_, isInterface := typ.Underlying().(*types.Interface)
|
_, isInterface := typ.Underlying().(*types.Interface)
|
||||||
@@ -512,7 +515,7 @@ var basicTypeNames = [...]string{
|
|||||||
// interface lowering pass to assign type codes as expected by the reflect
|
// interface lowering pass to assign type codes as expected by the reflect
|
||||||
// package. See getTypeCodeNum.
|
// package. See getTypeCodeNum.
|
||||||
func getTypeCodeName(t types.Type) (string, bool) {
|
func getTypeCodeName(t types.Type) (string, bool) {
|
||||||
switch t := t.(type) {
|
switch t := types.Unalias(t).(type) {
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
if t.Obj().Parent() != t.Obj().Pkg().Scope() {
|
if t.Obj().Parent() != t.Obj().Pkg().Scope() {
|
||||||
return "named:" + t.String() + "$local", true
|
return "named:" + t.String() + "$local", true
|
||||||
@@ -942,7 +945,7 @@ func signature(sig *types.Signature) string {
|
|||||||
// normalization around `byte` vs `uint8` for example.
|
// normalization around `byte` vs `uint8` for example.
|
||||||
func typestring(t types.Type) string {
|
func typestring(t types.Type) string {
|
||||||
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
|
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
|
||||||
switch t := t.(type) {
|
switch t := types.Unalias(t).(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
|
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
|
|||||||
@@ -452,6 +452,21 @@ func (b *builder) readStackPointer() llvm.Value {
|
|||||||
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeStackPointer emits a LLVM intrinsic call that updates the current stack
|
||||||
|
// pointer.
|
||||||
|
func (b *builder) writeStackPointer(sp llvm.Value) {
|
||||||
|
name := "llvm.stackrestore.p0"
|
||||||
|
if llvmutil.Version() < 18 {
|
||||||
|
name = "llvm.stackrestore" // backwards compatibility with LLVM 17 and below
|
||||||
|
}
|
||||||
|
stackrestore := b.mod.NamedFunction(name)
|
||||||
|
if stackrestore.IsNil() {
|
||||||
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
|
||||||
|
stackrestore = llvm.AddFunction(b.mod, name, fnType)
|
||||||
|
}
|
||||||
|
b.CreateCall(stackrestore.GlobalValueType(), stackrestore, []llvm.Value{sp}, "")
|
||||||
|
}
|
||||||
|
|
||||||
// createZExtOrTrunc lets the input value fit in the output type bits, by zero
|
// createZExtOrTrunc lets the input value fit in the output type bits, by zero
|
||||||
// extending or truncating the integer.
|
// extending or truncating the integer.
|
||||||
func (b *builder) createZExtOrTrunc(value llvm.Value, t llvm.Type) llvm.Value {
|
func (b *builder) createZExtOrTrunc(value llvm.Value, t llvm.Type) llvm.Value {
|
||||||
|
|||||||
+1
-3
@@ -248,7 +248,7 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
// can be compared with runtime.memequal. Note that padding bytes are undef
|
// can be compared with runtime.memequal. Note that padding bytes are undef
|
||||||
// and can alter two "equal" structs being equal when compared with memequal.
|
// and can alter two "equal" structs being equal when compared with memequal.
|
||||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||||
switch keyType := keyType.(type) {
|
switch keyType := keyType.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
@@ -263,8 +263,6 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
|
|||||||
return true
|
return true
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
return hashmapIsBinaryKey(keyType.Elem())
|
return hashmapIsBinaryKey(keyType.Elem())
|
||||||
case *types.Named:
|
|
||||||
return hashmapIsBinaryKey(keyType.Underlying())
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,6 +208,12 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
// > circumstances, and should not be exposed to source languages.
|
// > circumstances, and should not be exposed to source languages.
|
||||||
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
|
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
|
||||||
}
|
}
|
||||||
|
case "GetModuleHandleExA", "GetProcAddress", "GetSystemInfo", "GetSystemTimeAsFileTime", "LoadLibraryExW", "QueryPerformanceCounter", "QueryPerformanceFrequency", "QueryUnbiasedInterruptTime", "SetEnvironmentVariableA", "Sleep", "SystemFunction036", "VirtualAlloc":
|
||||||
|
// On Windows we need to use a special calling convention for some
|
||||||
|
// external calls.
|
||||||
|
if c.GOOS == "windows" && c.GOARCH == "386" {
|
||||||
|
llvmFn.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// External/exported functions may not retain pointer values.
|
// External/exported functions may not retain pointer values.
|
||||||
@@ -438,6 +444,10 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Nobounds {
|
||||||
|
info.nobounds = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether this function can be used in //go:wasmimport or
|
// Check whether this function can be used in //go:wasmimport or
|
||||||
|
|||||||
+24
-1
@@ -268,6 +268,8 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
// The signature looks like this:
|
// The signature looks like this:
|
||||||
// func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
// func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||||
|
|
||||||
|
isI386 := strings.HasPrefix(b.Triple, "i386-")
|
||||||
|
|
||||||
// Prepare input values.
|
// Prepare input values.
|
||||||
var paramTypes []llvm.Type
|
var paramTypes []llvm.Type
|
||||||
var params []llvm.Value
|
var params []llvm.Value
|
||||||
@@ -285,11 +287,17 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
if setLastError.IsNil() {
|
if setLastError.IsNil() {
|
||||||
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
||||||
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
||||||
|
if isI386 {
|
||||||
|
setLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getLastError := b.mod.NamedFunction("GetLastError")
|
getLastError := b.mod.NamedFunction("GetLastError")
|
||||||
if getLastError.IsNil() {
|
if getLastError.IsNil() {
|
||||||
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
||||||
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
||||||
|
if isI386 {
|
||||||
|
getLastError.SetFunctionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now do the actual call. Pseudocode:
|
// Now do the actual call. Pseudocode:
|
||||||
@@ -300,9 +308,24 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
// Note that SetLastError/GetLastError could be replaced with direct
|
// Note that SetLastError/GetLastError could be replaced with direct
|
||||||
// access to the thread control block, which is probably smaller and
|
// access to the thread control block, which is probably smaller and
|
||||||
// faster. The Go runtime does this in assembly.
|
// faster. The Go runtime does this in assembly.
|
||||||
b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
// On windows/386, we also need to save/restore the stack pointer. I'm
|
||||||
|
// not entirely sure why this is needed, but without it these calls
|
||||||
|
// change the stack pointer leading to a crash soon after.
|
||||||
|
setLastErrorCall := b.CreateCall(setLastError.GlobalValueType(), setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||||
|
var sp llvm.Value
|
||||||
|
if isI386 {
|
||||||
|
setLastErrorCall.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
sp = b.readStackPointer()
|
||||||
|
}
|
||||||
syscallResult := b.CreateCall(llvmType, fnPtr, params, "")
|
syscallResult := b.CreateCall(llvmType, fnPtr, params, "")
|
||||||
|
if isI386 {
|
||||||
|
syscallResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
b.writeStackPointer(sp)
|
||||||
|
}
|
||||||
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
errResult := b.CreateCall(getLastError.GlobalValueType(), getLastError, nil, "err")
|
||||||
|
if isI386 {
|
||||||
|
errResult.SetInstructionCallConv(llvm.X86StdcallCallConv)
|
||||||
|
}
|
||||||
if b.uintptrType != b.ctx.Int32Type() {
|
if b.uintptrType != b.ctx.Int32Type() {
|
||||||
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-20
@@ -10,6 +10,7 @@ import (
|
|||||||
"go/types"
|
"go/types"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -23,6 +24,11 @@ import (
|
|||||||
type Diagnostic struct {
|
type Diagnostic struct {
|
||||||
Pos token.Position
|
Pos token.Position
|
||||||
Msg string
|
Msg string
|
||||||
|
|
||||||
|
// Start and end position, if available. For many errors these positions are
|
||||||
|
// not available, but for some they are.
|
||||||
|
StartPos token.Position
|
||||||
|
EndPos token.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
// One or multiple errors of a particular package.
|
// One or multiple errors of a particular package.
|
||||||
@@ -114,12 +120,22 @@ func createPackageDiagnostic(err error) PackageDiagnostic {
|
|||||||
func createDiagnostics(err error) []Diagnostic {
|
func createDiagnostics(err error) []Diagnostic {
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case types.Error:
|
case types.Error:
|
||||||
return []Diagnostic{
|
diag := Diagnostic{
|
||||||
{
|
Pos: err.Fset.Position(err.Pos),
|
||||||
Pos: err.Fset.Position(err.Pos),
|
Msg: err.Msg,
|
||||||
Msg: err.Msg,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
// There is a special unexported API since Go 1.16 that provides the
|
||||||
|
// range (start and end position) where the type error exists.
|
||||||
|
// There is no promise of backwards compatibility in future Go versions
|
||||||
|
// so we have to be extra careful here to be resilient.
|
||||||
|
v := reflect.ValueOf(err)
|
||||||
|
start := v.FieldByName("go116start")
|
||||||
|
end := v.FieldByName("go116end")
|
||||||
|
if start.IsValid() && end.IsValid() && start.Int() != end.Int() {
|
||||||
|
diag.StartPos = err.Fset.Position(token.Pos(start.Int()))
|
||||||
|
diag.EndPos = err.Fset.Position(token.Pos(end.Int()))
|
||||||
|
}
|
||||||
|
return []Diagnostic{diag}
|
||||||
case scanner.Error:
|
case scanner.Error:
|
||||||
return []Diagnostic{
|
return []Diagnostic{
|
||||||
{
|
{
|
||||||
@@ -188,25 +204,29 @@ func (diag Diagnostic) WriteTo(w io.Writer, wd string) {
|
|||||||
fmt.Fprintln(w, diag.Msg)
|
fmt.Fprintln(w, diag.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pos := diag.Pos // make a copy
|
pos := RelativePosition(diag.Pos, wd)
|
||||||
if !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
|
|
||||||
// This file is not from the standard library (either the GOROOT or the
|
|
||||||
// TINYGOROOT). Make the path relative, for easier reading. Ignore any
|
|
||||||
// errors in the process (falling back to the absolute path).
|
|
||||||
pos.Filename = tryToMakePathRelative(pos.Filename, wd)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s: %s\n", pos, diag.Msg)
|
fmt.Fprintf(w, "%s: %s\n", pos, diag.Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to make the path relative to the current working directory. If any error
|
// Convert the position in pos (assumed to have an absolute path) into a
|
||||||
// occurs, this error is ignored and the absolute path is returned instead.
|
// relative path if possible. Paths inside GOROOT/TINYGOROOT will remain
|
||||||
func tryToMakePathRelative(dir, wd string) string {
|
// absolute.
|
||||||
|
func RelativePosition(pos token.Position, wd string) token.Position {
|
||||||
|
// Check whether we even have a working directory.
|
||||||
if wd == "" {
|
if wd == "" {
|
||||||
return dir // working directory not found
|
return pos
|
||||||
}
|
}
|
||||||
relpath, err := filepath.Rel(wd, dir)
|
|
||||||
if err != nil {
|
// Paths inside GOROOT should be printed in full.
|
||||||
return dir
|
if strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) || strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
|
||||||
|
return pos
|
||||||
}
|
}
|
||||||
return relpath
|
|
||||||
|
// Make the path relative, for easier reading. Ignore any errors in the
|
||||||
|
// process (falling back to the absolute path).
|
||||||
|
relpath, err := filepath.Rel(wd, pos.Filename)
|
||||||
|
if err == nil {
|
||||||
|
pos.Filename = relpath
|
||||||
|
}
|
||||||
|
return pos
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/builder"
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/diagnostics"
|
"github.com/tinygo-org/tinygo/diagnostics"
|
||||||
)
|
)
|
||||||
@@ -64,7 +65,11 @@ func testErrorMessages(t *testing.T, filename string, options *compileopts.Optio
|
|||||||
|
|
||||||
// Try to build a binary (this should fail with an error).
|
// Try to build a binary (this should fail with an error).
|
||||||
tmpdir := t.TempDir()
|
tmpdir := t.TempDir()
|
||||||
err := Build(filename, tmpdir+"/out", options)
|
config, err := builder.NewConfig(options)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("expected to get a compiler error")
|
||||||
|
}
|
||||||
|
err = Build(filename, tmpdir+"/out", config)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected to get a compiler error")
|
t.Fatal("expected to get a compiler error")
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+4
-4
@@ -20,16 +20,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1728500571,
|
"lastModified": 1747953325,
|
||||||
"narHash": "sha256-dOymOQ3AfNI4Z337yEwHGohrVQb4yPODCW9MDUyAc4w=",
|
"narHash": "sha256-y2ZtlIlNTuVJUZCqzZAhIw5rrKP4DOSklev6c8PyCkQ=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d51c28603def282a24fa034bcb007e2bcb5b5dd0",
|
"rev": "55d1f923c480dadce40f5231feb472e81b0bab48",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
"ref": "nixos-24.05",
|
"ref": "nixos-25.05",
|
||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
# make llvm-source # fetch compiler-rt
|
# make llvm-source # fetch compiler-rt
|
||||||
# git submodule update --init # fetch lots of other libraries and SVD files
|
# git submodule update --init # fetch lots of other libraries and SVD files
|
||||||
# make gen-device -j4 # build src/device/*/*.go files
|
# make gen-device -j4 # build src/device/*/*.go files
|
||||||
# make wasi-libc # build support for wasi/wasm
|
|
||||||
#
|
#
|
||||||
# With this, you should have an environment that can compile anything - except
|
# With this, you should have an environment that can compile anything - except
|
||||||
# for the Xtensa architecture (ESP8266/ESP32) because support for that lives in
|
# for the Xtensa architecture (ESP8266/ESP32) because support for that lives in
|
||||||
@@ -35,7 +34,7 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
# Use a recent stable release, but fix the version to make it reproducible.
|
# Use a recent stable release, but fix the version to make it reproducible.
|
||||||
# This version should be updated from time to time.
|
# This version should be updated from time to time.
|
||||||
nixpkgs.url = "nixpkgs/nixos-24.05";
|
nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
@@ -49,11 +48,11 @@
|
|||||||
buildInputs = [
|
buildInputs = [
|
||||||
# These dependencies are required for building tinygo (go install).
|
# These dependencies are required for building tinygo (go install).
|
||||||
go
|
go
|
||||||
llvmPackages_18.llvm
|
llvmPackages_20.llvm
|
||||||
llvmPackages_18.libclang
|
llvmPackages_20.libclang
|
||||||
# Additional dependencies needed at runtime, for building and/or
|
# Additional dependencies needed at runtime, for building and/or
|
||||||
# flashing.
|
# flashing.
|
||||||
llvmPackages_18.lld
|
llvmPackages_20.lld
|
||||||
avrdude
|
avrdude
|
||||||
binaryen
|
binaryen
|
||||||
# Additional dependencies needed for on-chip debugging.
|
# Additional dependencies needed for on-chip debugging.
|
||||||
@@ -64,20 +63,12 @@
|
|||||||
#openocd
|
#openocd
|
||||||
];
|
];
|
||||||
shellHook= ''
|
shellHook= ''
|
||||||
# Configure CLANG, LLVM_AR, and LLVM_NM for `make wasi-libc`.
|
|
||||||
# Without setting these explicitly, Homebrew versions might be used
|
|
||||||
# or the default `ar` and `nm` tools might be used (which don't
|
|
||||||
# support wasi).
|
|
||||||
export CLANG="clang-18 -resource-dir ${llvmPackages_18.clang.cc.lib}/lib/clang/18"
|
|
||||||
export LLVM_AR=llvm-ar
|
|
||||||
export LLVM_NM=llvm-nm
|
|
||||||
|
|
||||||
# Make `make smoketest` work (the default is `md5`, while Nix only
|
# Make `make smoketest` work (the default is `md5`, while Nix only
|
||||||
# has `md5sum`).
|
# has `md5sum`).
|
||||||
export MD5SUM=md5sum
|
export MD5SUM=md5sum
|
||||||
|
|
||||||
# Ugly hack to make the Clang resources directory available.
|
# Ugly hack to make the Clang resources directory available.
|
||||||
export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_18.clang.cc.lib}/lib/clang/18\" -tags=llvm18"
|
export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_20.clang.cc.lib}/lib/clang/20\" -tags=llvm20"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
module github.com/tinygo-org/tinygo
|
module github.com/tinygo-org/tinygo
|
||||||
|
|
||||||
go 1.19
|
go 1.22.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aykevl/go-wasm v0.0.2-0.20240825160117-b76c3f9f0982
|
github.com/aykevl/go-wasm v0.0.2-0.20250317121156-42b86c494139
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||||
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee
|
github.com/chromedp/cdproto v0.0.0-20220113222801-0725d94bb6ee
|
||||||
github.com/chromedp/chromedp v0.7.6
|
github.com/chromedp/chromedp v0.7.6
|
||||||
@@ -16,11 +16,11 @@ require (
|
|||||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
|
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
|
||||||
github.com/tetratelabs/wazero v1.6.0
|
github.com/tetratelabs/wazero v1.6.0
|
||||||
go.bug.st/serial v1.6.0
|
go.bug.st/serial v1.6.0
|
||||||
golang.org/x/net v0.26.0
|
golang.org/x/net v0.35.0
|
||||||
golang.org/x/sys v0.21.0
|
golang.org/x/sys v0.30.0
|
||||||
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504
|
golang.org/x/tools v0.30.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
tinygo.org/x/go-llvm v0.0.0-20250119132755-9dca92dfb4f9
|
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -33,5 +33,5 @@ require (
|
|||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/stretchr/testify v1.8.4 // indirect
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
github.com/aykevl/go-wasm v0.0.2-0.20240825160117-b76c3f9f0982 h1:cD7QfvrJdYmBw2tFP/VyKPT8ZESlcrwSwo7SvH9Y4dc=
|
github.com/aykevl/go-wasm v0.0.2-0.20250317121156-42b86c494139 h1:2O/WuAt8J5id3khcAtVB90czG80m+v0sfkLE07GrCVg=
|
||||||
github.com/aykevl/go-wasm v0.0.2-0.20240825160117-b76c3f9f0982/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
github.com/aykevl/go-wasm v0.0.2-0.20250317121156-42b86c494139/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||||
github.com/chromedp/cdproto v0.0.0-20211126220118-81fa0469ad77/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
github.com/chromedp/cdproto v0.0.0-20211126220118-81fa0469ad77/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
||||||
@@ -12,6 +12,7 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
|
|||||||
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
||||||
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||||
@@ -44,6 +45,7 @@ github.com/mattn/go-tty v0.0.4/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3px
|
|||||||
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5 h1:1SoBaSPudixRecmlHXb/GxmaD3fLMtHIDN13QujwQuc=
|
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5 h1:1SoBaSPudixRecmlHXb/GxmaD3fLMtHIDN13QujwQuc=
|
||||||
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
|
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
|
||||||
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
|
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
@@ -52,10 +54,12 @@ github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzl
|
|||||||
github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
|
github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
|
||||||
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
|
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
|
||||||
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -63,16 +67,17 @@ golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504 h1:MMsD8mMfluf/578+3wrTn22pjI/Xkzm+gPW47SYfspY=
|
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||||
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20250119132755-9dca92dfb4f9 h1:rMvEzuCYjyiR+pmdiCVWTQw3L6VqiSIXoL19I3lYufE=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20250119132755-9dca92dfb4f9/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74 h1:ovavgTdIBWCH8YWlcfq9gkpoyT1+IxMKSn+Df27QwE8=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
// Version of TinyGo.
|
// Version of TinyGo.
|
||||||
// Update this value before release of new version of software.
|
// Update this value before release of new version of software.
|
||||||
const version = "0.38.0-dev"
|
const version = "0.38.0"
|
||||||
|
|
||||||
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
||||||
// (like 0.30.0-dev-abcd012).
|
// (like 0.30.0-dev-abcd012).
|
||||||
|
|||||||
+14
-1
@@ -364,9 +364,22 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
nBytes := uint32(n * elemSize)
|
nBytes := uint32(n * elemSize)
|
||||||
|
srcObj := mem.get(src.index())
|
||||||
dstObj := mem.getWritable(dst.index())
|
dstObj := mem.getWritable(dst.index())
|
||||||
|
if srcObj.buffer == nil || dstObj.buffer == nil {
|
||||||
|
// If the buffer is nil, it means the slice is external.
|
||||||
|
// This can happen for example when copying data out of
|
||||||
|
// a //go:embed slice, which is not available at interp
|
||||||
|
// time.
|
||||||
|
// See: https://github.com/tinygo-org/tinygo/issues/4895
|
||||||
|
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mem, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
dstBuf := dstObj.buffer.asRawValue(r)
|
dstBuf := dstObj.buffer.asRawValue(r)
|
||||||
srcBuf := mem.get(src.index()).buffer.asRawValue(r)
|
srcBuf := srcObj.buffer.asRawValue(r)
|
||||||
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
||||||
dstObj.buffer = dstBuf
|
dstObj.buffer = dstBuf
|
||||||
mem.put(dst.index(), dstObj)
|
mem.put(dst.index(), dstObj)
|
||||||
|
|||||||
Vendored
+18
@@ -11,6 +11,10 @@ target triple = "x86_64--linux"
|
|||||||
@main.sliceDstUntaint.buf = internal global [2 x i8] zeroinitializer
|
@main.sliceDstUntaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
|
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
|
||||||
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
|
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcExternal1.buf = external global [2 x i8]
|
||||||
|
@main.sliceDstExternal1.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcExternal2.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceDstExternal2.buf = external global [2 x i8]
|
||||||
|
|
||||||
declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
|
declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
|
||||||
|
|
||||||
@@ -58,6 +62,14 @@ entry:
|
|||||||
%sliceDstTaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstTaint.buf, i32 0)
|
%sliceDstTaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstTaint.buf, i32 0)
|
||||||
call void @runtime.printuint8(i8 %sliceDstTaint.val)
|
call void @runtime.printuint8(i8 %sliceDstTaint.val)
|
||||||
|
|
||||||
|
; print(sliceDstExternal1[0])
|
||||||
|
%sliceDstExternal1.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstExternal1.buf, i32 0)
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstExternal1.val)
|
||||||
|
|
||||||
|
; print(sliceDstExternal2[0])
|
||||||
|
%sliceDstExternal2.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstExternal2.buf, i32 0)
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstExternal2.val)
|
||||||
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,5 +114,11 @@ entry:
|
|||||||
call void @use(ptr @main.sliceSrcTaint.buf)
|
call void @use(ptr @main.sliceSrcTaint.buf)
|
||||||
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
|
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
|
||||||
|
|
||||||
|
; Test that copying from or into external buffers works correctly.
|
||||||
|
; These copy operations must be done at runtime.
|
||||||
|
; https://github.com/tinygo-org/tinygo/issues/4895
|
||||||
|
%copy.n5 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal1.buf, ptr @main.sliceSrcExternal1.buf, i64 2, i64 2, i64 1)
|
||||||
|
%copy.n6 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal2.buf, ptr @main.sliceSrcExternal2.buf, i64 2, i64 2, i64 1)
|
||||||
|
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+10
@@ -3,6 +3,10 @@ target triple = "x86_64--linux"
|
|||||||
|
|
||||||
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
|
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
|
||||||
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
|
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcExternal1.buf = external global [2 x i8]
|
||||||
|
@main.sliceDstExternal1.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceSrcExternal2.buf = internal global [2 x i8] zeroinitializer
|
||||||
|
@main.sliceDstExternal2.buf = external global [2 x i8]
|
||||||
|
|
||||||
declare i64 @runtime.sliceCopy(ptr, ptr, i64, i64, i64) unnamed_addr
|
declare i64 @runtime.sliceCopy(ptr, ptr, i64, i64, i64) unnamed_addr
|
||||||
|
|
||||||
@@ -16,6 +20,8 @@ define void @runtime.initAll() unnamed_addr {
|
|||||||
entry:
|
entry:
|
||||||
call void @use(ptr @main.sliceSrcTaint.buf)
|
call void @use(ptr @main.sliceSrcTaint.buf)
|
||||||
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
|
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
|
||||||
|
%copy.n5 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal1.buf, ptr @main.sliceSrcExternal1.buf, i64 2, i64 2, i64 1)
|
||||||
|
%copy.n6 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal2.buf, ptr @main.sliceSrcExternal2.buf, i64 2, i64 2, i64 1)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,5 +34,9 @@ entry:
|
|||||||
call void @runtime.printuint8(i8 97)
|
call void @runtime.printuint8(i8 97)
|
||||||
%sliceDstTaint.val = load i8, ptr @main.sliceDstTaint.buf, align 1
|
%sliceDstTaint.val = load i8, ptr @main.sliceDstTaint.buf, align 1
|
||||||
call void @runtime.printuint8(i8 %sliceDstTaint.val)
|
call void @runtime.printuint8(i8 %sliceDstTaint.val)
|
||||||
|
%sliceDstExternal1.val = load i8, ptr @main.sliceDstExternal1.buf, align 1
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstExternal1.val)
|
||||||
|
%sliceDstExternal2.val = load i8, ptr @main.sliceDstExternal2.buf, align 1
|
||||||
|
call void @runtime.printuint8(i8 %sliceDstExternal2.val)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
Submodule lib/bdwgc updated: d1ff06cc50...1166f11f7d
+3
-1
@@ -8,7 +8,9 @@ import (
|
|||||||
//
|
//
|
||||||
// The program must already be parsed and type-checked with the .Parse() method.
|
// The program must already be parsed and type-checked with the .Parse() method.
|
||||||
func (p *Program) LoadSSA() *ssa.Program {
|
func (p *Program) LoadSSA() *ssa.Program {
|
||||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
|
// TODO: re-enable SanityCheckFunctions when x/tools is upgraded to
|
||||||
|
// a version with a fix for https://golang.org/issues/73594.
|
||||||
|
prog := ssa.NewProgram(p.fset /*ssa.SanityCheckFunctions|*/, ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
|
||||||
|
|
||||||
for _, pkg := range p.sorted {
|
for _, pkg := range p.sorted {
|
||||||
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
||||||
|
|||||||
@@ -138,27 +138,13 @@ func printCommand(cmd string, args ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build compiles and links the given package and writes it to outpath.
|
// Build compiles and links the given package and writes it to outpath.
|
||||||
func Build(pkgName, outpath string, options *compileopts.Options) error {
|
func Build(pkgName, outpath string, config *compileopts.Config) error {
|
||||||
config, err := builder.NewConfig(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.PrintJSON {
|
|
||||||
b, err := json.MarshalIndent(config, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
handleCompilerError(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("%s\n", string(b))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a temporary directory for intermediary files.
|
// Create a temporary directory for intermediary files.
|
||||||
tmpdir, err := os.MkdirTemp("", "tinygo")
|
tmpdir, err := os.MkdirTemp("", "tinygo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !options.Work {
|
if !config.Options.Work {
|
||||||
defer os.RemoveAll(tmpdir)
|
defer os.RemoveAll(tmpdir)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1027,15 +1013,50 @@ func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
|
|||||||
return nil, fmt.Errorf("could not list mount points: %w", err)
|
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||||
}
|
}
|
||||||
for _, elem := range list {
|
for _, elem := range list {
|
||||||
// TODO: find a way to check for the filesystem type.
|
volumePath := filepath.Join("/Volumes", elem.Name())
|
||||||
// (Only return FAT filesystems).
|
if _, err := os.Stat(volumePath); err != nil {
|
||||||
points = append(points, mountPoint{
|
continue
|
||||||
name: elem.Name(),
|
}
|
||||||
path: filepath.Join("/Volumes", elem.Name()),
|
|
||||||
})
|
cmd := exec.Command("diskutil", "info", volumePath)
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
continue // skip if diskutil failed
|
||||||
|
}
|
||||||
|
|
||||||
|
diskInfo := map[string]string{}
|
||||||
|
scanner := bufio.NewScanner(&out)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
parts := strings.SplitN(line, ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := strings.TrimSpace(parts[0])
|
||||||
|
value := strings.TrimSpace(parts[1])
|
||||||
|
diskInfo[key] = value
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
volName, okv := diskInfo["Volume Name"]
|
||||||
|
fsType, okf := diskInfo["File System Personality"]
|
||||||
|
if !okv || !okf {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a filesystem type is FAT-based
|
||||||
|
if strings.Contains(strings.ToUpper(fsType), "FAT") {
|
||||||
|
points = append(points, mountPoint{
|
||||||
|
name: volName,
|
||||||
|
path: volumePath,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sort.Slice(points, func(i, j int) bool {
|
sort.Slice(points, func(i, j int) bool {
|
||||||
return points[i].path < points[j].name
|
return points[i].path < points[j].path
|
||||||
})
|
})
|
||||||
return points, nil
|
return points, nil
|
||||||
case "linux":
|
case "linux":
|
||||||
@@ -1396,6 +1417,68 @@ func usage(command string) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print diagnostics very similar to the -json flag in Go.
|
||||||
|
func printBuildOutput(err error, jsonDiagnostics bool) {
|
||||||
|
if err == nil {
|
||||||
|
return // nothing to report
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsonDiagnostics {
|
||||||
|
workingDir, getwdErr := os.Getwd()
|
||||||
|
if getwdErr != nil {
|
||||||
|
workingDir = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonDiagnosticOutput struct {
|
||||||
|
ImportPath string
|
||||||
|
Action string
|
||||||
|
Output string `json:",omitempty"`
|
||||||
|
StartPos string `json:",omitempty"` // non-standard
|
||||||
|
EndPos string `json:",omitempty"` // non-standard
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, diags := range diagnostics.CreateDiagnostics(err) {
|
||||||
|
if diags.ImportPath != "" {
|
||||||
|
output, _ := json.Marshal(jsonDiagnosticOutput{
|
||||||
|
ImportPath: diags.ImportPath,
|
||||||
|
Action: "build-output",
|
||||||
|
Output: "# " + diags.ImportPath + "\n",
|
||||||
|
})
|
||||||
|
os.Stdout.Write(append(output, '\n'))
|
||||||
|
}
|
||||||
|
for _, diag := range diags.Diagnostics {
|
||||||
|
w := &bytes.Buffer{}
|
||||||
|
diag.WriteTo(w, workingDir)
|
||||||
|
data := jsonDiagnosticOutput{
|
||||||
|
ImportPath: diags.ImportPath,
|
||||||
|
Action: "build-output",
|
||||||
|
Output: w.String(),
|
||||||
|
}
|
||||||
|
if diag.StartPos.IsValid() && diag.EndPos.IsValid() {
|
||||||
|
// Include the non-standard StartPos/EndPos values. These
|
||||||
|
// are useful for the TinyGo Playground to show better error
|
||||||
|
// messages.
|
||||||
|
data.StartPos = diagnostics.RelativePosition(diag.StartPos, workingDir).String()
|
||||||
|
data.EndPos = diagnostics.RelativePosition(diag.EndPos, workingDir).String()
|
||||||
|
}
|
||||||
|
output, _ := json.Marshal(data)
|
||||||
|
os.Stdout.Write(append(output, '\n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the "Action":"build-fail" JSON.
|
||||||
|
output, _ := json.Marshal(jsonDiagnosticOutput{
|
||||||
|
ImportPath: diags.ImportPath,
|
||||||
|
Action: "build-fail",
|
||||||
|
})
|
||||||
|
os.Stdout.Write(append(output, '\n'))
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular diagnostic handling.
|
||||||
|
handleCompilerError(err)
|
||||||
|
}
|
||||||
|
|
||||||
func handleCompilerError(err error) {
|
func handleCompilerError(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wd, getwdErr := os.Getwd()
|
wd, getwdErr := os.Getwd()
|
||||||
@@ -1492,9 +1575,9 @@ func main() {
|
|||||||
command := os.Args[1]
|
command := os.Args[1]
|
||||||
|
|
||||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||||
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
|
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative, custom, precise, boehm)")
|
||||||
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
||||||
scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, asyncify)")
|
scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, cores, threads, asyncify)")
|
||||||
serial := flag.String("serial", "", "which serial output to use (none, uart, usb, rtt)")
|
serial := flag.String("serial", "", "which serial output to use (none, uart, usb, rtt)")
|
||||||
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
|
||||||
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
|
||||||
@@ -1512,8 +1595,10 @@ func main() {
|
|||||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||||
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
||||||
printCommands := flag.Bool("x", false, "Print commands")
|
printCommands := flag.Bool("x", false, "Print commands")
|
||||||
|
flagJSON := flag.Bool("json", false, "print output in JSON format")
|
||||||
parallelism := flag.Int("p", runtime.GOMAXPROCS(0), "the number of build jobs that can run in parallel")
|
parallelism := flag.Int("p", runtime.GOMAXPROCS(0), "the number of build jobs that can run in parallel")
|
||||||
nodebug := flag.Bool("no-debug", false, "strip debug information")
|
nodebug := flag.Bool("no-debug", false, "strip debug information")
|
||||||
|
nobounds := flag.Bool("nobounds", false, "do not emit bounds checks")
|
||||||
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
|
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
|
||||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||||
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
||||||
@@ -1536,10 +1621,7 @@ func main() {
|
|||||||
// development it can be useful to not emit debug information at all.
|
// development it can be useful to not emit debug information at all.
|
||||||
skipDwarf := flag.Bool("internal-nodwarf", false, "internal flag, use -no-debug instead")
|
skipDwarf := flag.Bool("internal-nodwarf", false, "internal flag, use -no-debug instead")
|
||||||
|
|
||||||
var flagJSON, flagDeps, flagTest bool
|
var flagDeps, flagTest bool
|
||||||
if command == "help" || command == "list" || command == "info" || command == "build" {
|
|
||||||
flag.BoolVar(&flagJSON, "json", false, "print data in JSON format")
|
|
||||||
}
|
|
||||||
if command == "help" || command == "list" {
|
if command == "help" || command == "list" {
|
||||||
flag.BoolVar(&flagDeps, "deps", false, "supply -deps flag to go list")
|
flag.BoolVar(&flagDeps, "deps", false, "supply -deps flag to go list")
|
||||||
flag.BoolVar(&flagTest, "test", false, "supply -test flag to go list")
|
flag.BoolVar(&flagTest, "test", false, "supply -test flag to go list")
|
||||||
@@ -1625,6 +1707,7 @@ func main() {
|
|||||||
SkipDWARF: *skipDwarf,
|
SkipDWARF: *skipDwarf,
|
||||||
Semaphore: make(chan struct{}, *parallelism),
|
Semaphore: make(chan struct{}, *parallelism),
|
||||||
Debug: !*nodebug,
|
Debug: !*nodebug,
|
||||||
|
Nobounds: *nobounds,
|
||||||
PrintSizes: *printSize,
|
PrintSizes: *printSize,
|
||||||
PrintStacks: *printStacks,
|
PrintStacks: *printStacks,
|
||||||
PrintAllocs: printAllocs,
|
PrintAllocs: printAllocs,
|
||||||
@@ -1634,7 +1717,6 @@ func main() {
|
|||||||
Programmer: *programmer,
|
Programmer: *programmer,
|
||||||
OpenOCDCommands: ocdCommands,
|
OpenOCDCommands: ocdCommands,
|
||||||
LLVMFeatures: *llvmFeatures,
|
LLVMFeatures: *llvmFeatures,
|
||||||
PrintJSON: flagJSON,
|
|
||||||
Monitor: *monitor,
|
Monitor: *monitor,
|
||||||
BaudRate: *baudrate,
|
BaudRate: *baudrate,
|
||||||
Timeout: *timeout,
|
Timeout: *timeout,
|
||||||
@@ -1689,13 +1771,15 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := Build(pkgName, outpath, options)
|
config, err := builder.NewConfig(options)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
|
err = Build(pkgName, outpath, config)
|
||||||
|
printBuildOutput(err, *flagJSON)
|
||||||
case "flash", "gdb", "lldb":
|
case "flash", "gdb", "lldb":
|
||||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||||
if command == "flash" {
|
if command == "flash" {
|
||||||
err := Flash(pkgName, *port, options)
|
err := Flash(pkgName, *port, options)
|
||||||
handleCompilerError(err)
|
printBuildOutput(err, *flagJSON)
|
||||||
} else {
|
} else {
|
||||||
if !options.Debug {
|
if !options.Debug {
|
||||||
fmt.Fprintln(os.Stderr, "Debug disabled while running debugger?")
|
fmt.Fprintln(os.Stderr, "Debug disabled while running debugger?")
|
||||||
@@ -1703,7 +1787,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err := Debug(command, pkgName, *ocdOutput, options)
|
err := Debug(command, pkgName, *ocdOutput, options)
|
||||||
handleCompilerError(err)
|
printBuildOutput(err, *flagJSON)
|
||||||
}
|
}
|
||||||
case "run":
|
case "run":
|
||||||
if flag.NArg() < 1 {
|
if flag.NArg() < 1 {
|
||||||
@@ -1713,7 +1797,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||||
err := Run(pkgName, options, flag.Args()[1:])
|
err := Run(pkgName, options, flag.Args()[1:])
|
||||||
handleCompilerError(err)
|
printBuildOutput(err, *flagJSON)
|
||||||
case "test":
|
case "test":
|
||||||
var pkgNames []string
|
var pkgNames []string
|
||||||
for i := 0; i < flag.NArg(); i++ {
|
for i := 0; i < flag.NArg(); i++ {
|
||||||
@@ -1847,7 +1931,7 @@ func main() {
|
|||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if flagJSON {
|
if *flagJSON {
|
||||||
json, _ := json.MarshalIndent(struct {
|
json, _ := json.MarshalIndent(struct {
|
||||||
Target *compileopts.TargetSpec `json:"target"`
|
Target *compileopts.TargetSpec `json:"target"`
|
||||||
GOROOT string `json:"goroot"`
|
GOROOT string `json:"goroot"`
|
||||||
@@ -1889,7 +1973,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
var extraArgs []string
|
var extraArgs []string
|
||||||
if flagJSON {
|
if *flagJSON {
|
||||||
extraArgs = append(extraArgs, "-json")
|
extraArgs = append(extraArgs, "-json")
|
||||||
}
|
}
|
||||||
if flagDeps {
|
if flagDeps {
|
||||||
|
|||||||
+56
-24
@@ -35,6 +35,8 @@ const TESTDATA = "testdata"
|
|||||||
|
|
||||||
var testTarget = flag.String("target", "", "override test target")
|
var testTarget = flag.String("target", "", "override test target")
|
||||||
|
|
||||||
|
var testOnlyCurrentOS = flag.Bool("only-current-os", false, "")
|
||||||
|
|
||||||
var supportedLinuxArches = map[string]string{
|
var supportedLinuxArches = map[string]string{
|
||||||
"AMD64Linux": "linux/amd64",
|
"AMD64Linux": "linux/amd64",
|
||||||
"X86Linux": "linux/386",
|
"X86Linux": "linux/386",
|
||||||
@@ -158,20 +160,51 @@ func TestBuild(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
if !*testOnlyCurrentOS {
|
||||||
t.Parallel()
|
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||||
runPlatTests(optionsFromTarget("cortex-m-qemu", sema), tests, t)
|
t.Parallel()
|
||||||
})
|
runPlatTests(optionsFromTarget("cortex-m-qemu", sema), tests, t)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("EmulatedRISCV", func(t *testing.T) {
|
t.Run("EmulatedRISCV", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runPlatTests(optionsFromTarget("riscv-qemu", sema), tests, t)
|
runPlatTests(optionsFromTarget("riscv-qemu", sema), tests, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("AVR", func(t *testing.T) {
|
t.Run("AVR", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
|
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("WebAssembly", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
runPlatTests(optionsFromTarget("wasm", sema), tests, t)
|
||||||
|
// Test with -gc=boehm.
|
||||||
|
t.Run("gc.go-boehm", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
optionsBoehm := optionsFromTarget("wasm", sema)
|
||||||
|
optionsBoehm.GC = "boehm"
|
||||||
|
runTest("gc.go", optionsBoehm, t, nil, nil)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("WASIp1", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
runPlatTests(optionsFromTarget("wasip1", sema), tests, t)
|
||||||
|
|
||||||
|
// Test with -gc=boehm.
|
||||||
|
t.Run("gc.go-boehm", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
optionsBoehm := optionsFromTarget("wasip1", sema)
|
||||||
|
optionsBoehm.GC = "boehm"
|
||||||
|
runTest("gc.go", optionsBoehm, t, nil, nil)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("WASIp2", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
runPlatTests(optionsFromTarget("wasip2", sema), tests, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
for name, osArch := range supportedLinuxArches {
|
for name, osArch := range supportedLinuxArches {
|
||||||
@@ -191,18 +224,13 @@ func TestBuild(t *testing.T) {
|
|||||||
options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
|
options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
|
||||||
runTest("cgo/", options, t, nil, nil)
|
runTest("cgo/", options, t, nil, nil)
|
||||||
})
|
})
|
||||||
t.Run("WebAssembly", func(t *testing.T) {
|
} else if runtime.GOOS == "windows" {
|
||||||
t.Parallel()
|
if runtime.GOARCH != "386" {
|
||||||
runPlatTests(optionsFromTarget("wasm", sema), tests, t)
|
t.Run("Windows386", func(t *testing.T) {
|
||||||
})
|
t.Parallel()
|
||||||
t.Run("WASI", func(t *testing.T) {
|
runPlatTests(optionsFromOSARCH("windows/386", sema), tests, t)
|
||||||
t.Parallel()
|
})
|
||||||
runPlatTests(optionsFromTarget("wasip1", sema), tests, t)
|
}
|
||||||
})
|
|
||||||
t.Run("WASIp2", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
runPlatTests(optionsFromTarget("wasip2", sema), tests, t)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,6 +703,10 @@ func TestWasmExport(t *testing.T) {
|
|||||||
// again.
|
// again.
|
||||||
checkResult("reentrantCall(2, 3)", mustCall(mod.ExportedFunction("reentrantCall").Call(ctx, 2, 3)), []uint64{5})
|
checkResult("reentrantCall(2, 3)", mustCall(mod.ExportedFunction("reentrantCall").Call(ctx, 2, 3)), []uint64{5})
|
||||||
checkResult("reentrantCall(1, 8)", mustCall(mod.ExportedFunction("reentrantCall").Call(ctx, 1, 8)), []uint64{9})
|
checkResult("reentrantCall(1, 8)", mustCall(mod.ExportedFunction("reentrantCall").Call(ctx, 1, 8)), []uint64{9})
|
||||||
|
|
||||||
|
// Check that goroutines started inside //go:wasmexport don't
|
||||||
|
// block the called function from returning.
|
||||||
|
checkResult("goroutineExit()", mustCall(mod.ExportedFunction("goroutineExit").Call(ctx)), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add wasip1 module.
|
// Add wasip1 module.
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package rand
|
package rand
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Reader = &reader{}
|
Reader = &reader{}
|
||||||
@@ -16,28 +19,22 @@ func (r *reader) Read(b []byte) (n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var randomByte uint32
|
// Use the old RtlGenRandom, introduced in Windows XP.
|
||||||
for i := range b {
|
// Even though the documentation says it is deprecated, it is widely used
|
||||||
// Call rand_s every four bytes because it's a C int (always 32-bit in
|
// and probably won't go away anytime soon.
|
||||||
// Windows).
|
// See for example: https://github.com/golang/go/issues/33542
|
||||||
if i%4 == 0 {
|
// For Windows 7 and newer, we might switch to ProcessPrng in the future
|
||||||
errCode := libc_rand_s(&randomByte)
|
// (which is a documented function and might be a tiny bit faster).
|
||||||
if errCode != 0 {
|
ok := libc_RtlGenRandom(unsafe.Pointer(&b[0]), len(b))
|
||||||
// According to the documentation, it can return an error.
|
if !ok {
|
||||||
return n, errRandom
|
return 0, errRandom
|
||||||
}
|
|
||||||
} else {
|
|
||||||
randomByte >>= 8
|
|
||||||
}
|
|
||||||
b[i] = byte(randomByte)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cryptographically secure random number generator.
|
// This function is part of advapi32.dll, and is called SystemFunction036 for
|
||||||
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s?view=msvc-170
|
// some reason. It's available on Windows XP and newer.
|
||||||
// errno_t rand_s(unsigned int* randomValue);
|
// See: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
|
||||||
//
|
//
|
||||||
//export rand_s
|
//export SystemFunction036
|
||||||
func libc_rand_s(randomValue *uint32) int32
|
func libc_RtlGenRandom(buf unsafe.Pointer, len int) bool
|
||||||
|
|||||||
@@ -146,6 +146,11 @@ const (
|
|||||||
SYST_CALIB_NOREF = 0x80000000 // Bit NOREF.
|
SYST_CALIB_NOREF = 0x80000000 // Bit NOREF.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ClearPendingIRQ clears the pending status of the interrupt.
|
||||||
|
func ClearPendingIRQ(irq uint32) {
|
||||||
|
NVIC.ICPR[irq>>5].Set(1 << (irq & 0x1F))
|
||||||
|
}
|
||||||
|
|
||||||
// Enable the given interrupt number.
|
// Enable the given interrupt number.
|
||||||
func EnableIRQ(irq uint32) {
|
func EnableIRQ(irq uint32) {
|
||||||
NVIC.ISER[irq>>5].Set(1 << (irq & 0x1F))
|
NVIC.ISER[irq>>5].Set(1 << (irq & 0x1F))
|
||||||
|
|||||||
@@ -275,3 +275,61 @@ const (
|
|||||||
DPC CSR = 0x7B1 // Debug PC.
|
DPC CSR = 0x7B1 // Debug PC.
|
||||||
DSCRATCH CSR = 0x7B2 // Debug scratch register.
|
DSCRATCH CSR = 0x7B2 // Debug scratch register.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Bitfields for the CSR registers above.
|
||||||
|
const (
|
||||||
|
// MSTATUS (common bits between RV32 and RV64)
|
||||||
|
MSTATUS_SIE = 1 << 1
|
||||||
|
MSTATUS_MIE = 1 << 3
|
||||||
|
MSTATUS_SPIE = 1 << 5
|
||||||
|
MSTATUS_UBE = 1 << 6
|
||||||
|
MSTATUS_MPIE = 1 << 7
|
||||||
|
MSTATUS_SPP = 1 << 8
|
||||||
|
MSTATUS_MPRV = 1 << 17
|
||||||
|
MSTATUS_SUM = 1 << 18
|
||||||
|
MSTATUS_MXR = 1 << 19
|
||||||
|
MSTATUS_TVM = 1 << 20
|
||||||
|
MSTATUS_TW = 1 << 21
|
||||||
|
MSTATUS_TSR = 1 << 22
|
||||||
|
|
||||||
|
MIE_SSIE = 1 << 1
|
||||||
|
MIE_MSIE = 1 << 3
|
||||||
|
MIE_STIE = 1 << 5
|
||||||
|
MIE_MTIE = 1 << 7
|
||||||
|
MIE_SEIE = 1 << 9
|
||||||
|
MIE_MEIE = 1 << 11
|
||||||
|
|
||||||
|
MIP_SSIP = 1 << 1
|
||||||
|
MIP_MSIP = 1 << 3
|
||||||
|
MIP_STIP = 1 << 5
|
||||||
|
MIP_MTIP = 1 << 7
|
||||||
|
MIP_SEIP = 1 << 9
|
||||||
|
MIP_MEIP = 1 << 11
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interrupt constants
|
||||||
|
const (
|
||||||
|
// MCAUSE values with the topmost bit (interrupt bit) set.
|
||||||
|
SupervisorSoftwareInterrupt = 1
|
||||||
|
MachineSoftwareInterrupt = 3
|
||||||
|
SupervisorTimerInterrupt = 5
|
||||||
|
MachineTimerInterrupt = 7
|
||||||
|
SupervisorExternalInterrupt = 9
|
||||||
|
MachineExternalInterrupt = 11
|
||||||
|
|
||||||
|
// MCAUSE values with the topmost bit (interrupt bit) clear.
|
||||||
|
InstructionAddressMisaligned = 0
|
||||||
|
InstructionAccessFault = 1
|
||||||
|
IllegalInstruction = 2
|
||||||
|
Breakpoint = 3
|
||||||
|
LoadAddressMisaligned = 4
|
||||||
|
LoadAccessFault = 5
|
||||||
|
StoreOrAMOAddressMisaligned = 6
|
||||||
|
StoreOrAMOAccessFault = 7
|
||||||
|
EnvironmentCallFromUMode = 8
|
||||||
|
EnvironmentCallFromSMode = 9
|
||||||
|
EnvironmentCallFromMMode = 11
|
||||||
|
InstructionPageFault = 12
|
||||||
|
LoadPageFault = 13
|
||||||
|
StoreOrAMOPageFault = 15
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,8 +3,47 @@
|
|||||||
.type _start,@function
|
.type _start,@function
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
|
// If we're on a multicore system, we need to wait for hart 0 to wake us up.
|
||||||
|
#if TINYGO_CORES > 1
|
||||||
|
csrr a0, mhartid
|
||||||
|
|
||||||
|
// Hart 0 stack
|
||||||
|
bnez a0, 1f
|
||||||
|
la sp, _stack_top
|
||||||
|
|
||||||
|
1:
|
||||||
|
// Hart 1 stack
|
||||||
|
li a1, 1
|
||||||
|
bne a0, a1, 2f
|
||||||
|
la sp, _stack1_top
|
||||||
|
|
||||||
|
2:
|
||||||
|
// Hart 2 stack
|
||||||
|
#if TINYGO_CORES >= 3
|
||||||
|
li a1, 2
|
||||||
|
bne a0, a1, 3f
|
||||||
|
la sp, _stack2_top
|
||||||
|
#endif
|
||||||
|
|
||||||
|
3:
|
||||||
|
// Hart 3 stack
|
||||||
|
#if TINYGO_CORES >= 4
|
||||||
|
li a1, 3
|
||||||
|
bne a0, a1, 4f
|
||||||
|
la sp, _stack3_top
|
||||||
|
#endif
|
||||||
|
|
||||||
|
4:
|
||||||
|
// done
|
||||||
|
|
||||||
|
#if TINYGO_CORES > 4
|
||||||
|
#error only up to 4 cores are supported at the moment!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
// Load the stack pointer.
|
// Load the stack pointer.
|
||||||
la sp, _stack_top
|
la sp, _stack_top
|
||||||
|
#endif
|
||||||
|
|
||||||
// Load the globals pointer. The program will load pointers relative to this
|
// Load the globals pointer. The program will load pointers relative to this
|
||||||
// register, so it must be set to the right value on startup.
|
// register, so it must be set to the right value on startup.
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// Hand created file. DO NOT DELETE.
|
||||||
|
// Definitions that are missing in src/device/rp/rp2040.go generated from SVDs
|
||||||
|
|
||||||
|
//go:build rp && rp2040
|
||||||
|
|
||||||
|
package rp
|
||||||
|
|
||||||
|
// DMA: 2.5.3.1. System DREQ Table from RP2040 Datasheet
|
||||||
|
// https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
|
||||||
|
const (
|
||||||
|
DREQ_PIO0_TX0 = 0
|
||||||
|
DREQ_PIO0_TX1 = 1
|
||||||
|
DREQ_PIO0_TX2 = 2
|
||||||
|
DREQ_PIO0_TX3 = 3
|
||||||
|
DREQ_PIO0_RX0 = 4
|
||||||
|
DREQ_PIO0_RX1 = 5
|
||||||
|
DREQ_PIO0_RX2 = 6
|
||||||
|
DREQ_PIO0_RX3 = 7
|
||||||
|
DREQ_PIO1_TX0 = 8
|
||||||
|
DREQ_PIO1_TX1 = 9
|
||||||
|
DREQ_PIO1_TX2 = 10
|
||||||
|
DREQ_PIO1_TX3 = 11
|
||||||
|
DREQ_PIO1_RX0 = 12
|
||||||
|
DREQ_PIO1_RX1 = 13
|
||||||
|
DREQ_PIO1_RX2 = 14
|
||||||
|
DREQ_PIO1_RX3 = 15
|
||||||
|
DREQ_SPI0_TX = 16
|
||||||
|
DREQ_SPI0_RX = 17
|
||||||
|
DREQ_SPI1_TX = 18
|
||||||
|
DREQ_SPI1_RX = 19
|
||||||
|
DREQ_UART0_TX = 20
|
||||||
|
DREQ_UART0_RX = 21
|
||||||
|
DREQ_UART1_TX = 22
|
||||||
|
DREQ_UART1_RX = 23
|
||||||
|
DREQ_PWM_WRAP0 = 24
|
||||||
|
DREQ_PWM_WRAP1 = 25
|
||||||
|
DREQ_PWM_WRAP2 = 26
|
||||||
|
DREQ_PWM_WRAP3 = 27
|
||||||
|
DREQ_PWM_WRAP4 = 28
|
||||||
|
DREQ_PWM_WRAP5 = 29
|
||||||
|
DREQ_PWM_WRAP6 = 30
|
||||||
|
DREQ_PWM_WRAP7 = 31
|
||||||
|
DREQ_I2C0_TX = 32
|
||||||
|
DREQ_I2C0_RX = 33
|
||||||
|
DREQ_I2C1_TX = 34
|
||||||
|
DREQ_I2C1_RX = 35
|
||||||
|
DREQ_ADC = 36
|
||||||
|
DREQ_XIP_STREAM = 37
|
||||||
|
DREQ_XIP_SSITX = 38
|
||||||
|
DREQ_XIP_SSIRX = 39
|
||||||
|
)
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// Hand created file. DO NOT DELETE.
|
||||||
|
// Definitions that are missing in src/device/rp/rp2350.go generated from SVDs
|
||||||
|
|
||||||
|
//go:build rp && rp2350
|
||||||
|
|
||||||
|
package rp
|
||||||
|
|
||||||
|
// DMA: 12.6.4.1. System DREQ Table from RP2350 Datasheet
|
||||||
|
// https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf
|
||||||
|
const (
|
||||||
|
DREQ_PIO0_TX0 = 0
|
||||||
|
DREQ_PIO0_TX1 = 1
|
||||||
|
DREQ_PIO0_TX2 = 2
|
||||||
|
DREQ_PIO0_TX3 = 3
|
||||||
|
DREQ_PIO0_RX0 = 4
|
||||||
|
DREQ_PIO0_RX1 = 5
|
||||||
|
DREQ_PIO0_RX2 = 6
|
||||||
|
DREQ_PIO0_RX3 = 7
|
||||||
|
DREQ_PIO1_TX0 = 8
|
||||||
|
DREQ_PIO1_TX1 = 9
|
||||||
|
DREQ_PIO1_TX2 = 10
|
||||||
|
DREQ_PIO1_TX3 = 11
|
||||||
|
DREQ_PIO1_RX0 = 12
|
||||||
|
DREQ_PIO1_RX1 = 13
|
||||||
|
DREQ_PIO1_RX2 = 14
|
||||||
|
DREQ_PIO1_RX3 = 15
|
||||||
|
DREQ_PIO2_TX0 = 16
|
||||||
|
DREQ_PIO2_TX1 = 17
|
||||||
|
DREQ_PIO2_TX2 = 18
|
||||||
|
DREQ_PIO2_TX3 = 19
|
||||||
|
DREQ_PIO2_RX0 = 20
|
||||||
|
DREQ_PIO2_RX1 = 21
|
||||||
|
DREQ_PIO2_RX2 = 22
|
||||||
|
DREQ_PIO2_RX3 = 23
|
||||||
|
DREQ_SPI0_TX = 24
|
||||||
|
DREQ_SPI0_RX = 25
|
||||||
|
DREQ_SPI1_TX = 26
|
||||||
|
DREQ_SPI1_RX = 27
|
||||||
|
DREQ_UART0_TX = 28
|
||||||
|
DREQ_UART0_RX = 29
|
||||||
|
DREQ_UART1_TX = 30
|
||||||
|
DREQ_UART1_RX = 31
|
||||||
|
DREQ_PWM_WRAP0 = 32
|
||||||
|
DREQ_PWM_WRAP1 = 33
|
||||||
|
DREQ_PWM_WRAP2 = 34
|
||||||
|
DREQ_PWM_WRAP3 = 35
|
||||||
|
DREQ_PWM_WRAP4 = 36
|
||||||
|
DREQ_PWM_WRAP5 = 37
|
||||||
|
DREQ_PWM_WRAP6 = 38
|
||||||
|
DREQ_PWM_WRAP7 = 39
|
||||||
|
DREQ_PWM_WRAP8 = 40
|
||||||
|
DREQ_PWM_WRAP9 = 41
|
||||||
|
DREQ_PWM_WRAP10 = 42
|
||||||
|
DREQ_PWM_WRAP11 = 43
|
||||||
|
DREQ_I2C0_TX = 44
|
||||||
|
DREQ_I2C0_RX = 45
|
||||||
|
DREQ_I2C1_TX = 46
|
||||||
|
DREQ_I2C1_RX = 47
|
||||||
|
DREQ_ADC = 48
|
||||||
|
DREQ_XIP_STREAM = 49
|
||||||
|
DREQ_XIP_QMITX = 50
|
||||||
|
DREQ_XIP_QMIRX = 51
|
||||||
|
DREQ_HSTX = 52
|
||||||
|
DREQ_CORESIGHT = 53
|
||||||
|
DREQ_SHA256 = 54
|
||||||
|
)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"machine/usb/msc"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
msc.Port(machine.Flash)
|
||||||
|
|
||||||
|
for {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build tinygo.unicore
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
// Atomics implementation for cooperative systems. The atomic types here aren't
|
// Atomics implementation for cooperative systems. The atomic types here aren't
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//go:build !tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// Atomics implementation for non-cooperative systems (multithreaded, etc).
|
||||||
|
// These atomic types use real atomic instructions.
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
type (
|
||||||
|
Uintptr = atomic.Uintptr
|
||||||
|
Uint32 = atomic.Uint32
|
||||||
|
Uint64 = atomic.Uint64
|
||||||
|
)
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build tinygo.unicore
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
// A futex is a way for userspace to wait with the pointer as the key, and for
|
// A futex is a way for userspace to wait with the pointer as the key, and for
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
//go:build scheduler.cores
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "runtime/interrupt"
|
||||||
|
|
||||||
|
// A futex is a way for userspace to wait with the pointer as the key, and for
|
||||||
|
// another thread to wake one or all waiting threads keyed on the same pointer.
|
||||||
|
//
|
||||||
|
// A futex does not change the underlying value, it only reads it before to prevent
|
||||||
|
// lost wake-ups.
|
||||||
|
type Futex struct {
|
||||||
|
Uint32
|
||||||
|
|
||||||
|
waiters Stack
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomically check for cmp to still be equal to the futex value and if so, go
|
||||||
|
// to sleep. Return true if we were definitely awoken by a call to Wake or
|
||||||
|
// WakeAll, and false if we can't be sure of that.
|
||||||
|
func (f *Futex) Wait(cmp uint32) (awoken bool) {
|
||||||
|
mask := lockFutex()
|
||||||
|
|
||||||
|
if f.Uint32.Load() != cmp {
|
||||||
|
unlockFutex(mask)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the current goroutine onto the waiter stack.
|
||||||
|
f.waiters.Push(Current())
|
||||||
|
|
||||||
|
unlockFutex(mask)
|
||||||
|
|
||||||
|
// Pause until this task is awoken by Wake/WakeAll.
|
||||||
|
Pause()
|
||||||
|
|
||||||
|
// We were awoken by a call to Wake or WakeAll. There is no chance for
|
||||||
|
// spurious wakeups.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake a single waiter.
|
||||||
|
func (f *Futex) Wake() {
|
||||||
|
mask := lockFutex()
|
||||||
|
if t := f.waiters.Pop(); t != nil {
|
||||||
|
scheduleTask(t)
|
||||||
|
}
|
||||||
|
unlockFutex(mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake all waiters.
|
||||||
|
func (f *Futex) WakeAll() {
|
||||||
|
mask := lockFutex()
|
||||||
|
for t := f.waiters.Pop(); t != nil; t = f.waiters.Pop() {
|
||||||
|
scheduleTask(t)
|
||||||
|
}
|
||||||
|
unlockFutex(mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname lockFutex runtime.lockFutex
|
||||||
|
func lockFutex() interrupt.State
|
||||||
|
|
||||||
|
//go:linkname unlockFutex runtime.unlockFutex
|
||||||
|
func unlockFutex(interrupt.State)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build scheduler.threads
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "internal/futex"
|
||||||
|
|
||||||
|
type Futex = futex.Futex
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build linux && !baremetal
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// Musl uses a pointer (or unsigned long for C++) so unsafe.Pointer should be
|
||||||
|
// fine.
|
||||||
|
type threadID unsafe.Pointer
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build tinygo.unicore
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
type Mutex struct {
|
type Mutex struct {
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
//go:build !tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// Futex-based mutex.
|
||||||
|
// This is largely based on the paper "Futexes are Tricky" by Ulrich Drepper.
|
||||||
|
// It describes a few ways to implement mutexes using a futex, and how some
|
||||||
|
// seemingly-obvious implementations don't exactly work as intended.
|
||||||
|
// Unfortunately, Go atomic operations work slightly differently so we can't
|
||||||
|
// copy the algorithm verbatim.
|
||||||
|
//
|
||||||
|
// The implementation works like this. The futex can have 3 different values,
|
||||||
|
// depending on the state:
|
||||||
|
//
|
||||||
|
// - 0: the futex is currently unlocked.
|
||||||
|
// - 1: the futex is locked, but is uncontended. There is one special case: if
|
||||||
|
// a contended futex is unlocked, it is set to 0. It is possible for another
|
||||||
|
// thread to lock the futex before the next waiter is woken. But because a
|
||||||
|
// waiter will be woken (if there is one), it will always change to 2
|
||||||
|
// regardless. So this is not a problem.
|
||||||
|
// - 2: the futex is locked, and is contended. At least one thread is trying
|
||||||
|
// to obtain the lock (and is in the contended loop, see below).
|
||||||
|
//
|
||||||
|
// For the paper, see:
|
||||||
|
// https://dept-info.labri.fr/~denis/Enseignement/2008-IR/Articles/01-futex.pdf)
|
||||||
|
|
||||||
|
type Mutex struct {
|
||||||
|
futex Futex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mutex) Lock() {
|
||||||
|
// Fast path: try to take an uncontended lock.
|
||||||
|
if m.futex.CompareAndSwap(0, 1) {
|
||||||
|
// We obtained the mutex.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The futex is contended, so we enter the contended loop.
|
||||||
|
// If we manage to change the futex from 0 to 2, we managed to take the
|
||||||
|
// lock. Else, we have to wait until a call to Unlock unlocks this mutex.
|
||||||
|
// (Unlock will wake one waiter when it finds the futex is set to 2 when
|
||||||
|
// unlocking).
|
||||||
|
for m.futex.Swap(2) != 0 {
|
||||||
|
// Wait until we get resumed in Unlock.
|
||||||
|
m.futex.Wait(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mutex) Unlock() {
|
||||||
|
if old := m.futex.Swap(0); old == 0 {
|
||||||
|
// Mutex wasn't locked before.
|
||||||
|
panic("sync: unlock of unlocked Mutex")
|
||||||
|
} else if old == 2 {
|
||||||
|
// Mutex was a contended lock, so we need to wake the next waiter.
|
||||||
|
m.futex.Wake()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock tries to lock m and reports whether it succeeded.
|
||||||
|
//
|
||||||
|
// Note that while correct uses of TryLock do exist, they are rare,
|
||||||
|
// and use of TryLock is often a sign of a deeper problem
|
||||||
|
// in a particular use of mutexes.
|
||||||
|
func (m *Mutex) TryLock() bool {
|
||||||
|
// Fast path: try to take an uncontended lock.
|
||||||
|
if m.futex.CompareAndSwap(0, 1) {
|
||||||
|
// We obtained the mutex.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build tinygo.unicore
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
// PMutex is a real mutex on systems that can be either preemptive or threaded,
|
// PMutex is a real mutex on systems that can be either preemptive or threaded,
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build !tinygo.unicore
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
// PMutex is a real mutex on systems that can be either preemptive or threaded,
|
||||||
|
// and a dummy lock on other (purely cooperative) systems.
|
||||||
|
//
|
||||||
|
// It is mainly useful for short operations that need a lock when threading may
|
||||||
|
// be involved, but which do not need a lock with a purely cooperative
|
||||||
|
// scheduler.
|
||||||
|
type PMutex = Mutex
|
||||||
+30
-17
@@ -12,9 +12,9 @@ type Queue struct {
|
|||||||
|
|
||||||
// Push a task onto the queue.
|
// Push a task onto the queue.
|
||||||
func (q *Queue) Push(t *Task) {
|
func (q *Queue) Push(t *Task) {
|
||||||
i := interrupt.Disable()
|
mask := lockAtomics()
|
||||||
if asserts && t.Next != nil {
|
if asserts && t.Next != nil {
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
panic("runtime: pushing a task to a queue with a non-nil Next pointer")
|
panic("runtime: pushing a task to a queue with a non-nil Next pointer")
|
||||||
}
|
}
|
||||||
if q.tail != nil {
|
if q.tail != nil {
|
||||||
@@ -25,15 +25,15 @@ func (q *Queue) Push(t *Task) {
|
|||||||
if q.head == nil {
|
if q.head == nil {
|
||||||
q.head = t
|
q.head = t
|
||||||
}
|
}
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop a task off of the queue.
|
// Pop a task off of the queue.
|
||||||
func (q *Queue) Pop() *Task {
|
func (q *Queue) Pop() *Task {
|
||||||
i := interrupt.Disable()
|
mask := lockAtomics()
|
||||||
t := q.head
|
t := q.head
|
||||||
if t == nil {
|
if t == nil {
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
q.head = t.Next
|
q.head = t.Next
|
||||||
@@ -41,13 +41,13 @@ func (q *Queue) Pop() *Task {
|
|||||||
q.tail = nil
|
q.tail = nil
|
||||||
}
|
}
|
||||||
t.Next = nil
|
t.Next = nil
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append pops the contents of another queue and pushes them onto the end of this queue.
|
// Append pops the contents of another queue and pushes them onto the end of this queue.
|
||||||
func (q *Queue) Append(other *Queue) {
|
func (q *Queue) Append(other *Queue) {
|
||||||
i := interrupt.Disable()
|
mask := lockAtomics()
|
||||||
if q.head == nil {
|
if q.head == nil {
|
||||||
q.head = other.head
|
q.head = other.head
|
||||||
} else {
|
} else {
|
||||||
@@ -55,14 +55,14 @@ func (q *Queue) Append(other *Queue) {
|
|||||||
}
|
}
|
||||||
q.tail = other.tail
|
q.tail = other.tail
|
||||||
other.head, other.tail = nil, nil
|
other.head, other.tail = nil, nil
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty checks if the queue is empty.
|
// Empty checks if the queue is empty.
|
||||||
func (q *Queue) Empty() bool {
|
func (q *Queue) Empty() bool {
|
||||||
i := interrupt.Disable()
|
mask := lockAtomics()
|
||||||
empty := q.head == nil
|
empty := q.head == nil
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
return empty
|
return empty
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,24 +75,24 @@ type Stack struct {
|
|||||||
|
|
||||||
// Push a task onto the stack.
|
// Push a task onto the stack.
|
||||||
func (s *Stack) Push(t *Task) {
|
func (s *Stack) Push(t *Task) {
|
||||||
i := interrupt.Disable()
|
mask := lockAtomics()
|
||||||
if asserts && t.Next != nil {
|
if asserts && t.Next != nil {
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
panic("runtime: pushing a task to a stack with a non-nil Next pointer")
|
panic("runtime: pushing a task to a stack with a non-nil Next pointer")
|
||||||
}
|
}
|
||||||
s.top, t.Next = t, s.top
|
s.top, t.Next = t, s.top
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop a task off of the stack.
|
// Pop a task off of the stack.
|
||||||
func (s *Stack) Pop() *Task {
|
func (s *Stack) Pop() *Task {
|
||||||
i := interrupt.Disable()
|
mask := lockAtomics()
|
||||||
t := s.top
|
t := s.top
|
||||||
if t != nil {
|
if t != nil {
|
||||||
s.top = t.Next
|
s.top = t.Next
|
||||||
t.Next = nil
|
t.Next = nil
|
||||||
}
|
}
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,13 +112,26 @@ func (t *Task) tail() *Task {
|
|||||||
// Queue moves the contents of the stack into a queue.
|
// Queue moves the contents of the stack into a queue.
|
||||||
// Elements can be popped from the queue in the same order that they would be popped from the stack.
|
// Elements can be popped from the queue in the same order that they would be popped from the stack.
|
||||||
func (s *Stack) Queue() Queue {
|
func (s *Stack) Queue() Queue {
|
||||||
i := interrupt.Disable()
|
mask := lockAtomics()
|
||||||
head := s.top
|
head := s.top
|
||||||
s.top = nil
|
s.top = nil
|
||||||
q := Queue{
|
q := Queue{
|
||||||
head: head,
|
head: head,
|
||||||
tail: head.tail(),
|
tail: head.tail(),
|
||||||
}
|
}
|
||||||
interrupt.Restore(i)
|
unlockAtomics(mask)
|
||||||
return q
|
return q
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use runtime.lockAtomics and runtime.unlockAtomics so that Queue and Stack
|
||||||
|
// work correctly even on multicore systems. These functions are normally used
|
||||||
|
// to implement atomic operations, but the same spinlock can also be used for
|
||||||
|
// Queue/Stack operations which are very fast.
|
||||||
|
// These functions are just plain old interrupt disable/restore on non-multicore
|
||||||
|
// systems.
|
||||||
|
|
||||||
|
//go:linkname lockAtomics runtime.lockAtomics
|
||||||
|
func lockAtomics() interrupt.State
|
||||||
|
|
||||||
|
//go:linkname unlockAtomics runtime.unlockAtomics
|
||||||
|
func unlockAtomics(mask interrupt.State)
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
// Barebones semaphore implementation.
|
||||||
|
// The main limitation is that if there are multiple waiters, a single Post()
|
||||||
|
// call won't do anything. Only when Post() has been called to awaken all
|
||||||
|
// waiters will the waiters proceed.
|
||||||
|
// This limitation is not a problem when there will only be a single waiter.
|
||||||
|
type Semaphore struct {
|
||||||
|
futex Futex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post (unlock) the semaphore, incrementing the value in the semaphore.
|
||||||
|
func (s *Semaphore) Post() {
|
||||||
|
newValue := s.futex.Add(1)
|
||||||
|
if newValue == 0 {
|
||||||
|
s.futex.WakeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait (lock) the semaphore, decrementing the value in the semaphore.
|
||||||
|
func (s *Semaphore) Wait() {
|
||||||
|
delta := int32(-1)
|
||||||
|
value := s.futex.Add(uint32(delta))
|
||||||
|
for {
|
||||||
|
if int32(value) >= 0 {
|
||||||
|
// Semaphore unlocked!
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.futex.Wait(value)
|
||||||
|
value = s.futex.Load()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,11 +24,28 @@ type Task struct {
|
|||||||
// This is needed for some crypto packages.
|
// This is needed for some crypto packages.
|
||||||
FipsIndicator uint8
|
FipsIndicator uint8
|
||||||
|
|
||||||
|
// State of the goroutine: running, paused, or must-resume-next-pause.
|
||||||
|
// This extra field doesn't increase memory usage on 32-bit CPUs and above,
|
||||||
|
// since it falls into the padding of the FipsIndicator bit above.
|
||||||
|
RunState uint8
|
||||||
|
|
||||||
// DeferFrame stores a pointer to the (stack allocated) defer frame of the
|
// DeferFrame stores a pointer to the (stack allocated) defer frame of the
|
||||||
// goroutine that is used for the recover builtin.
|
// goroutine that is used for the recover builtin.
|
||||||
DeferFrame unsafe.Pointer
|
DeferFrame unsafe.Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Initial state: the goroutine state is saved on the stack.
|
||||||
|
RunStatePaused = iota
|
||||||
|
|
||||||
|
// The goroutine is running right now.
|
||||||
|
RunStateRunning
|
||||||
|
|
||||||
|
// The goroutine is running, but already marked as "can resume".
|
||||||
|
// The next call to Pause() won't actually pause the goroutine.
|
||||||
|
RunStateResuming
|
||||||
|
)
|
||||||
|
|
||||||
// DataUint32 returns the Data field as a uint32. The value is only valid after
|
// DataUint32 returns the Data field as a uint32. The value is only valid after
|
||||||
// setting it through SetDataUint32 or by storing to it using DataAtomicUint32.
|
// setting it through SetDataUint32 or by storing to it using DataAtomicUint32.
|
||||||
func (t *Task) DataUint32() uint32 {
|
func (t *Task) DataUint32() uint32 {
|
||||||
|
|||||||
@@ -36,3 +36,9 @@ func OnSystemStack() bool {
|
|||||||
// This scheduler does not do any stack switching.
|
// This scheduler does not do any stack switching.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SystemStack() uintptr {
|
||||||
|
// System stack is the current stack, so this shouldn't be called.
|
||||||
|
runtimePanic("scheduler is disabled")
|
||||||
|
return 0 // unreachable
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
//go:build scheduler.tasks
|
//go:build scheduler.tasks || scheduler.cores
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime/interrupt"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,43 +31,12 @@ type state struct {
|
|||||||
canaryPtr *uintptr
|
canaryPtr *uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// currentTask is the current running task, or nil if currently in the scheduler.
|
//export tinygo_task_exit
|
||||||
var currentTask *Task
|
func taskExit() {
|
||||||
|
// TODO: explicitly free the stack after switching back to the scheduler.
|
||||||
// Current returns the current active task.
|
|
||||||
func Current() *Task {
|
|
||||||
return currentTask
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause suspends the current task and returns to the scheduler.
|
|
||||||
// This function may only be called when running on a goroutine stack, not when running on the system stack or in an interrupt.
|
|
||||||
func Pause() {
|
|
||||||
// Check whether the canary (the lowest address of the stack) is still
|
|
||||||
// valid. If it is not, a stack overflow has occurred.
|
|
||||||
if *currentTask.state.canaryPtr != stackCanary {
|
|
||||||
runtimePanic("goroutine stack overflow")
|
|
||||||
}
|
|
||||||
if interrupt.In() {
|
|
||||||
runtimePanic("blocked inside interrupt")
|
|
||||||
}
|
|
||||||
currentTask.state.pause()
|
|
||||||
}
|
|
||||||
|
|
||||||
//export tinygo_pause
|
|
||||||
func pause() {
|
|
||||||
Pause()
|
Pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resume the task until it pauses or completes.
|
|
||||||
// This may only be called from the scheduler.
|
|
||||||
func (t *Task) Resume() {
|
|
||||||
currentTask = t
|
|
||||||
t.gcData.swap()
|
|
||||||
t.state.resume()
|
|
||||||
t.gcData.swap()
|
|
||||||
currentTask = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
||||||
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
// Create a stack.
|
// Create a stack.
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
#ifdef _WIN32
|
||||||
|
.global _tinygo_startTask
|
||||||
|
_tinygo_startTask:
|
||||||
|
#else // Linux etc
|
||||||
.section .text.tinygo_startTask
|
.section .text.tinygo_startTask
|
||||||
.global tinygo_startTask
|
.global tinygo_startTask
|
||||||
.type tinygo_startTask, %function
|
.type tinygo_startTask, %function
|
||||||
tinygo_startTask:
|
tinygo_startTask:
|
||||||
|
#endif
|
||||||
.cfi_startproc
|
.cfi_startproc
|
||||||
// Small assembly stub for starting a goroutine. This is already run on the
|
// Small assembly stub for starting a goroutine. This is already run on the
|
||||||
// new stack, with the callee-saved registers already loaded.
|
// new stack, with the callee-saved registers already loaded.
|
||||||
@@ -24,12 +29,21 @@ tinygo_startTask:
|
|||||||
addl $4, %esp
|
addl $4, %esp
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
jmp tinygo_pause
|
#ifdef _WIN32
|
||||||
|
jmp _tinygo_task_exit
|
||||||
|
#else
|
||||||
|
jmp tinygo_task_exit
|
||||||
|
#endif
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
.global _tinygo_swapTask
|
||||||
|
_tinygo_swapTask:
|
||||||
|
#else
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
.type tinygo_swapTask, %function
|
.type tinygo_swapTask, %function
|
||||||
tinygo_swapTask:
|
tinygo_swapTask:
|
||||||
|
#endif
|
||||||
// This function gets the following parameters:
|
// This function gets the following parameters:
|
||||||
movl 4(%esp), %eax // newStack uintptr
|
movl 4(%esp), %eax // newStack uintptr
|
||||||
movl 8(%esp), %ecx // oldStack *uintptr
|
movl 8(%esp), %ecx // oldStack *uintptr
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ tinygo_startTask:
|
|||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
#ifdef __MACH__
|
#ifdef __MACH__
|
||||||
jmp _tinygo_pause
|
jmp _tinygo_task_exit
|
||||||
#else
|
#else
|
||||||
jmp tinygo_pause
|
jmp tinygo_task_exit
|
||||||
#endif
|
#endif
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ tinygo_startTask:
|
|||||||
|
|
||||||
// After return, exit this goroutine.
|
// After return, exit this goroutine.
|
||||||
// This has to be a call, not a jump, to keep the stack correctly aligned.
|
// This has to be a call, not a jump, to keep the stack correctly aligned.
|
||||||
callq tinygo_pause
|
callq tinygo_task_exit
|
||||||
|
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
.section .text.tinygo_swapTask,"ax"
|
.section .text.tinygo_swapTask,"ax"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ tinygo_startTask:
|
|||||||
blx r4
|
blx r4
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
bl tinygo_pause
|
bl tinygo_task_exit
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
.size tinygo_startTask, .-tinygo_startTask
|
.size tinygo_startTask, .-tinygo_startTask
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ tinygo_startTask:
|
|||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
#ifdef __MACH__
|
#ifdef __MACH__
|
||||||
b _tinygo_pause
|
b _tinygo_task_exit
|
||||||
#else
|
#else
|
||||||
b tinygo_pause
|
b tinygo_task_exit
|
||||||
#endif
|
#endif
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
#ifndef __MACH__
|
#ifndef __MACH__
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ tinygo_startTask:
|
|||||||
// Note that they will probably not be able to run more than the main
|
// Note that they will probably not be able to run more than the main
|
||||||
// goroutine anyway, but this file is compiled for all AVRs so it needs to
|
// goroutine anyway, but this file is compiled for all AVRs so it needs to
|
||||||
// compile at least.
|
// compile at least.
|
||||||
rcall tinygo_pause
|
rcall tinygo_task_exit
|
||||||
#else
|
#else
|
||||||
// Other devices can (and must) use the regular call instruction.
|
// Other devices can (and must) use the regular call instruction.
|
||||||
call tinygo_pause
|
call tinygo_task_exit
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ tinygo_startTask:
|
|||||||
blx r4
|
blx r4
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
bl tinygo_pause
|
bl tinygo_task_exit
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
.size tinygo_startTask, .-tinygo_startTask
|
.size tinygo_startTask, .-tinygo_startTask
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build scheduler.tasks && cortexm
|
//go:build (scheduler.tasks || scheduler.cores) && cortexm
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
uintptr_t SystemStack() {
|
uintptr_t SystemStack() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build scheduler.tasks && cortexm
|
//go:build (scheduler.tasks || scheduler.cores) && cortexm
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ tinygo_startTask:
|
|||||||
callx4 a3
|
callx4 a3
|
||||||
|
|
||||||
// After return, exit this goroutine. This call never returns.
|
// After return, exit this goroutine. This call never returns.
|
||||||
call4 tinygo_pause
|
call4 tinygo_task_exit
|
||||||
|
|
||||||
.section .text.tinygo_swapTask,"ax",@progbits
|
.section .text.tinygo_swapTask,"ax",@progbits
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ tinygo_startTask:
|
|||||||
callx0 a12
|
callx0 a12
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
call0 tinygo_pause
|
call0 tinygo_task_exit
|
||||||
.size tinygo_startTask, .-tinygo_startTask
|
.size tinygo_startTask, .-tinygo_startTask
|
||||||
|
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ tinygo_startTask:
|
|||||||
nop
|
nop
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
j tinygo_pause
|
j tinygo_task_exit
|
||||||
nop
|
nop
|
||||||
|
|
||||||
.section .text.tinygo_swapTask
|
.section .text.tinygo_swapTask
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
//go:build scheduler.cores
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "runtime/interrupt"
|
||||||
|
|
||||||
|
// Current returns the current active task.
|
||||||
|
//
|
||||||
|
//go:linkname Current runtime.currentTask
|
||||||
|
func Current() *Task
|
||||||
|
|
||||||
|
// Pause suspends the current task and returns to the scheduler.
|
||||||
|
// This function may only be called when running on a goroutine stack, not when running on the system stack or in an interrupt.
|
||||||
|
func Pause() {
|
||||||
|
lockScheduler()
|
||||||
|
PauseLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PauseLocked is the same as Pause, but must be called with the scheduler lock
|
||||||
|
// already taken.
|
||||||
|
func PauseLocked() {
|
||||||
|
// Check whether the canary (the lowest address of the stack) is still
|
||||||
|
// valid. If it is not, a stack overflow has occurred.
|
||||||
|
current := Current()
|
||||||
|
if *current.state.canaryPtr != stackCanary {
|
||||||
|
runtimePanic("goroutine stack overflow")
|
||||||
|
}
|
||||||
|
if interrupt.In() {
|
||||||
|
runtimePanic("blocked inside interrupt")
|
||||||
|
}
|
||||||
|
if current.RunState == RunStateResuming {
|
||||||
|
// Another core already marked this goroutine as ready to resume.
|
||||||
|
current.RunState = RunStateRunning
|
||||||
|
unlockScheduler()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
current.RunState = RunStatePaused
|
||||||
|
current.state.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume the task until it pauses or completes.
|
||||||
|
// This may only be called from the scheduler.
|
||||||
|
func (t *Task) Resume() {
|
||||||
|
t.gcData.swap()
|
||||||
|
t.state.resume()
|
||||||
|
t.gcData.swap()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname lockScheduler runtime.lockScheduler
|
||||||
|
func lockScheduler()
|
||||||
|
|
||||||
|
//go:linkname unlockScheduler runtime.unlockScheduler
|
||||||
|
func unlockScheduler()
|
||||||
@@ -19,7 +19,7 @@ tinygo_startTask:
|
|||||||
jalr s0
|
jalr s0
|
||||||
|
|
||||||
// After return, exit this goroutine. This is a tail call.
|
// After return, exit this goroutine. This is a tail call.
|
||||||
tail tinygo_pause
|
tail tinygo_task_exit
|
||||||
|
|
||||||
.section .text.tinygo_swapTask
|
.section .text.tinygo_swapTask
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
//go:build scheduler.tasks && tinygo.riscv
|
//go:build (scheduler.tasks || scheduler.cores) && tinygo.riscv
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var systemStack uintptr
|
// Returns a pointer where the system stack can be stored.
|
||||||
|
// This is a layering violation! We should probably refactor this so that we
|
||||||
|
// don't need such gymnastics to store the system stack pointer. (It should
|
||||||
|
// probably be moved to the runtime).
|
||||||
|
//
|
||||||
|
//go:linkname runtime_systemStackPtr runtime.systemStackPtr
|
||||||
|
func runtime_systemStackPtr() *uintptr
|
||||||
|
|
||||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||||
// switching between tasks. Also see scheduler_riscv.S that relies on the
|
// switching between tasks. Also see scheduler_riscv.S that relies on the
|
||||||
@@ -50,17 +56,18 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) resume() {
|
func (s *state) resume() {
|
||||||
swapTask(s.sp, &systemStack)
|
swapTask(s.sp, runtime_systemStackPtr())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) pause() {
|
func (s *state) pause() {
|
||||||
newStack := systemStack
|
systemStackPtr := runtime_systemStackPtr()
|
||||||
systemStack = 0
|
newStack := *systemStackPtr
|
||||||
|
*systemStackPtr = 0
|
||||||
swapTask(newStack, &s.sp)
|
swapTask(newStack, &s.sp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemStack returns the system stack pointer when called from a task stack.
|
// SystemStack returns the system stack pointer when called from a task stack.
|
||||||
// When called from the system stack, it returns 0.
|
// When called from the system stack, it returns 0.
|
||||||
func SystemStack() uintptr {
|
func SystemStack() uintptr {
|
||||||
return systemStack
|
return *runtime_systemStackPtr()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
//go:build scheduler.tasks
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "runtime/interrupt"
|
||||||
|
|
||||||
|
// currentTask is the current running task, or nil if currently in the scheduler.
|
||||||
|
var currentTask *Task
|
||||||
|
|
||||||
|
// Current returns the current active task.
|
||||||
|
func Current() *Task {
|
||||||
|
return currentTask
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause suspends the current task and returns to the scheduler.
|
||||||
|
// This function may only be called when running on a goroutine stack, not when running on the system stack or in an interrupt.
|
||||||
|
func Pause() {
|
||||||
|
// Check whether the canary (the lowest address of the stack) is still
|
||||||
|
// valid. If it is not, a stack overflow has occurred.
|
||||||
|
if *currentTask.state.canaryPtr != stackCanary {
|
||||||
|
runtimePanic("goroutine stack overflow")
|
||||||
|
}
|
||||||
|
if interrupt.In() {
|
||||||
|
runtimePanic("blocked inside interrupt")
|
||||||
|
}
|
||||||
|
currentTask.state.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume the task until it pauses or completes.
|
||||||
|
// This may only be called from the scheduler.
|
||||||
|
func (t *Task) Resume() {
|
||||||
|
currentTask = t
|
||||||
|
t.gcData.swap()
|
||||||
|
t.state.resume()
|
||||||
|
t.gcData.swap()
|
||||||
|
currentTask = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
//go:build none
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// BDWGC also uses SIGRTMIN+6 on Linux, which seems like a reasonable choice.
|
||||||
|
#ifdef __linux__
|
||||||
|
#define taskPauseSignal (SIGRTMIN + 6)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Pointer to the current task.Task structure.
|
||||||
|
// Ideally the entire task.Task structure would be a thread-local variable but
|
||||||
|
// this also works.
|
||||||
|
static __thread void *current_task;
|
||||||
|
|
||||||
|
struct state_pass {
|
||||||
|
void *(*start)(void*);
|
||||||
|
void *args;
|
||||||
|
void *task;
|
||||||
|
uintptr_t *stackTop;
|
||||||
|
sem_t startlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle the GC pause in Go.
|
||||||
|
void tinygo_task_gc_pause(int sig);
|
||||||
|
|
||||||
|
// Initialize the main thread.
|
||||||
|
void tinygo_task_init(void *mainTask, pthread_t *thread, int *numCPU, void *context) {
|
||||||
|
// Make sure the current task pointer is set correctly for the main
|
||||||
|
// goroutine as well.
|
||||||
|
current_task = mainTask;
|
||||||
|
|
||||||
|
// Store the thread ID of the main thread.
|
||||||
|
*thread = pthread_self();
|
||||||
|
|
||||||
|
// Register the "GC pause" signal for the entire process.
|
||||||
|
// Using pthread_kill, we can still send the signal to a specific thread.
|
||||||
|
struct sigaction act = { 0 };
|
||||||
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
act.sa_handler = &tinygo_task_gc_pause;
|
||||||
|
sigaction(taskPauseSignal, &act, NULL);
|
||||||
|
|
||||||
|
// Obtain the number of CPUs available on program start (for NumCPU).
|
||||||
|
int num = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
if (num <= 0) {
|
||||||
|
// Fallback in case there is an error.
|
||||||
|
num = 1;
|
||||||
|
}
|
||||||
|
*numCPU = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tinygo_task_exited(void*);
|
||||||
|
|
||||||
|
// Helper to start a goroutine while also storing the 'task' structure.
|
||||||
|
static void* start_wrapper(void *arg) {
|
||||||
|
struct state_pass *state = arg;
|
||||||
|
void *(*start)(void*) = state->start;
|
||||||
|
void *args = state->args;
|
||||||
|
current_task = state->task;
|
||||||
|
|
||||||
|
// Save the current stack pointer in the goroutine state, for the GC.
|
||||||
|
int stackAddr;
|
||||||
|
*(state->stackTop) = (uintptr_t)(&stackAddr);
|
||||||
|
|
||||||
|
// Notify the caller that the thread has successfully started and
|
||||||
|
// initialized.
|
||||||
|
sem_post(&state->startlock);
|
||||||
|
|
||||||
|
// Run the goroutine function.
|
||||||
|
start(args);
|
||||||
|
|
||||||
|
// Notify the Go side this thread will exit.
|
||||||
|
tinygo_task_exited(current_task);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start a new goroutine in an OS thread.
|
||||||
|
int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, uintptr_t *stackTop, void *context) {
|
||||||
|
// Sanity check. Should get optimized away.
|
||||||
|
if (sizeof(pthread_t) != sizeof(void*)) {
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct state_pass state = {
|
||||||
|
.start = (void*)fn,
|
||||||
|
.args = args,
|
||||||
|
.task = task,
|
||||||
|
.stackTop = stackTop,
|
||||||
|
};
|
||||||
|
sem_init(&state.startlock, 0, 0);
|
||||||
|
int result = pthread_create(thread, NULL, &start_wrapper, &state);
|
||||||
|
|
||||||
|
// Wait until the thread has been created and read all state_pass variables.
|
||||||
|
sem_wait(&state.startlock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the current task (for task.Current()).
|
||||||
|
void* tinygo_task_current(void) {
|
||||||
|
return current_task;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a signal to cause the task to pause for the GC mark phase.
|
||||||
|
void tinygo_task_send_gc_signal(pthread_t thread) {
|
||||||
|
pthread_kill(thread, taskPauseSignal);
|
||||||
|
}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
//go:build scheduler.threads
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// If true, print verbose debug logs.
|
||||||
|
const verbose = false
|
||||||
|
|
||||||
|
// Scheduler-specific state.
|
||||||
|
type state struct {
|
||||||
|
// Goroutine ID. The number here is not really significant and after a while
|
||||||
|
// it could wrap around. But it is useful for debugging.
|
||||||
|
id uintptr
|
||||||
|
|
||||||
|
// Thread ID, pthread_t or similar (typically implemented as a pointer).
|
||||||
|
thread threadID
|
||||||
|
|
||||||
|
// Highest address of the stack. It is stored when the goroutine starts, and
|
||||||
|
// is needed to be able to scan the stack.
|
||||||
|
stackTop uintptr
|
||||||
|
|
||||||
|
// Next task in the activeTasks queue.
|
||||||
|
QueueNext *Task
|
||||||
|
|
||||||
|
// Semaphore to pause/resume the thread atomically.
|
||||||
|
pauseSem Semaphore
|
||||||
|
|
||||||
|
// Semaphore used for stack scanning.
|
||||||
|
// We can't reuse pauseSem here since the thread might have been paused for
|
||||||
|
// other reasons (for example, because it was waiting on a channel).
|
||||||
|
gcSem Semaphore
|
||||||
|
}
|
||||||
|
|
||||||
|
// Goroutine counter, starting at 0 for the main goroutine.
|
||||||
|
var goroutineID uintptr
|
||||||
|
|
||||||
|
var numCPU int32
|
||||||
|
|
||||||
|
var mainTask Task
|
||||||
|
|
||||||
|
// Queue of tasks (see QueueNext) that currently exist in the program.
|
||||||
|
var activeTasks = &mainTask
|
||||||
|
var activeTaskLock PMutex
|
||||||
|
|
||||||
|
func OnSystemStack() bool {
|
||||||
|
runtimePanic("todo: task.OnSystemStack")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the main goroutine state. Must be called by the runtime on
|
||||||
|
// startup, before starting any other goroutines.
|
||||||
|
func Init(sp uintptr) {
|
||||||
|
mainTask.state.stackTop = sp
|
||||||
|
tinygo_task_init(&mainTask, &mainTask.state.thread, &numCPU)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the task struct for the current thread.
|
||||||
|
func Current() *Task {
|
||||||
|
t := (*Task)(tinygo_task_current())
|
||||||
|
if t == nil {
|
||||||
|
runtimePanic("unknown current task")
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause pauses the current task, until it is resumed by another task.
|
||||||
|
// It is possible that another task has called Resume() on the task before it
|
||||||
|
// hits Pause(), in which case the task won't be paused but continues
|
||||||
|
// immediately.
|
||||||
|
func Pause() {
|
||||||
|
// Wait until resumed
|
||||||
|
t := Current()
|
||||||
|
if verbose {
|
||||||
|
println("*** pause: ", t.state.id)
|
||||||
|
}
|
||||||
|
t.state.pauseSem.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume the given task.
|
||||||
|
// It is legal to resume a task before it gets paused, it means that the next
|
||||||
|
// call to Pause() won't pause but will continue immediately. This happens in
|
||||||
|
// practice sometimes in channel operations, where the Resume() might get called
|
||||||
|
// between the channel unlock and the call to Pause().
|
||||||
|
func (t *Task) Resume() {
|
||||||
|
if verbose {
|
||||||
|
println("*** resume: ", t.state.id)
|
||||||
|
}
|
||||||
|
// Increment the semaphore counter.
|
||||||
|
// If the task is currently paused in Wait(), it will resume.
|
||||||
|
// If the task is not yet paused, the next call to Wait() will continue
|
||||||
|
// immediately.
|
||||||
|
t.state.pauseSem.Post()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new OS thread.
|
||||||
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
|
t := &Task{}
|
||||||
|
t.state.id = atomic.AddUintptr(&goroutineID, 1)
|
||||||
|
if verbose {
|
||||||
|
println("*** start: ", t.state.id, "from", Current().state.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the new thread, and add it to the list of threads.
|
||||||
|
// Do this with a lock so that only started threads are part of the queue
|
||||||
|
// and the stop-the-world GC won't see threads that haven't started yet or
|
||||||
|
// are not fully started yet.
|
||||||
|
activeTaskLock.Lock()
|
||||||
|
errCode := tinygo_task_start(fn, args, t, &t.state.thread, &t.state.stackTop)
|
||||||
|
if errCode != 0 {
|
||||||
|
runtimePanic("could not start thread")
|
||||||
|
}
|
||||||
|
t.state.QueueNext = activeTasks
|
||||||
|
activeTasks = t
|
||||||
|
activeTaskLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
//export tinygo_task_exited
|
||||||
|
func taskExited(t *Task) {
|
||||||
|
if verbose {
|
||||||
|
println("*** exit:", t.state.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from the queue.
|
||||||
|
// TODO: this can be made more efficient by using a doubly linked list.
|
||||||
|
activeTaskLock.Lock()
|
||||||
|
found := false
|
||||||
|
for q := &activeTasks; *q != nil; q = &(*q).state.QueueNext {
|
||||||
|
if *q == t {
|
||||||
|
*q = t.state.QueueNext
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activeTaskLock.Unlock()
|
||||||
|
|
||||||
|
// Sanity check.
|
||||||
|
if !found {
|
||||||
|
runtimePanic("taskExited failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Futex to wait on until all tasks have finished scanning the stack.
|
||||||
|
// This is basically a sync.WaitGroup.
|
||||||
|
var scanDoneFutex Futex
|
||||||
|
|
||||||
|
// GC scan phase. Because we need to stop the world while scanning, this kinda
|
||||||
|
// needs to be done in the tasks package.
|
||||||
|
//
|
||||||
|
// After calling this function, GCResumeWorld needs to be called once to resume
|
||||||
|
// all threads again.
|
||||||
|
func GCStopWorldAndScan() {
|
||||||
|
current := Current()
|
||||||
|
|
||||||
|
// Don't allow new goroutines to be started while pausing/resuming threads
|
||||||
|
// in the stop-the-world phase.
|
||||||
|
activeTaskLock.Lock()
|
||||||
|
|
||||||
|
// Pause all other threads.
|
||||||
|
numOtherThreads := uint32(0)
|
||||||
|
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||||
|
if t != current {
|
||||||
|
numOtherThreads++
|
||||||
|
tinygo_task_send_gc_signal(t.state.thread)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the number of threads to wait for in the futex.
|
||||||
|
// This is the equivalent of doing an initial wg.Add(numOtherThreads).
|
||||||
|
scanDoneFutex.Store(numOtherThreads)
|
||||||
|
|
||||||
|
// Scan the current stack, and all current registers.
|
||||||
|
scanCurrentStack()
|
||||||
|
|
||||||
|
// Wake each paused thread for the first time so it will scan the stack.
|
||||||
|
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||||
|
if t != current {
|
||||||
|
t.state.gcSem.Post()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until all threads have finished scanning their stack.
|
||||||
|
// This is the equivalent of wg.Wait()
|
||||||
|
for {
|
||||||
|
val := scanDoneFutex.Load()
|
||||||
|
if val == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
scanDoneFutex.Wait(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan all globals (implemented in the runtime).
|
||||||
|
gcScanGlobals()
|
||||||
|
}
|
||||||
|
|
||||||
|
// After the GC is done scanning, resume all other threads.
|
||||||
|
//
|
||||||
|
// This must only be called after a GCStopWorldAndScan call.
|
||||||
|
func GCResumeWorld() {
|
||||||
|
current := Current()
|
||||||
|
|
||||||
|
// Wake each paused thread for the second time, so they will resume normal
|
||||||
|
// operation.
|
||||||
|
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||||
|
if t != current {
|
||||||
|
t.state.gcSem.Post()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow goroutines to start and exit again.
|
||||||
|
activeTaskLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan globals, implemented in the runtime package.
|
||||||
|
func gcScanGlobals()
|
||||||
|
|
||||||
|
var stackScanLock PMutex
|
||||||
|
|
||||||
|
//export tinygo_task_gc_pause
|
||||||
|
func tingyo_task_gc_pause() {
|
||||||
|
// Wait until we get the signal to start scanning the stack.
|
||||||
|
Current().state.gcSem.Wait()
|
||||||
|
|
||||||
|
// Scan the thread stack.
|
||||||
|
// Only scan a single thread stack at a time, because the GC marking phase
|
||||||
|
// doesn't support parallelism.
|
||||||
|
// TODO: it may be possible to call markRoots directly (without saving
|
||||||
|
// registers) since we are in a signal handler that already saved a bunch of
|
||||||
|
// registers. This is an optimization left for a future time.
|
||||||
|
stackScanLock.Lock()
|
||||||
|
scanCurrentStack()
|
||||||
|
stackScanLock.Unlock()
|
||||||
|
|
||||||
|
// Equivalent of wg.Done(): subtract one from the futex and if the result is
|
||||||
|
// 0 (meaning we were the last in the waitgroup), wake the waiting thread.
|
||||||
|
n := uint32(1)
|
||||||
|
if scanDoneFutex.Add(-n) == 0 {
|
||||||
|
scanDoneFutex.Wake()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until we get the signal we can resume normally (after the mark phase
|
||||||
|
// has finished).
|
||||||
|
Current().state.gcSem.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export tinygo_scanCurrentStack
|
||||||
|
func scanCurrentStack()
|
||||||
|
|
||||||
|
// Return the highest address of the current stack.
|
||||||
|
func StackTop() uintptr {
|
||||||
|
return Current().state.stackTop
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname runtimePanic runtime.runtimePanic
|
||||||
|
func runtimePanic(msg string)
|
||||||
|
|
||||||
|
// Using //go:linkname instead of //export so that we don't tell the compiler
|
||||||
|
// that the 't' parameter won't escape (because it will).
|
||||||
|
//
|
||||||
|
//go:linkname tinygo_task_init tinygo_task_init
|
||||||
|
func tinygo_task_init(t *Task, thread *threadID, numCPU *int32)
|
||||||
|
|
||||||
|
// Here same as for tinygo_task_init.
|
||||||
|
//
|
||||||
|
//go:linkname tinygo_task_start tinygo_task_start
|
||||||
|
func tinygo_task_start(fn uintptr, args unsafe.Pointer, t *Task, thread *threadID, stackTop *uintptr) int32
|
||||||
|
|
||||||
|
// Pause the thread by sending it a signal.
|
||||||
|
//
|
||||||
|
//export tinygo_task_send_gc_signal
|
||||||
|
func tinygo_task_send_gc_signal(threadID)
|
||||||
|
|
||||||
|
//export tinygo_task_current
|
||||||
|
func tinygo_task_current() unsafe.Pointer
|
||||||
|
|
||||||
|
func NumCPU() int {
|
||||||
|
return int(numCPU)
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
//go:build btt_skr_pico
|
||||||
|
|
||||||
|
// This contains the pin mappings for the BigTreeTech SKR Pico.
|
||||||
|
//
|
||||||
|
// Purchase link: https://biqu.equipment/products/btt-skr-pico-v1-0
|
||||||
|
// Board schematic: https://github.com/bigtreetech/SKR-Pico/blob/master/Hardware/BTT%20SKR%20Pico%20V1.0-SCH.pdf
|
||||||
|
// Pin diagram: https://github.com/bigtreetech/SKR-Pico/blob/master/Hardware/BTT%20SKR%20Pico%20V1.0-PIN.pdf
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// TMC stepper driver motor direction.
|
||||||
|
// X/Y/Z/E refers to motors for X/Y/Z and the extruder.
|
||||||
|
const (
|
||||||
|
X_DIR = GPIO10
|
||||||
|
Y_DIR = GPIO5
|
||||||
|
Z_DIR = GPIO28
|
||||||
|
E_DIR = GPIO13
|
||||||
|
)
|
||||||
|
|
||||||
|
// TMC stepper driver motor step
|
||||||
|
const (
|
||||||
|
X_STEP = GPIO11
|
||||||
|
Y_STEP = GPIO6
|
||||||
|
Z_STEP = GPIO19
|
||||||
|
E_STEP = GPIO14
|
||||||
|
)
|
||||||
|
|
||||||
|
// TMC stepper driver enable
|
||||||
|
const (
|
||||||
|
X_ENABLE = GPIO12
|
||||||
|
Y_ENABLE = GPIO7
|
||||||
|
Z_ENABLE = GPIO2
|
||||||
|
E_ENABLE = GPIO15
|
||||||
|
)
|
||||||
|
|
||||||
|
// TMC stepper driver UART
|
||||||
|
const (
|
||||||
|
TMC_UART_TX = UART1_TX_PIN
|
||||||
|
TMC_UART_RX = UART1_RX_PIN
|
||||||
|
)
|
||||||
|
|
||||||
|
// Endstops
|
||||||
|
const (
|
||||||
|
X_ENDSTOP = GPIO4
|
||||||
|
Y_ENDSTOP = GPIO3
|
||||||
|
Z_ENDSTOP = GPIO25
|
||||||
|
E_ENDSTOP = GPIO16
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fan PWM
|
||||||
|
const (
|
||||||
|
FAN1_PWM = GPIO17
|
||||||
|
FAN2_PWM = GPIO18
|
||||||
|
FAN3_PWM = GPIO20
|
||||||
|
)
|
||||||
|
|
||||||
|
// Heater PWM
|
||||||
|
const (
|
||||||
|
HEATER_BED_PWM = GPIO21
|
||||||
|
HEATER_EXTRUDER_PWM = GPIO23
|
||||||
|
)
|
||||||
|
|
||||||
|
// Thermistors
|
||||||
|
const (
|
||||||
|
THERM_BED = GPIO26 // Bed heater
|
||||||
|
THERM_EXTRUDER = GPIO27 // Toolhead heater
|
||||||
|
)
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
const (
|
||||||
|
RGB = GPIO24 // Neopixel
|
||||||
|
SERVO = GPIO29 // Servo
|
||||||
|
PROBE = GPIO22 // Probe
|
||||||
|
)
|
||||||
|
|
||||||
|
// Onboard crystal oscillator frequency, in MHz.
|
||||||
|
const (
|
||||||
|
xoscFreq = 12 // MHz
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C. We don't have this available
|
||||||
|
const (
|
||||||
|
I2C0_SDA_PIN = NoPin
|
||||||
|
I2C0_SCL_PIN = NoPin
|
||||||
|
|
||||||
|
I2C1_SDA_PIN = NoPin
|
||||||
|
I2C1_SCL_PIN = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI. We don't have this available
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = NoPin
|
||||||
|
SPI0_SDO_PIN = NoPin
|
||||||
|
SPI0_SDI_PIN = NoPin
|
||||||
|
SPI1_SCK_PIN = NoPin
|
||||||
|
SPI1_SDO_PIN = NoPin
|
||||||
|
SPI1_SDI_PIN = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "SKR Pico"
|
||||||
|
usb_STRING_MANUFACTURER = "BigTreeTech"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x2e8a
|
||||||
|
usb_PID uint16 = 0x0003
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART pins
|
||||||
|
const (
|
||||||
|
UART0_TX_PIN = GPIO0
|
||||||
|
UART0_RX_PIN = GPIO1
|
||||||
|
UART1_TX_PIN = GPIO8
|
||||||
|
UART1_RX_PIN = GPIO9
|
||||||
|
UART_TX_PIN = UART0_TX_PIN
|
||||||
|
UART_RX_PIN = UART0_RX_PIN
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultUART = UART0
|
||||||
@@ -1904,7 +1904,7 @@ func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteAt writes the given number of bytes to the block device.
|
// WriteAt writes the given number of bytes to the block device.
|
||||||
// Only word (32 bits) length data can be programmed.
|
// Data is written to the page buffer in 4-byte chunks, then saved to flash memory.
|
||||||
// See Atmel-42181G–SAM-D21_Datasheet–09/2015 page 359.
|
// See Atmel-42181G–SAM-D21_Datasheet–09/2015 page 359.
|
||||||
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
||||||
// This method assumes that the destination is already erased.
|
// This method assumes that the destination is already erased.
|
||||||
@@ -1921,8 +1921,10 @@ func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
|||||||
waitWhileFlashBusy()
|
waitWhileFlashBusy()
|
||||||
|
|
||||||
for j := 0; j < len(padded); j += int(f.WriteBlockSize()) {
|
for j := 0; j < len(padded); j += int(f.WriteBlockSize()) {
|
||||||
// write word
|
// page buffer is 64 bytes long, but only 4 bytes can be written at once
|
||||||
*(*uint32)(unsafe.Pointer(address)) = binary.LittleEndian.Uint32(padded[j : j+int(f.WriteBlockSize())])
|
for k := 0; k < int(f.WriteBlockSize()); k += 4 {
|
||||||
|
*(*uint32)(unsafe.Pointer(address + uintptr(k))) = binary.LittleEndian.Uint32(padded[j+k : j+k+4])
|
||||||
|
}
|
||||||
|
|
||||||
sam.NVMCTRL.SetADDR(uint32(address >> 1))
|
sam.NVMCTRL.SetADDR(uint32(address >> 1))
|
||||||
sam.NVMCTRL.CTRLA.Set(sam.NVMCTRL_CTRLA_CMD_WP | (sam.NVMCTRL_CTRLA_CMDEX_KEY << sam.NVMCTRL_CTRLA_CMDEX_Pos))
|
sam.NVMCTRL.CTRLA.Set(sam.NVMCTRL_CTRLA_CMD_WP | (sam.NVMCTRL_CTRLA_CMDEX_KEY << sam.NVMCTRL_CTRLA_CMDEX_Pos))
|
||||||
@@ -1944,7 +1946,7 @@ func (f flashBlockDevice) Size() int64 {
|
|||||||
return int64(FlashDataEnd() - FlashDataStart())
|
return int64(FlashDataEnd() - FlashDataStart())
|
||||||
}
|
}
|
||||||
|
|
||||||
const writeBlockSize = 4
|
const writeBlockSize = 64
|
||||||
|
|
||||||
// WriteBlockSize returns the block size in which data can be written to
|
// WriteBlockSize returns the block size in which data can be written to
|
||||||
// memory. It can be used by a client to optimize writes, non-aligned writes
|
// memory. It can be used by a client to optimize writes, non-aligned writes
|
||||||
@@ -2027,3 +2029,62 @@ func checkFlashError() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Watchdog provides access to the hardware watchdog available
|
||||||
|
// in the SAMD21.
|
||||||
|
var Watchdog = &watchdogImpl{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// WatchdogMaxTimeout in milliseconds (16s)
|
||||||
|
WatchdogMaxTimeout = (16384 * 1000) / 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
type watchdogImpl struct{}
|
||||||
|
|
||||||
|
// Configure the watchdog.
|
||||||
|
//
|
||||||
|
// This method should not be called after the watchdog is started and on
|
||||||
|
// some platforms attempting to reconfigure after starting the watchdog
|
||||||
|
// is explicitly forbidden / will not work.
|
||||||
|
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
|
||||||
|
// Use OSCULP32K as source for Generic Clock Generator 8, divided by 32 to get 1.024kHz
|
||||||
|
sam.GCLK.GENDIV.Set(sam.GCLK_CLKCTRL_GEN_GCLK8 | (32 << sam.GCLK_GENDIV_DIV_Pos))
|
||||||
|
sam.GCLK.GENCTRL.Set(sam.GCLK_CLKCTRL_GEN_GCLK8 | (sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) | sam.GCLK_GENCTRL_GENEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Use GCLK8 for watchdog
|
||||||
|
sam.GCLK.CLKCTRL.Set(sam.GCLK_CLKCTRL_ID_WDT | (sam.GCLK_CLKCTRL_GEN_GCLK8 << sam.GCLK_CLKCTRL_GEN_Pos) | sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
|
||||||
|
// Power on the watchdog peripheral
|
||||||
|
sam.PM.APBAMASK.SetBits(sam.PM_APBAMASK_WDT_)
|
||||||
|
|
||||||
|
// 1.024kHz clock
|
||||||
|
cycles := int((int64(config.TimeoutMillis) * 1024) / 1000)
|
||||||
|
|
||||||
|
// period is expressed as a power-of-two, starting at 8 / 1024ths of a second
|
||||||
|
period := uint8(0)
|
||||||
|
cfgCycles := 8
|
||||||
|
for cfgCycles < cycles {
|
||||||
|
period++
|
||||||
|
cfgCycles <<= 1
|
||||||
|
|
||||||
|
if period >= 0xB {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sam.WDT.CONFIG.Set(period << sam.WDT_CONFIG_PER_Pos)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starts the watchdog.
|
||||||
|
func (wd *watchdogImpl) Start() error {
|
||||||
|
sam.WDT.CTRL.SetBits(sam.WDT_CTRL_ENABLE)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the watchdog, indicating that `source` is healthy.
|
||||||
|
func (wd *watchdogImpl) Update() {
|
||||||
|
sam.WDT.CLEAR.Set(sam.WDT_CLEAR_CLEAR_KEY)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//go:build !baremetal && (gemma_m0 || qtpy || trinket_m0 || arduino_mkr1000 || arduino_mkrwifi1010 || arduino_nano33 || arduino_zero || circuitplay_express || feather_m0_express || feather_m0 || itsybitsy_m0 || p1am_100 || xiao)
|
||||||
|
|
||||||
|
// Simulated atsamd21 chips.
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// The timer channels/pins match the hardware, and encode the same information
|
||||||
|
// as pinTimerMapping but in a more generic (less efficient) way.
|
||||||
|
|
||||||
|
var TCC0 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 48e6,
|
||||||
|
bits: 24,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{PA04, PA08, PB10, PA14, PB16, PA22, PB30}, // channel 0
|
||||||
|
{PA05, PA09, PB11, PA15, PB17, PA23, PB31}, // channel 1
|
||||||
|
{PA10, PB12, PA12, PA16, PA18, PA20}, // channel 2
|
||||||
|
{PA11, PB13, PA13, PA17, PA19, PA21}, // channel 3
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var TCC1 = &timerType{
|
||||||
|
instance: 1,
|
||||||
|
frequency: 48e6,
|
||||||
|
bits: 24,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{PA06, PA10, PA30}, // channel 0
|
||||||
|
{PA07, PA11, PA31}, // channel 1
|
||||||
|
{PA08, PA24, PB30}, // channel 2
|
||||||
|
{PA09, PA25, PB31}, // channel 3
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var TCC2 = &timerType{
|
||||||
|
instance: 2,
|
||||||
|
frequency: 48e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{PA00, PA12, PA16}, // channel 0
|
||||||
|
{PA01, PA13, PA17}, // channel 1
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -19,6 +19,21 @@ const (
|
|||||||
|
|
||||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||||
|
|
||||||
|
NumberOfUSBEndpoints = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
endPoints = []uint32{
|
||||||
|
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
|
||||||
|
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
|
||||||
|
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
|
||||||
|
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
|
||||||
|
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||||
|
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
|
||||||
|
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
|
||||||
|
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||||
@@ -179,10 +194,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
|||||||
setEPINTFLAG(i, epFlags)
|
setEPINTFLAG(i, epFlags)
|
||||||
if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 {
|
if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 {
|
||||||
buf := handleEndpointRx(i)
|
buf := handleEndpointRx(i)
|
||||||
if usbRxHandler[i] != nil {
|
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
|
||||||
usbRxHandler[i](buf)
|
AckUsbOutTransfer(i)
|
||||||
}
|
}
|
||||||
handleEndpointRxComplete(i)
|
|
||||||
} else if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
|
} else if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
|
||||||
if usbTxHandler[i] != nil {
|
if usbTxHandler[i] != nil {
|
||||||
usbTxHandler[i]()
|
usbTxHandler[i]()
|
||||||
@@ -334,6 +348,8 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
||||||
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||||
l := uint16(len(data))
|
l := uint16(len(data))
|
||||||
@@ -400,7 +416,8 @@ func handleEndpointRx(ep uint32) []byte {
|
|||||||
return udd_ep_out_cache_buffer[ep][:count]
|
return udd_ep_out_cache_buffer[ep][:count]
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleEndpointRxComplete(ep uint32) {
|
// AckUsbOutTransfer is called to acknowledge the completion of a USB OUT transfer.
|
||||||
|
func AckUsbOutTransfer(ep uint32) {
|
||||||
// set byte count to zero
|
// set byte count to zero
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
|
|
||||||
|
|||||||
@@ -2163,7 +2163,7 @@ func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteAt writes the given number of bytes to the block device.
|
// WriteAt writes the given number of bytes to the block device.
|
||||||
// Only word (32 bits) length data can be programmed.
|
// Data is written to the page buffer in 4-byte chunks, then saved to flash memory.
|
||||||
// See SAM-D5x-E5x-Family-Data-Sheet-DS60001507.pdf page 591-592.
|
// See SAM-D5x-E5x-Family-Data-Sheet-DS60001507.pdf page 591-592.
|
||||||
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
// If the length of p is not long enough it will be padded with 0xFF bytes.
|
||||||
// This method assumes that the destination is already erased.
|
// This method assumes that the destination is already erased.
|
||||||
@@ -2185,16 +2185,13 @@ func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
|||||||
waitWhileFlashBusy()
|
waitWhileFlashBusy()
|
||||||
|
|
||||||
for j := 0; j < len(padded); j += int(f.WriteBlockSize()) {
|
for j := 0; j < len(padded); j += int(f.WriteBlockSize()) {
|
||||||
// write first word using double-word low order word
|
// page buffer is 512 bytes long, but only 4 bytes can be written at once
|
||||||
*(*uint32)(unsafe.Pointer(address)) = binary.LittleEndian.Uint32(padded[j : j+int(f.WriteBlockSize()/2)])
|
for k := 0; k < int(f.WriteBlockSize()); k += 4 {
|
||||||
|
*(*uint32)(unsafe.Pointer(address + uintptr(k))) = binary.LittleEndian.Uint32(padded[j+k : j+k+4])
|
||||||
// write second word using double-word high order word
|
}
|
||||||
*(*uint32)(unsafe.Add(unsafe.Pointer(address), uintptr(f.WriteBlockSize())/2)) = binary.LittleEndian.Uint32(padded[j+int(f.WriteBlockSize()/2) : j+int(f.WriteBlockSize())])
|
|
||||||
|
|
||||||
waitWhileFlashBusy()
|
|
||||||
|
|
||||||
sam.NVMCTRL.SetADDR(uint32(address))
|
sam.NVMCTRL.SetADDR(uint32(address))
|
||||||
sam.NVMCTRL.CTRLB.Set(sam.NVMCTRL_CTRLB_CMD_WQW | (sam.NVMCTRL_CTRLB_CMDEX_KEY << sam.NVMCTRL_CTRLB_CMDEX_Pos))
|
sam.NVMCTRL.CTRLB.Set(sam.NVMCTRL_CTRLB_CMD_WP | (sam.NVMCTRL_CTRLB_CMDEX_KEY << sam.NVMCTRL_CTRLB_CMDEX_Pos))
|
||||||
|
|
||||||
waitWhileFlashBusy()
|
waitWhileFlashBusy()
|
||||||
|
|
||||||
@@ -2213,7 +2210,7 @@ func (f flashBlockDevice) Size() int64 {
|
|||||||
return int64(FlashDataEnd() - FlashDataStart())
|
return int64(FlashDataEnd() - FlashDataStart())
|
||||||
}
|
}
|
||||||
|
|
||||||
const writeBlockSize = 8
|
const writeBlockSize = 512
|
||||||
|
|
||||||
// WriteBlockSize returns the block size in which data can be written to
|
// WriteBlockSize returns the block size in which data can be written to
|
||||||
// memory. It can be used by a client to optimize writes, non-aligned writes
|
// memory. It can be used by a client to optimize writes, non-aligned writes
|
||||||
@@ -2357,6 +2354,5 @@ func (wd *watchdogImpl) Start() error {
|
|||||||
|
|
||||||
// Update the watchdog, indicating that `source` is healthy.
|
// Update the watchdog, indicating that `source` is healthy.
|
||||||
func (wd *watchdogImpl) Update() {
|
func (wd *watchdogImpl) Update() {
|
||||||
// 0xA5 = magic value (see datasheet)
|
sam.WDT.CLEAR.Set(sam.WDT_CLEAR_CLEAR_KEY)
|
||||||
sam.WDT.CLEAR.Set(0xA5)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,21 @@ const (
|
|||||||
|
|
||||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||||
|
|
||||||
|
NumberOfUSBEndpoints = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
endPoints = []uint32{
|
||||||
|
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
|
||||||
|
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
|
||||||
|
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
|
||||||
|
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
|
||||||
|
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||||
|
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
|
||||||
|
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
|
||||||
|
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||||
@@ -182,10 +197,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
|||||||
setEPINTFLAG(i, epFlags)
|
setEPINTFLAG(i, epFlags)
|
||||||
if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 {
|
if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 {
|
||||||
buf := handleEndpointRx(i)
|
buf := handleEndpointRx(i)
|
||||||
if usbRxHandler[i] != nil {
|
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
|
||||||
usbRxHandler[i](buf)
|
AckUsbOutTransfer(i)
|
||||||
}
|
}
|
||||||
handleEndpointRxComplete(i)
|
|
||||||
} else if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
} else if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
||||||
if usbTxHandler[i] != nil {
|
if usbTxHandler[i] != nil {
|
||||||
usbTxHandler[i]()
|
usbTxHandler[i]()
|
||||||
@@ -337,6 +351,8 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
||||||
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||||
l := uint16(len(data))
|
l := uint16(len(data))
|
||||||
@@ -403,7 +419,8 @@ func handleEndpointRx(ep uint32) []byte {
|
|||||||
return udd_ep_out_cache_buffer[ep][:count]
|
return udd_ep_out_cache_buffer[ep][:count]
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleEndpointRxComplete(ep uint32) {
|
// AckUsbOutTransfer is called to acknowledge the completion of a USB OUT transfer.
|
||||||
|
func AckUsbOutTransfer(ep uint32) {
|
||||||
// set byte count to zero
|
// set byte count to zero
|
||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,103 @@ func gpioSet(pin Pin, value bool)
|
|||||||
//export __tinygo_gpio_get
|
//export __tinygo_gpio_get
|
||||||
func gpioGet(pin Pin) bool
|
func gpioGet(pin Pin) bool
|
||||||
|
|
||||||
|
// Generic PWM/timer peripheral. Properties can be configured depending on the
|
||||||
|
// hardware.
|
||||||
|
type timerType struct {
|
||||||
|
// Static properties.
|
||||||
|
instance int32
|
||||||
|
frequency uint64
|
||||||
|
bits int
|
||||||
|
prescalers []int
|
||||||
|
channelPins [][]Pin
|
||||||
|
|
||||||
|
// Configured 'top' value.
|
||||||
|
top uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the PWM/timer peripheral.
|
||||||
|
func (t *timerType) Configure(config PWMConfig) error {
|
||||||
|
// Note: for very large period values, this multiplication will overflow.
|
||||||
|
top := config.Period * t.frequency / 1e9
|
||||||
|
if config.Period == 0 {
|
||||||
|
top = 0xffff // default for LEDs
|
||||||
|
}
|
||||||
|
|
||||||
|
// The maximum value that can be stored with the given number of bits in
|
||||||
|
// this timer.
|
||||||
|
maxTop := uint64(1)<<uint64(t.bits) - 1
|
||||||
|
|
||||||
|
// Look for an appropriate prescaler value.
|
||||||
|
var prescaler int
|
||||||
|
for _, div := range t.prescalers {
|
||||||
|
if top/uint64(div) <= maxTop {
|
||||||
|
prescaler = div
|
||||||
|
top = top / uint64(div)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if prescaler == 0 {
|
||||||
|
return ErrPWMPeriodTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set these values as the configuration.
|
||||||
|
t.top = uint32(top)
|
||||||
|
pwmConfigure(t.instance, float64(t.frequency)/float64(prescaler), uint32(top))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel returns a PWM channel for the given pin. Note that one channel may be
|
||||||
|
// shared between multiple pins, and so will have the same duty cycle. If this
|
||||||
|
// is not desirable, look for a different PWM/timer peripheral or consider using
|
||||||
|
// a different pin.
|
||||||
|
func (t *timerType) Channel(pin Pin) (uint8, error) {
|
||||||
|
for ch, pins := range t.channelPins {
|
||||||
|
// For nrf52xxx chips specifically we can assign any channel to any pin.
|
||||||
|
// We use a similar (identical?) logic to the hardware implementation,
|
||||||
|
// and pick the first empty channel.
|
||||||
|
if pins == nil {
|
||||||
|
t.channelPins[ch] = []Pin{pin}
|
||||||
|
pwmChannelConfigure(t.instance, int32(ch), pin)
|
||||||
|
return uint8(ch), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the pin can be used on this channel.
|
||||||
|
for _, p := range pins {
|
||||||
|
if p == pin {
|
||||||
|
pwmChannelConfigure(t.instance, int32(ch), pin)
|
||||||
|
return uint8(ch), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, ErrInvalidOutputPin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timerType) Set(channel uint8, value uint32) {
|
||||||
|
pwmChannelSet(t.instance, channel, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||||
|
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||||
|
// constant.
|
||||||
|
//
|
||||||
|
// The value returned here is hardware dependent. In general, it's best to treat
|
||||||
|
// it as an opaque value that can be divided by some number and passed to Set
|
||||||
|
// (see Set documentation for more information).
|
||||||
|
func (t *timerType) Top() uint32 {
|
||||||
|
return t.top
|
||||||
|
}
|
||||||
|
|
||||||
|
//export __tinygo_pwm_configure
|
||||||
|
func pwmConfigure(instance int32, frequency float64, top uint32)
|
||||||
|
|
||||||
|
//export __tinygo_pwm_channel_configure
|
||||||
|
func pwmChannelConfigure(instance, channel int32, pin Pin)
|
||||||
|
|
||||||
|
//export __tinygo_pwm_channel_set
|
||||||
|
func pwmChannelSet(instance int32, channel uint8, value uint32)
|
||||||
|
|
||||||
type SPI struct {
|
type SPI struct {
|
||||||
Bus uint8
|
Bus uint8
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
//go:build !baremetal && (bluemicro840 || circuitplay_bluefruit || clue_alpha || feather_nrf52840_sense || feather_nrf52840 || itsybitsy_nrf52840 || mdbt50qrx || nano_33_ble || nicenano || nrf52840_mdk || particle_3rd_gen || pca10056 || pca10059 || rak4631 || reelboard || xiao_ble)
|
||||||
|
|
||||||
|
// Simulator support for nrf52840 based boards.
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// Channel values below are nil, so that they get filled in on the first use.
|
||||||
|
// This is the same as what happens on baremetal.
|
||||||
|
|
||||||
|
var PWM0 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 16e6,
|
||||||
|
bits: 15,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
nil, // channel 0
|
||||||
|
nil, // channel 1
|
||||||
|
nil, // channel 2
|
||||||
|
nil, // channel 3
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM1 = &timerType{
|
||||||
|
instance: 1,
|
||||||
|
frequency: 16e6,
|
||||||
|
bits: 15,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
nil, // channel 0
|
||||||
|
nil, // channel 1
|
||||||
|
nil, // channel 2
|
||||||
|
nil, // channel 3
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM2 = &timerType{
|
||||||
|
instance: 2,
|
||||||
|
frequency: 16e6,
|
||||||
|
bits: 15,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
nil, // channel 0
|
||||||
|
nil, // channel 1
|
||||||
|
nil, // channel 2
|
||||||
|
nil, // channel 3
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM3 = &timerType{
|
||||||
|
instance: 3,
|
||||||
|
frequency: 16e6,
|
||||||
|
bits: 15,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
nil, // channel 0
|
||||||
|
nil, // channel 1
|
||||||
|
nil, // channel 2
|
||||||
|
nil, // channel 3
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const NumberOfUSBEndpoints = 8
|
||||||
|
|
||||||
var (
|
var (
|
||||||
sendOnEP0DATADONE struct {
|
sendOnEP0DATADONE struct {
|
||||||
ptr *byte
|
ptr *byte
|
||||||
@@ -20,6 +22,17 @@ var (
|
|||||||
epinen uint32
|
epinen uint32
|
||||||
epouten uint32
|
epouten uint32
|
||||||
easyDMABusy volatile.Register8
|
easyDMABusy volatile.Register8
|
||||||
|
|
||||||
|
endPoints = []uint32{
|
||||||
|
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
|
||||||
|
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
|
||||||
|
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
|
||||||
|
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
|
||||||
|
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||||
|
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
|
||||||
|
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
|
||||||
|
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// enterCriticalSection is used to protect access to easyDMA - only one thing
|
// enterCriticalSection is used to protect access to easyDMA - only one thing
|
||||||
@@ -193,10 +206,9 @@ func handleUSBIRQ(interrupt.Interrupt) {
|
|||||||
if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 {
|
if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 {
|
||||||
nrf.USBD.EVENTS_ENDEPOUT[i].Set(0)
|
nrf.USBD.EVENTS_ENDEPOUT[i].Set(0)
|
||||||
buf := handleEndpointRx(uint32(i))
|
buf := handleEndpointRx(uint32(i))
|
||||||
if usbRxHandler[i] != nil {
|
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
|
||||||
usbRxHandler[i](buf)
|
AckUsbOutTransfer(uint32(i))
|
||||||
}
|
}
|
||||||
handleEndpointRxComplete(uint32(i))
|
|
||||||
exitCriticalSection()
|
exitCriticalSection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,6 +264,8 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
||||||
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||||
count := len(data)
|
count := len(data)
|
||||||
@@ -289,7 +303,8 @@ func handleEndpointRx(ep uint32) []byte {
|
|||||||
return udd_ep_out_cache_buffer[ep][:count]
|
return udd_ep_out_cache_buffer[ep][:count]
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleEndpointRxComplete(ep uint32) {
|
// AckUsbOutTransfer is called to acknowledge the completion of a USB OUT transfer.
|
||||||
|
func AckUsbOutTransfer(ep uint32) {
|
||||||
// set ready for next data
|
// set ready for next data
|
||||||
nrf.USBD.SIZE.EPOUT[ep].Set(0)
|
nrf.USBD.SIZE.EPOUT[ep].Set(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
//go:build !baremetal && (ae_rp2040 || badger2040_w || badger2040 || challenger_rp2040 || elecrow_rp2040 || feather_rp2040 || gopher_badge || kb2040 || macropad_rp2040 || nano_rp2040 || pico || qtpy_rp2040 || thingplus_rp2040 || thumby || trinkey_qt2040 || tufty2040 || waveshare_rp2040_tiny || waveshare_rp2040_zero || xiao_rp2040)
|
||||||
|
|
||||||
|
// Simulator support for the RP2040.
|
||||||
|
//
|
||||||
|
// This is *only* for the RP2040. RP2350 is a different chip with slightly
|
||||||
|
// different characteristics.
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
var PWM0 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 200e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256}, // actually a continuing range, TODO
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{GPIO0, GPIO16}, // channel A (0)
|
||||||
|
{GPIO1, GPIO17}, // channel B (1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM1 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 200e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{GPIO2, GPIO18}, // channel A (0)
|
||||||
|
{GPIO3, GPIO19}, // channel B (1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM2 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 200e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{GPIO4, GPIO20}, // channel A (0)
|
||||||
|
{GPIO5, GPIO21}, // channel B (1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM3 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 200e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{GPIO6, GPIO22}, // channel A (0)
|
||||||
|
{GPIO7, GPIO23}, // channel B (1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM4 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 200e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{GPIO8, GPIO24}, // channel A (0)
|
||||||
|
{GPIO9, GPIO25}, // channel B (1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM5 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 200e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{GPIO10, GPIO26}, // channel A (0)
|
||||||
|
{GPIO11, GPIO27}, // channel B (1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM6 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 200e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{GPIO12, GPIO28}, // channel A (0)
|
||||||
|
{GPIO13, GPIO29}, // channel B (1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var PWM7 = &timerType{
|
||||||
|
instance: 0,
|
||||||
|
frequency: 200e6,
|
||||||
|
bits: 16,
|
||||||
|
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
|
||||||
|
channelPins: [][]Pin{
|
||||||
|
{GPIO14}, // channel A (0)
|
||||||
|
{GPIO15}, // channel B (1)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -6,16 +6,6 @@ import (
|
|||||||
"device/rp"
|
"device/rp"
|
||||||
"machine/usb"
|
"machine/usb"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sendOnEP0DATADONE struct {
|
|
||||||
offset int
|
|
||||||
data []byte
|
|
||||||
pid uint32
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||||
@@ -77,7 +67,7 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
|||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
// Stall endpoint?
|
// Stall endpoint?
|
||||||
sendStallViaEPIn(0)
|
USBDev.SetStallEPIn(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -105,10 +95,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
|||||||
for i := 0; i < 16; i++ {
|
for i := 0; i < 16; i++ {
|
||||||
if s2&(1<<(i*2+1)) > 0 {
|
if s2&(1<<(i*2+1)) > 0 {
|
||||||
buf := handleEndpointRx(uint32(i))
|
buf := handleEndpointRx(uint32(i))
|
||||||
if usbRxHandler[i] != nil {
|
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
|
||||||
usbRxHandler[i](buf)
|
AckUsbOutTransfer(uint32(i))
|
||||||
}
|
}
|
||||||
handleEndpointRxComplete(uint32(i))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,40 +123,6 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initEndpoint(ep, config uint32) {
|
|
||||||
val := uint32(usbEpControlEnable) | uint32(usbEpControlInterruptPerBuff)
|
|
||||||
offset := ep*2*usbBufferLen + 0x100
|
|
||||||
val |= offset
|
|
||||||
|
|
||||||
switch config {
|
|
||||||
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn:
|
|
||||||
val |= usbEpControlEndpointTypeInterrupt
|
|
||||||
_usbDPSRAM.EPxControl[ep].In.Set(val)
|
|
||||||
|
|
||||||
case usb.ENDPOINT_TYPE_BULK | usb.EndpointOut:
|
|
||||||
val |= usbEpControlEndpointTypeBulk
|
|
||||||
_usbDPSRAM.EPxControl[ep].Out.Set(val)
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.Set(usbBufferLen & usbBuf0CtrlLenMask)
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
|
|
||||||
|
|
||||||
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
|
|
||||||
val |= usbEpControlEndpointTypeInterrupt
|
|
||||||
_usbDPSRAM.EPxControl[ep].Out.Set(val)
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.Set(usbBufferLen & usbBuf0CtrlLenMask)
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
|
|
||||||
|
|
||||||
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
|
|
||||||
val |= usbEpControlEndpointTypeBulk
|
|
||||||
_usbDPSRAM.EPxControl[ep].In.Set(val)
|
|
||||||
|
|
||||||
case usb.ENDPOINT_TYPE_CONTROL:
|
|
||||||
val |= usbEpControlEndpointTypeControl
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.Set(usbBuf0CtrlData1Pid)
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleUSBSetAddress(setup usb.Setup) bool {
|
func handleUSBSetAddress(setup usb.Setup) bool {
|
||||||
// Using 570μs timeout which is exactly the same as SAMD21.
|
// Using 570μs timeout which is exactly the same as SAMD21.
|
||||||
const ackTimeout = 570
|
const ackTimeout = 570
|
||||||
@@ -188,191 +143,6 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
func armEPZeroStall() {
|
||||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
rp.USBCTRL_REGS.EP_STALL_ARM.Set(rp.USBCTRL_REGS_EP_STALL_ARM_EP0_IN)
|
||||||
sendUSBPacket(ep, data, 0)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:noinline
|
|
||||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
|
||||||
count := len(data)
|
|
||||||
if 0 < int(maxsize) && int(maxsize) < count {
|
|
||||||
count = int(maxsize)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ep == 0 {
|
|
||||||
if count > usb.EndpointPacketSize {
|
|
||||||
count = usb.EndpointPacketSize
|
|
||||||
|
|
||||||
sendOnEP0DATADONE.offset = count
|
|
||||||
sendOnEP0DATADONE.data = data
|
|
||||||
} else {
|
|
||||||
sendOnEP0DATADONE.offset = 0
|
|
||||||
}
|
|
||||||
epXdata0[ep] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
sendViaEPIn(ep, data, count)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
|
||||||
var b [cdcLineInfoSize]byte
|
|
||||||
ep := 0
|
|
||||||
|
|
||||||
for !_usbDPSRAM.EPxBufferControl[ep].Out.HasBits(usbBuf0CtrlFull) {
|
|
||||||
// TODO: timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrl := _usbDPSRAM.EPxBufferControl[ep].Out.Get()
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.Set(usbBufferLen & usbBuf0CtrlLenMask)
|
|
||||||
sz := ctrl & usbBuf0CtrlLenMask
|
|
||||||
|
|
||||||
copy(b[:], _usbDPSRAM.EPxBuffer[ep].Buffer0[:sz])
|
|
||||||
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlData1Pid)
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
|
|
||||||
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleEndpointRx(ep uint32) []byte {
|
|
||||||
ctrl := _usbDPSRAM.EPxBufferControl[ep].Out.Get()
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.Set(usbBufferLen & usbBuf0CtrlLenMask)
|
|
||||||
sz := ctrl & usbBuf0CtrlLenMask
|
|
||||||
|
|
||||||
return _usbDPSRAM.EPxBuffer[ep].Buffer0[:sz]
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleEndpointRxComplete(ep uint32) {
|
|
||||||
epXdata0[ep] = !epXdata0[ep]
|
|
||||||
if epXdata0[ep] || ep == 0 {
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlData1Pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SendZlp() {
|
|
||||||
sendUSBPacket(0, []byte{}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendViaEPIn(ep uint32, data []byte, count int) {
|
|
||||||
// Prepare buffer control register value
|
|
||||||
val := uint32(count) | usbBuf0CtrlAvail
|
|
||||||
|
|
||||||
// DATA0 or DATA1
|
|
||||||
epXdata0[ep&0x7F] = !epXdata0[ep&0x7F]
|
|
||||||
if !epXdata0[ep&0x7F] {
|
|
||||||
val |= usbBuf0CtrlData1Pid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as full
|
|
||||||
val |= usbBuf0CtrlFull
|
|
||||||
|
|
||||||
copy(_usbDPSRAM.EPxBuffer[ep&0x7F].Buffer0[:], data[:count])
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendStallViaEPIn(ep uint32) {
|
|
||||||
// Prepare buffer control register value
|
|
||||||
if ep == 0 {
|
|
||||||
rp.USBCTRL_REGS.EP_STALL_ARM.Set(rp.USBCTRL_REGS_EP_STALL_ARM_EP0_IN)
|
|
||||||
}
|
|
||||||
val := uint32(usbBuf0CtrlFull)
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
|
|
||||||
val |= uint32(usbBuf0CtrlStall)
|
|
||||||
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
type usbDPSRAM struct {
|
|
||||||
// Note that EPxControl[0] is not EP0Control but 8-byte setup data.
|
|
||||||
EPxControl [16]usbEndpointControlRegister
|
|
||||||
|
|
||||||
EPxBufferControl [16]usbBufferControlRegister
|
|
||||||
|
|
||||||
EPxBuffer [16]usbBuffer
|
|
||||||
}
|
|
||||||
|
|
||||||
type usbEndpointControlRegister struct {
|
|
||||||
In volatile.Register32
|
|
||||||
Out volatile.Register32
|
|
||||||
}
|
|
||||||
type usbBufferControlRegister struct {
|
|
||||||
In volatile.Register32
|
|
||||||
Out volatile.Register32
|
|
||||||
}
|
|
||||||
|
|
||||||
type usbBuffer struct {
|
|
||||||
Buffer0 [usbBufferLen]byte
|
|
||||||
Buffer1 [usbBufferLen]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_usbDPSRAM = (*usbDPSRAM)(unsafe.Pointer(uintptr(0x50100000)))
|
|
||||||
epXdata0 [16]bool
|
|
||||||
setupBytes [8]byte
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *usbDPSRAM) setupBytes() []byte {
|
|
||||||
|
|
||||||
data := d.EPxControl[usb.CONTROL_ENDPOINT].In.Get()
|
|
||||||
setupBytes[0] = byte(data)
|
|
||||||
setupBytes[1] = byte(data >> 8)
|
|
||||||
setupBytes[2] = byte(data >> 16)
|
|
||||||
setupBytes[3] = byte(data >> 24)
|
|
||||||
|
|
||||||
data = d.EPxControl[usb.CONTROL_ENDPOINT].Out.Get()
|
|
||||||
setupBytes[4] = byte(data)
|
|
||||||
setupBytes[5] = byte(data >> 8)
|
|
||||||
setupBytes[6] = byte(data >> 16)
|
|
||||||
setupBytes[7] = byte(data >> 24)
|
|
||||||
|
|
||||||
return setupBytes[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *usbDPSRAM) clear() {
|
|
||||||
for i := 0; i < len(d.EPxControl); i++ {
|
|
||||||
d.EPxControl[i].In.Set(0)
|
|
||||||
d.EPxControl[i].Out.Set(0)
|
|
||||||
d.EPxBufferControl[i].In.Set(0)
|
|
||||||
d.EPxBufferControl[i].Out.Set(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// DPRAM : Endpoint control register
|
|
||||||
usbEpControlEnable = 0x80000000
|
|
||||||
usbEpControlDoubleBuffered = 0x40000000
|
|
||||||
usbEpControlInterruptPerBuff = 0x20000000
|
|
||||||
usbEpControlInterruptPerDoubleBuff = 0x10000000
|
|
||||||
usbEpControlEndpointType = 0x0c000000
|
|
||||||
usbEpControlInterruptOnStall = 0x00020000
|
|
||||||
usbEpControlInterruptOnNak = 0x00010000
|
|
||||||
usbEpControlBufferAddress = 0x0000ffff
|
|
||||||
|
|
||||||
usbEpControlEndpointTypeControl = 0x00000000
|
|
||||||
usbEpControlEndpointTypeISO = 0x04000000
|
|
||||||
usbEpControlEndpointTypeBulk = 0x08000000
|
|
||||||
usbEpControlEndpointTypeInterrupt = 0x0c000000
|
|
||||||
|
|
||||||
// Endpoint buffer control bits
|
|
||||||
usbBuf1CtrlFull = 0x80000000
|
|
||||||
usbBuf1CtrlLast = 0x40000000
|
|
||||||
usbBuf1CtrlData0Pid = 0x20000000
|
|
||||||
usbBuf1CtrlData1Pid = 0x00000000
|
|
||||||
usbBuf1CtrlSel = 0x10000000
|
|
||||||
usbBuf1CtrlStall = 0x08000000
|
|
||||||
usbBuf1CtrlAvail = 0x04000000
|
|
||||||
usbBuf1CtrlLenMask = 0x03FF0000
|
|
||||||
usbBuf0CtrlFull = 0x00008000
|
|
||||||
usbBuf0CtrlLast = 0x00004000
|
|
||||||
usbBuf0CtrlData0Pid = 0x00000000
|
|
||||||
usbBuf0CtrlData1Pid = 0x00002000
|
|
||||||
usbBuf0CtrlSel = 0x00001000
|
|
||||||
usbBuf0CtrlStall = 0x00000800
|
|
||||||
usbBuf0CtrlAvail = 0x00000400
|
|
||||||
usbBuf0CtrlLenMask = 0x000003FF
|
|
||||||
|
|
||||||
usbBufferLen = 64
|
|
||||||
)
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user