Compare commits

..

2 Commits

Author SHA1 Message Date
sago35 3591e194f7 machine: changed to block if no read data exists on UART and USB 2022-03-29 08:53:52 +09:00
sago35 cc3009b5c1 os, runtime: enable os.Stdin for baremetal target 2022-03-29 08:51:23 +09:00
255 changed files with 5388 additions and 10551 deletions
+14 -10
View File
@@ -22,16 +22,15 @@ commands:
steps: steps:
- restore_cache: - restore_cache:
keys: keys:
- llvm-source-14-v2 - llvm-source-13-v1
- run: - run:
name: "Fetch LLVM source" name: "Fetch LLVM source"
command: make llvm-source command: make llvm-source
- save_cache: - save_cache:
key: llvm-source-14-v2 key: llvm-source-13-v1
paths: paths:
- llvm-project/clang/lib/Headers - llvm-project/clang/lib/Headers
- llvm-project/clang/include - llvm-project/clang/include
- llvm-project/compiler-rt
- llvm-project/lld/include - llvm-project/lld/include
- llvm-project/llvm/include - llvm-project/llvm/include
hack-ninja-jobs: hack-ninja-jobs:
@@ -86,10 +85,10 @@ commands:
- run: go install -tags=llvm<<parameters.llvm>> . - run: go install -tags=llvm<<parameters.llvm>> .
- restore_cache: - restore_cache:
keys: keys:
- wasi-libc-sysroot-systemclang-v5 - wasi-libc-sysroot-systemclang-v4
- run: make wasi-libc - run: make wasi-libc
- save_cache: - save_cache:
key: wasi-libc-sysroot-systemclang-v5 key: wasi-libc-sysroot-systemclang-v4
paths: paths:
- lib/wasi-libc/sysroot - lib/wasi-libc/sysroot
- run: make gen-device -j4 - run: make gen-device -j4
@@ -102,16 +101,21 @@ commands:
- run: make fmt-check - run: make fmt-check
jobs: jobs:
test-llvm13-go116: test-llvm11-go115:
docker:
- image: circleci/golang:1.15-buster
steps:
- test-linux:
llvm: "11"
test-llvm12-go116:
docker: docker:
- image: circleci/golang:1.16-buster - image: circleci/golang:1.16-buster
steps: steps:
- test-linux: - test-linux:
llvm: "13" llvm: "12"
workflows: workflows:
test-all: test-all:
jobs: jobs:
# This tests our lowest supported versions of Go and LLVM, to make sure at - test-llvm11-go115
# least the smoke tests still pass. - test-llvm12-go116
- test-llvm13-go116
+5 -6
View File
@@ -10,12 +10,12 @@ on:
jobs: jobs:
build-macos: build-macos:
name: build-macos name: build-macos
runs-on: macos-11 runs-on: macos-10.15
steps: steps:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: '1.18.1' go-version: '1.17'
- name: Install Dependencies - name: Install Dependencies
shell: bash shell: bash
run: | run: |
@@ -35,11 +35,10 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-14-macos-v1 key: llvm-source-13-macos-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Download LLVM source - name: Download LLVM source
@@ -49,7 +48,7 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-14-macos-v1 key: llvm-build-13-macos-v3
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -67,7 +66,7 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-wasi-libc id: cache-wasi-libc
with: with:
key: wasi-libc-sysroot-v3 key: wasi-libc-sysroot-v2
path: lib/wasi-libc/sysroot path: lib/wasi-libc/sysroot
- name: Build wasi-libc - name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true' if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
+12 -218
View File
@@ -26,7 +26,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: '1.18.1' go-version: '1.17'
- name: Cache Go - name: Cache Go
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
@@ -38,11 +38,10 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-14-linux-v2 key: llvm-source-13-linux-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Download LLVM source - name: Download LLVM source
@@ -52,7 +51,7 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-14-linux-v1 key: llvm-build-13-linux-v2
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -77,13 +76,14 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-wasi-libc id: cache-wasi-libc
with: with:
key: wasi-libc-sysroot-linux-asserts-v4 key: wasi-libc-sysroot-linux-asserts-v3
path: lib/wasi-libc/sysroot path: lib/wasi-libc/sysroot
- name: Build wasi-libc - name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true' if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc run: make wasi-libc
- name: Install fpm - name: Install fpm
run: | run: |
sudo apt-get install ruby ruby-dev
sudo gem install --no-document fpm sudo gem install --no-document fpm
- name: Build TinyGo release - name: Build TinyGo release
run: | run: |
@@ -93,7 +93,7 @@ jobs:
- name: Publish release artifact - name: Publish release artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: linux-amd64-double-zipped name: release-double-zipped
path: | path: |
/tmp/tinygo.linux-amd64.tar.gz /tmp/tinygo.linux-amd64.tar.gz
/tmp/tinygo_amd64.deb /tmp/tinygo_amd64.deb
@@ -107,7 +107,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: '1.18.1' go-version: '1.17'
- name: Install wasmtime - name: Install wasmtime
run: | run: |
curl https://wasmtime.dev/install.sh -sSf | bash curl https://wasmtime.dev/install.sh -sSf | bash
@@ -115,7 +115,7 @@ jobs:
- name: Download release artifact - name: Download release artifact
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
with: with:
name: linux-amd64-double-zipped name: release-double-zipped
- name: Extract release tarball - name: Extract release tarball
run: | run: |
mkdir -p ~/lib mkdir -p ~/lib
@@ -159,7 +159,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: '1.18.1' go-version: '1.17'
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
@@ -179,11 +179,10 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-14-linux-asserts-v2 key: llvm-source-13-linux-asserts-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Download LLVM source - name: Download LLVM source
@@ -193,7 +192,7 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-14-linux-asserts-v1 key: llvm-build-13-linux-asserts-v2
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -218,7 +217,7 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-wasi-libc id: cache-wasi-libc
with: with:
key: wasi-libc-sysroot-linux-asserts-v4 key: wasi-libc-sysroot-linux-asserts-v3
path: lib/wasi-libc/sysroot path: lib/wasi-libc/sysroot
- name: Build wasi-libc - name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true' if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
@@ -241,208 +240,3 @@ jobs:
- run: make smoketest - run: make smoketest
- run: make wasmtest - run: make wasmtest
- run: make tinygo-baremetal - run: make tinygo-baremetal
build-linux-arm:
# Build ARM Linux binaries, ready for release.
# This intentionally uses an older Linux image, so that we compile against
# an older glibc version and therefore are compatible with a wide range of
# Linux distributions.
# It is set to "needs: build-linux" because it modifies the release created
# in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball.
runs-on: ubuntu-18.04
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
qemu-user \
g++-arm-linux-gnueabihf \
libc6-dev-armhf-cross
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-arm-v2-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-14-linux-v2
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-14-linux-arm-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
run: |
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# Install build dependencies.
sudo apt-get install --no-install-recommends ninja-build
# build!
make llvm-build CROSS=arm-linux-gnueabihf
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-arm-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
sudo apt-get install --no-install-recommends ninja-build
git submodule update --init lib/binaryen
make CROSS=arm-linux-gnueabihf binaryen
- name: Install fpm
run: |
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=arm-linux-gnueabihf
- name: Download amd64 release
uses: actions/download-artifact@v2
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
run: |
mkdir -p build/release
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
- name: Modify release
run: |
cp -p build/tinygo build/release/tinygo/bin
cp -p build/wasm-opt build/release/tinygo/bin
- name: Create arm release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=armhf
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
cp -p build/release.deb /tmp/tinygo_armhf.deb
- name: Publish release artifact
uses: actions/upload-artifact@v2
with:
name: linux-arm-double-zipped
path: |
/tmp/tinygo.linux-arm.tar.gz
/tmp/tinygo_armhf.deb
build-linux-arm64:
# Build ARM64 Linux binaries, ready for release.
# It is set to "needs: build-linux" because it modifies the release created
# in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball.
runs-on: ubuntu-18.04
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
qemu-user \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
ninja-build
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-arm64-v2-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-14-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-14-linux-arm64-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
run: |
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# build!
make llvm-build CROSS=aarch64-linux-gnu
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-arm64-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
git submodule update --init lib/binaryen
make CROSS=aarch64-linux-gnu binaryen
- name: Install fpm
run: |
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=aarch64-linux-gnu
- name: Download amd64 release
uses: actions/download-artifact@v2
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
run: |
mkdir -p build/release
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
- name: Modify release
run: |
cp -p build/tinygo build/release/tinygo/bin
cp -p build/wasm-opt build/release/tinygo/bin
- name: Create arm64 release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=arm64
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
cp -p build/release.deb /tmp/tinygo_arm64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v2
with:
name: linux-arm64-double-zipped
path: |
/tmp/tinygo.linux-arm64.tar.gz
/tmp/tinygo_arm64.deb
+5 -13
View File
@@ -9,15 +9,13 @@ on:
jobs: jobs:
build-windows: build-windows:
runs-on: windows-2022 runs-on: windows-2019
steps: steps:
- name: Install Go - name: Install Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: '1.18.1' go-version: '1.17'
- uses: brechtm/setup-scoop@v2 - uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
- name: Install Dependencies - name: Install Dependencies
shell: bash shell: bash
run: | run: |
@@ -37,11 +35,10 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-llvm-source id: cache-llvm-source
with: with:
key: llvm-source-14-windows-v2 key: llvm-source-13-windows-v1
path: | path: |
llvm-project/clang/lib/Headers llvm-project/clang/lib/Headers
llvm-project/clang/include llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include llvm-project/lld/include
llvm-project/llvm/include llvm-project/llvm/include
- name: Download LLVM source - name: Download LLVM source
@@ -51,7 +48,7 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-llvm-build id: cache-llvm-build
with: with:
key: llvm-build-14-windows-v1 key: llvm-build-13-windows-v2
path: llvm-build path: llvm-build
- name: Build LLVM - name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true' if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -68,14 +65,11 @@ jobs:
uses: actions/cache@v2 uses: actions/cache@v2
id: cache-wasi-libc id: cache-wasi-libc
with: with:
key: wasi-libc-sysroot-v3 key: wasi-libc-sysroot-v2
path: lib/wasi-libc/sysroot path: lib/wasi-libc/sysroot
- name: Build wasi-libc - name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true' if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc run: make wasi-libc
- name: Install wasmtime
run: |
scoop install wasmtime
- name: Test TinyGo - name: Test TinyGo
shell: bash shell: bash
run: make test GOTESTFLAGS="-v -short" run: make test GOTESTFLAGS="-v -short"
@@ -101,5 +95,3 @@ jobs:
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0 run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
- name: Test stdlib packages - name: Test stdlib packages
run: make tinygo-test run: make tinygo-test
- name: Test stdlib packages on wasi
run: make tinygo-test-wasi-fast
+4
View File
@@ -10,6 +10,10 @@
[submodule "lib/cmsis-svd"] [submodule "lib/cmsis-svd"]
path = lib/cmsis-svd path = lib/cmsis-svd
url = https://github.com/tinygo-org/cmsis-svd url = https://github.com/tinygo-org/cmsis-svd
[submodule "lib/compiler-rt"]
path = lib/compiler-rt
url = https://github.com/llvm-mirror/compiler-rt.git
branch = release_80
[submodule "lib/wasi-libc"] [submodule "lib/wasi-libc"]
path = lib/wasi-libc path = lib/wasi-libc
url = https://github.com/CraneStation/wasi-libc url = https://github.com/CraneStation/wasi-libc
+1 -1
View File
@@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
LLVM, Clang and LLD are quite light on dependencies, requiring only standard LLVM, Clang and LLD are quite light on dependencies, requiring only standard
build tools to be built. Go is of course necessary to build TinyGo itself. build tools to be built. Go is of course necessary to build TinyGo itself.
* Go (1.16+) * Go (1.15+)
* Standard build tools (gcc/clang) * Standard build tools (gcc/clang)
* git * git
* CMake * CMake
-98
View File
@@ -1,101 +1,3 @@
0.23.0
---
* **command line**
- add `-work` flag
- add Go 1.18 support
- add LLVM 14 support
- `run`: add support for command-line parameters
- `build`: calculate default output path if `-o` is not specified
- `build`: add JSON output
- `test`: support multiple test binaries with `-c`
- `test`: support flags like `-v` on all targets (including emulated firmware)
* **compiler**
- add support for ThinLTO
- use compiler-rt from LLVM
- `builder`: prefer GNU build ID over Go build ID for caching
- `builder`: add support for cross compiling to Darwin
- `builder`: support machine outlining pass in stacksize calculation
- `builder`: disable asynchronous unwind tables
- `compileopts`: fix emulator configuration on non-amd64 Linux architectures
- `compiler`: move allocations > 256 bytes to the heap
- `compiler`: fix incorrect `unsafe.Alignof` on some 32-bit architectures
- `compiler`: accept alias for slice `cap` builtin
- `compiler`: allow slices of empty structs
- `compiler`: fix difference in aliases in interface methods
- `compiler`: make `RawSyscall` an alias for `Syscall`
- `compiler`: remove support for memory references in `AsmFull`
- `loader`: only add Clang header path for CGo
- `transform`: fix poison value in heap-to-stack transform
* **standard library**
- `internal/fuzz`: add this package as a shim
- `os`: implement readdir for darwin and linux
- `os`: add `DirFS`, which is used by many programs to access readdir.
- `os`: isWine: be compatible with older versions of wine, too
- `os`: implement `RemoveAll`
- `os`: Use a `uintptr` for `NewFile`
- `os`: add stubs for `exec.ExitError` and `ProcessState.ExitCode`
- `os`: export correct values for `DevNull` for each OS
- `os`: improve support for `Signal` by fixing various bugs
- `os`: implement `File.Fd` method
- `os`: implement `UserHomeDir`
- `os`: add `exec.ProcessState` stub
- `os`: implement `Pipe` for darwin
- `os`: define stub `ErrDeadlineExceeded`
- `reflect`: add stubs for more missing methods
- `reflect`: rename `reflect.Ptr` to `reflect.Pointer`
- `reflect`: add `Value.FieldByIndexErr` stub
- `runtime`: fix various small GC bugs
- `runtime`: use memzero for leaking collector instead of manually zeroing objects
- `runtime`: implement `memhash`
- `runtime`: implement `fastrand`
- `runtime`: add stub for `debug.ReadBuildInfo`
- `runtime`: add stub for `NumCPU`
- `runtime`: don't inline `runtime.alloc` with `-gc=leaking`
- `runtime`: add `Version`
- `runtime`: add stubs for `NumCgoCall` and `NumGoroutine`
- `runtime`: stub {Lock,Unlock}OSThread on Windows
- `runtime`: be able to deal with a very small heap
- `syscall`: make `Environ` return a copy of the environment
- `syscall`: implement getpagesize and munmap
- `syscall`: `wasi`: define `MAP_SHARED` and `PROT_READ`
- `syscall`: stub mmap(), munmap(), MAP_SHARED, PROT_READ, SIGBUS, etc. on nonhosted targets
- `syscall`: darwin: more complete list of signals
- `syscall`: `wasi`: more complete list of signals
- `syscall`: stub `WaitStatus`
- `syscall/js`: allow copyBytesTo(Go|JS) to use `Uint8ClampedArray`
- `testing`: implement `TempDir`
- `testing`: nudge type TB closer to upstream; should be a no-op change.
- `testing`: on baremetal platforms, use simpler test matcher
* **targets**
- `atsamd`: fix usbcdc initialization when `-serial=uart`
- `atsamd51`: allow higher frequency when using SPI
- `esp`: support CGo
- `esp32c3`: add support for input pin
- `esp32c3`: add support for GPIO interrupts
- `esp32c3`: add support to receive UART data
- `rp2040`: fix PWM bug at high frequency
- `rp2040`: fix some minor I2C bugs
- `rp2040`: fix incorrect inline assembly
- `rp2040`: fix spurious i2c STOP during write+read transaction
- `rp2040`: improve ADC support
- `wasi`: remove `--export-dynamic` linker flag
- `wasm`: remove heap allocator from wasi-libc
* **boards**
- `circuitplay-bluefruit`: move pin mappings so board can be compiled for WASM use in Playground
- `esp32-c3-12f`: add the ESP32-C3-12f Kit
- `m5stamp-c3`: add pin setting of UART
- `macropad-rp2040`: add the Adafruit MacroPad RP2040 board
- `nano-33-ble`: typo in LPS22HB peripheral definition and documentation (#2579)
- `teensy41`: add the Teensy 4.1 board
- `teensy40`: add ADC support
- `teensy40`: add SPI support
- `thingplus-rp2040`: add the SparkFun Thing Plus RP2040 board
- `wioterminal`: add DefaultUART
- `wioterminal`: verify written data when flashing through OpenOCD
- `xiao-ble`: add XIAO BLE nRF52840 support
0.22.0 0.22.0
--- ---
+1 -1
View File
@@ -1,5 +1,5 @@
# tinygo-llvm stage obtains the llvm source for TinyGo # tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.18 AS tinygo-llvm FROM golang:1.17 AS tinygo-llvm
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
+26 -82
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools. # Try to autodetect LLVM build tools.
# Versions are listed here in descending priority order. # Versions are listed here in descending priority order.
LLVM_VERSIONS = 14 13 12 11 LLVM_VERSIONS = 13 12 11
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)
@@ -50,38 +50,6 @@ else
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF' LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
endif endif
# Cross compiling support.
ifneq ($(CROSS),)
CC = $(CROSS)-gcc
CXX = $(CROSS)-g++
LLVM_OPTION += \
-DCMAKE_C_COMPILER=$(CC) \
-DCMAKE_CXX_COMPILER=$(CXX) \
-DLLVM_DEFAULT_TARGET_TRIPLE=$(CROSS) \
-DCROSS_TOOLCHAIN_FLAGS_NATIVE="-UCMAKE_C_COMPILER;-UCMAKE_CXX_COMPILER"
ifeq ($(CROSS), arm-linux-gnueabihf)
# Assume we're building on a Debian-like distro, with QEMU installed.
LLVM_CONFIG_PREFIX = qemu-arm -L /usr/arm-linux-gnueabihf/
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
LLVM_OPTION += \
-DCMAKE_SYSTEM_NAME=Linux \
-DLLVM_TARGET_ARCH=ARM
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
else ifeq ($(CROSS), aarch64-linux-gnu)
# Assume we're building on a Debian-like distro, with QEMU installed.
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
LLVM_OPTION += \
-DCMAKE_SYSTEM_NAME=Linux \
-DLLVM_TARGET_ARCH=AArch64
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
else
$(error Unknown cross compilation target: $(CROSS))
endif
endif
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp .PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
@@ -123,11 +91,11 @@ CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGe
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++ CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
# Libraries that should be linked in for the statically linked LLD. # Libraries that should be linked in for the statically linked LLD.
LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO2 lldMinGW lldReaderWriter lldWasm lldYAML
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP) LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
# Other libraries that are needed to link TinyGo. # Other libraries that are needed to link TinyGo.
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA EXTRA_LIB_NAMES = LLVMInterpreter
# All libraries to be built and linked with the tinygo binary (lib/lib*.a). # All libraries to be built and linked with the tinygo binary (lib/lib*.a).
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES) LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
@@ -142,9 +110,9 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(ad
# For static linking. # For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","") ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CXXFLAGS=-std=c++14 CGO_CXXFLAGS=-std=c++14
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA) CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
endif endif
clean: clean:
@@ -207,12 +175,12 @@ gen-device-rp: build/gen-device-svd
# Get LLVM sources. # Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm: $(LLVM_PROJECTDIR)/llvm:
git clone -b xtensa_release_14.0.0-patched --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR) git clone -b xtensa_release_13.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM. # Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd) TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: $(LLVM_BUILDDIR)/build.ninja: llvm-source
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION) mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
# Build LLVM. # Build LLVM.
@@ -240,18 +208,21 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
# Build the Go compiler. # Build the Go compiler.
tinygo: tinygo:
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi @if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" . CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform . CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi # Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
TEST_PACKAGES_SLOW = \ TEST_PACKAGES_SLOW = \
compress/bzip2 \ compress/bzip2 \
compress/flate \
crypto/dsa \ crypto/dsa \
index/suffixarray \ index/suffixarray \
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows # Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
TEST_PACKAGES_FAST = \ TEST_PACKAGES_FAST = \
compress/lzw \
compress/zlib \ compress/zlib \
container/heap \ container/heap \
container/list \ container/list \
@@ -265,7 +236,6 @@ TEST_PACKAGES_FAST = \
crypto/sha256 \ crypto/sha256 \
crypto/sha512 \ crypto/sha512 \
debug/macho \ debug/macho \
embed/internal/embedtest \
encoding \ encoding \
encoding/ascii85 \ encoding/ascii85 \
encoding/base32 \ encoding/base32 \
@@ -299,25 +269,17 @@ TEST_PACKAGES_FAST = \
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows # debug/plan9obj requires os.ReadAt, which is not yet supported on windows
# io/fs requires os.ReadDir, which is not yet supported on windows or wasi # io/fs requires os.ReadDir, which is not yet supported on windows or wasi
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi # testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
# compress/flate fails windows go 1.18, https://github.com/tinygo-org/tinygo/issues/2762
# compress/lzw fails windows go 1.18 wasi, https://github.com/tinygo-org/tinygo/issues/2762
# Additional standard library packages that pass tests on individual platforms # Additional standard library packages that pass tests on individual platforms
TEST_PACKAGES_LINUX := \ TEST_PACKAGES_LINUX := \
archive/zip \ archive/zip \
compress/flate \
compress/lzw \
debug/dwarf \ debug/dwarf \
debug/plan9obj \ debug/plan9obj \
io/fs \ io/fs \
io/ioutil \
testing/fstest testing/fstest
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX) TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
TEST_PACKAGES_WINDOWS := \
compress/lzw
# 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.$$$$) jointmp := $(shell echo /tmp/join.$$$$)
report-stdlib-tests-pass: report-stdlib-tests-pass:
@@ -336,7 +298,7 @@ ifeq ($(shell uname),Linux)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX) TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
endif endif
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS) TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST)
endif endif
# Test known-working standard library packages. # Test known-working standard library packages.
@@ -377,8 +339,6 @@ tinygo-baremetal:
.PHONY: smoketest .PHONY: smoketest
smoketest: smoketest:
$(TINYGO) version $(TINYGO) version
# regression test for #2892
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
# compile-only platform-independent examples # compile-only platform-independent examples
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
# regression test for #2563 # regression test for #2563
@@ -416,10 +376,6 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test $(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
@$(MD5SUM) test.hex
# test simulated boards on play.tinygo.org # test simulated boards on play.tinygo.org
ifneq ($(WASM), 0) ifneq ($(WASM), 0)
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1 $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
@@ -434,8 +390,6 @@ ifneq ($(WASM), 0)
@$(MD5SUM) test.wasm @$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1 $(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
@$(MD5SUM) test.wasm @$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
@$(MD5SUM) test.wasm
endif endif
# test all targets/boards # test all targets/boards
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
@@ -561,11 +515,6 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm $(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
# test usbhid
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
@$(MD5SUM) test.hex
ifneq ($(STM32), 0) ifneq ($(STM32), 0)
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1 $(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
@@ -657,8 +606,7 @@ endif
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo GOOS=darwin GOARCH=amd64 $(TINYGO) build -o test ./testdata/cgo
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
ifneq ($(OS),Windows_NT) ifneq ($(OS),Windows_NT)
# TODO: this does not yet work on Windows. Somehow, unused functions are # TODO: this does not yet work on Windows. Somehow, unused functions are
# not garbage collected. # not garbage collected.
@@ -673,6 +621,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
@mkdir -p build/release/tinygo/bin @mkdir -p build/release/tinygo/bin
@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/compiler-rt/lib
@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/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-headers/defaults @mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@@ -694,6 +643,9 @@ endif
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include @cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS @cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS @cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
@cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk @cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
@@ -730,29 +682,21 @@ 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-stdio.c build/release/tinygo/lib @cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot @cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
@cp -rp src build/release/tinygo/src @cp -rp src build/release/tinygo/src
@cp -rp targets build/release/tinygo/targets @cp -rp targets build/release/tinygo/targets
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt ./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt ./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt ./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc ./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc ./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc ./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
release: release: build/release
tar -czf build/release.tar.gz -C build/release tinygo tar -czf build/release.tar.gz -C build/release tinygo
DEB_ARCH ?= native deb: build/release
deb:
@mkdir -p build/release-deb/usr/local/bin @mkdir -p build/release-deb/usr/local/bin
@mkdir -p build/release-deb/usr/local/lib @mkdir -p build/release-deb/usr/local/lib
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
ifneq ($(RELEASEONLY), 1)
release: build/release
deb: build/release
endif
+1 -4
View File
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux. You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 85 microcontroller boards are currently supported: The following 82 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333) * [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333) * [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
@@ -85,7 +85,6 @@ The following 85 microcontroller boards are currently supported:
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html) * [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
* [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32) * [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32)
* [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32) * [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32)
* [ESP32-C3-12f](https://www.espressif.com/en/products/socs/esp32-c3)
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266) * [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266) * [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance) * [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
@@ -113,12 +112,10 @@ The following 85 microcontroller boards are currently supported:
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/) * [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199) * [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html) * [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html) * [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html) * [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) * [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1) * [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html) * [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html) * [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html) * [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
+94 -260
View File
@@ -4,7 +4,6 @@
package builder package builder
import ( import (
"crypto/sha256"
"crypto/sha512" "crypto/sha512"
"debug/elf" "debug/elf"
"encoding/binary" "encoding/binary"
@@ -38,14 +37,8 @@ import (
// BuildResult is the output of a build. This includes the binary itself and // BuildResult is the output of a build. This includes the binary itself and
// some other metadata that is obtained while building the binary. // some other metadata that is obtained while building the binary.
type BuildResult struct { type BuildResult struct {
// The executable directly from the linker, usually including debug
// information. Used for GDB for example.
Executable string
// A path to the output binary. It will be removed after Build returns, so // A path to the output binary. It will be removed after Build returns, so
// if it should be kept it must be copied or moved away. // if it should be kept it must be copied or moved away.
// It is often the same as Executable, but differs if the output format is
// .hex for example (instead of the usual ELF).
Binary string Binary string
// The directory of the main package. This is useful for testing as the test // The directory of the main package. This is useful for testing as the test
@@ -81,7 +74,6 @@ type packageAction struct {
Config *compiler.Config Config *compiler.Config
CFlags []string CFlags []string
FileHashes map[string]string // hash of every file that's part of the package FileHashes map[string]string // hash of every file that's part of the package
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
Imports map[string]string // map from imported package to action ID hash Imports map[string]string // map from imported package to action ID hash
OptLevel int // LLVM optimization level (0-3) OptLevel int // LLVM optimization level (0-3)
SizeLevel int // LLVM optimization for size level (0-2) SizeLevel int // LLVM optimization for size level (0-2)
@@ -190,10 +182,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
return err return err
} }
defer machine.Dispose()
// Load entire program AST into memory. // Load entire program AST into memory.
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{ lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine), Sizes: compiler.Sizes(machine),
}) })
if err != nil { if err != nil {
@@ -211,7 +202,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Add jobs to compile each package. // Add jobs to compile each package.
// Packages that have a cache hit will not be compiled again. // Packages that have a cache hit will not be compiled again.
var packageJobs []*compileJob var packageJobs []*compileJob
packageActionIDJobs := make(map[string]*compileJob) packageBitcodePaths := make(map[string]string)
packageActionIDs := make(map[string]string)
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" { if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
version := goenv.Version version := goenv.Version
@@ -227,7 +219,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
config.Options.GlobalValues["runtime"]["buildVersion"] = version config.Options.GlobalValues["runtime"]["buildVersion"] = version
} }
var embedFileObjects []*compileJob
for _, pkg := range lprogram.Sorted() { for _, pkg := range lprogram.Sorted() {
pkg := pkg // necessary to avoid a race condition pkg := pkg // necessary to avoid a race condition
@@ -237,114 +228,52 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
} }
sort.Strings(undefinedGlobals) sort.Strings(undefinedGlobals)
// Make compile jobs to load files to be embedded in the output binary. // Create a cache key: a hash from the action ID below that contains all
var actionIDDependencies []*compileJob // the parameters for the build.
allFiles := map[string][]*loader.EmbedFile{} actionID := packageAction{
for _, files := range pkg.EmbedGlobals { ImportPath: pkg.ImportPath,
for _, file := range files { CompilerBuildID: string(compilerBuildID),
allFiles[file.Name] = append(allFiles[file.Name], file) TinyGoVersion: goenv.Version,
} LLVMVersion: llvm.Version,
Config: compilerConfig,
CFlags: pkg.CFlags,
FileHashes: make(map[string]string, len(pkg.FileHashes)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
OptLevel: optLevel,
SizeLevel: sizeLevel,
UndefinedGlobals: undefinedGlobals,
} }
for name, files := range allFiles { for filePath, hash := range pkg.FileHashes {
name := name actionID.FileHashes[filePath] = hex.EncodeToString(hash)
files := files
job := &compileJob{
description: "make object file for " + name,
run: func(job *compileJob) error {
// Read the file contents in memory.
path := filepath.Join(pkg.Dir, name)
data, err := os.ReadFile(path)
if err != nil {
return err
}
// Hash the file.
sum := sha256.Sum256(data)
hexSum := hex.EncodeToString(sum[:16])
for _, file := range files {
file.Size = uint64(len(data))
file.Hash = hexSum
if file.NeedsData {
file.Data = data
}
}
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), dir, compilerConfig)
return err
},
}
actionIDDependencies = append(actionIDDependencies, job)
embedFileObjects = append(embedFileObjects, job)
} }
// Action ID jobs need to know the action ID of all the jobs the package
// imports.
var importedPackages []*compileJob
for _, imported := range pkg.Pkg.Imports() { for _, imported := range pkg.Pkg.Imports() {
job, ok := packageActionIDJobs[imported.Path()] hash, ok := packageActionIDs[imported.Path()]
if !ok { if !ok {
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path()) return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
} }
importedPackages = append(importedPackages, job) actionID.Imports[imported.Path()] = hash
actionIDDependencies = append(actionIDDependencies, job)
} }
buf, err := json.Marshal(actionID)
// Create a job that will calculate the action ID for a package compile if err != nil {
// job. The action ID is the cache key that is used for caching this panic(err) // shouldn't happen
// package.
packageActionIDJob := &compileJob{
description: "calculate cache key for package " + pkg.ImportPath,
dependencies: actionIDDependencies,
run: func(job *compileJob) error {
// Create a cache key: a hash from the action ID below that contains all
// the parameters for the build.
actionID := packageAction{
ImportPath: pkg.ImportPath,
CompilerBuildID: string(compilerBuildID),
TinyGoVersion: goenv.Version,
LLVMVersion: llvm.Version,
Config: compilerConfig,
CFlags: pkg.CFlags,
FileHashes: make(map[string]string, len(pkg.FileHashes)),
EmbeddedFiles: make(map[string]string, len(allFiles)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
OptLevel: optLevel,
SizeLevel: sizeLevel,
UndefinedGlobals: undefinedGlobals,
}
for filePath, hash := range pkg.FileHashes {
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
}
for name, files := range allFiles {
actionID.EmbeddedFiles[name] = files[0].Hash
}
for i, imported := range pkg.Pkg.Imports() {
actionID.Imports[imported.Path()] = importedPackages[i].result
}
buf, err := json.Marshal(actionID)
if err != nil {
return err // shouldn't happen
}
hash := sha512.Sum512_224(buf)
job.result = hex.EncodeToString(hash[:])
return nil
},
} }
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob hash := sha512.Sum512_224(buf)
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
// Now create the job to actually build the package. It will exit early // Determine the path of the bitcode file (which is a serialized version
// if the package is already compiled. // of a LLVM module).
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
packageBitcodePaths[pkg.ImportPath] = bitcodePath
// The package has not yet been compiled, so create a job to do so.
job := &compileJob{ job := &compileJob{
description: "compile package " + pkg.ImportPath, description: "compile package " + pkg.ImportPath,
dependencies: []*compileJob{packageActionIDJob}, run: func(*compileJob) error {
run: func(job *compileJob) error {
job.result = filepath.Join(cacheDir, "pkg-"+packageActionIDJob.result+".bc")
// Acquire a lock (if supported). // Acquire a lock (if supported).
unlock := lock(job.result + ".lock") unlock := lock(bitcodePath + ".lock")
defer unlock() defer unlock()
if _, err := os.Stat(job.result); err == nil { if _, err := os.Stat(bitcodePath); err == nil {
// Already cached, don't recreate this package. // Already cached, don't recreate this package.
return nil return nil
} }
@@ -352,8 +281,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Compile AST to IR. The compiler.CompilePackage function will // Compile AST to IR. The compiler.CompilePackage function will
// build the SSA as needed. // build the SSA as needed.
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA()) mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
defer mod.Context().Dispose()
defer mod.Dispose()
if errs != nil { if errs != nil {
return newMultiError(errs) return newMultiError(errs)
} }
@@ -439,13 +366,33 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return errors.New("verification error after interpreting " + pkgInit.Name()) return errors.New("verification error after interpreting " + pkgInit.Name())
} }
transform.OptimizePackage(mod, config) // Run function passes for each function in the module.
// These passes are intended to be run on each function right
// after they're created to reduce IR size (and maybe also for
// cache locality to improve performance), but for now they're
// run here for each function in turn. Maybe this can be
// improved in the future.
builder := llvm.NewPassManagerBuilder()
defer builder.Dispose()
builder.SetOptLevel(optLevel)
builder.SetSizeLevel(sizeLevel)
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
defer funcPasses.Dispose()
builder.PopulateFunc(funcPasses)
funcPasses.InitializeFunc()
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
if fn.IsDeclaration() {
continue
}
funcPasses.RunFunc(fn)
}
funcPasses.FinalizeFunc()
// Serialize the LLVM module as a bitcode file. // Serialize the LLVM module as a bitcode file.
// Write to a temporary path that is renamed to the destination // Write to a temporary path that is renamed to the destination
// file to avoid race conditions with other TinyGo invocatiosn // file to avoid race conditions with other TinyGo invocatiosn
// that might also be compiling this package at the same time. // that might also be compiling this package at the same time.
f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result)) f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
if err != nil { if err != nil {
return err return err
} }
@@ -465,13 +412,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
// WriteBitcodeToFile doesn't produce a useful error on its // WriteBitcodeToFile doesn't produce a useful error on its
// own, so create a somewhat useful error message here. // own, so create a somewhat useful error message here.
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, job.result) return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
} }
err = f.Close() err = f.Close()
if err != nil { if err != nil {
return err return err
} }
return os.Rename(f.Name(), job.result) return os.Rename(f.Name(), bitcodePath)
}, },
} }
packageJobs = append(packageJobs, job) packageJobs = append(packageJobs, job)
@@ -479,13 +426,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Add job that links and optimizes all packages together. // Add job that links and optimizes all packages together.
var mod llvm.Module var mod llvm.Module
defer func() {
if !mod.IsNil() {
ctx := mod.Context()
mod.Dispose()
ctx.Dispose()
}
}()
var stackSizeLoads []string var stackSizeLoads []string
programJob := &compileJob{ programJob := &compileJob{
description: "link+optimize packages (LTO)", description: "link+optimize packages (LTO)",
@@ -495,8 +435,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// anything, it only links the bitcode files together. // anything, it only links the bitcode files together.
ctx := llvm.NewContext() ctx := llvm.NewContext()
mod = ctx.NewModule("main") mod = ctx.NewModule("main")
for _, pkgJob := range packageJobs { for _, pkg := range lprogram.Sorted() {
pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result) pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
if err != nil { if err != nil {
return fmt.Errorf("failed to load bitcode file: %w", err) return fmt.Errorf("failed to load bitcode file: %w", err)
} }
@@ -588,7 +528,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
return err return err
} }
defer llvmBuf.Dispose()
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666) return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc": case ".bc":
var buf llvm.MemoryBuffer var buf llvm.MemoryBuffer
@@ -697,9 +636,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Add libc dependencies, if they exist. // Add libc dependencies, if they exist.
linkerDependencies = append(linkerDependencies, libcDependencies...) linkerDependencies = append(linkerDependencies, libcDependencies...)
// Add embedded files.
linkerDependencies = append(linkerDependencies, embedFileObjects...)
// Strip debug information with -no-debug. // Strip debug information with -no-debug.
if !config.Debug() { if !config.Debug() {
for _, tag := range config.BuildTags() { for _, tag := range config.BuildTags() {
@@ -709,12 +645,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets") return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
} }
} }
if config.GOOS() == "darwin" {
// Debug information isn't stored in the binary itself on MacOS but
// is left in the object files by default. The binary does store the
// path to these object files though.
return errors.New("cannot remove debug information: MacOS doesn't store debug info in the executable by default")
}
if config.Target.Linker == "wasm-ld" { if config.Target.Linker == "wasm-ld" {
// Don't just strip debug information, also compress relocations // Don't just strip debug information, also compress relocations
// while we're at it. Relocations can only be compressed when debug // while we're at it. Relocations can only be compressed when debug
@@ -724,8 +654,21 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// ld.lld is also used on Linux. // ld.lld is also used on Linux.
ldflags = append(ldflags, "--strip-debug") ldflags = append(ldflags, "--strip-debug")
} else { } else {
// Other linkers may have different flags. switch config.GOOS() {
return errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker) case "linux":
// Either real linux or an embedded system (like AVR) that
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
// case (not ld.lld - that case is handled above).
ldflags = append(ldflags, "-Wl,--strip-debug")
case "darwin":
// MacOS (darwin) doesn't have a linker flag to strip debug
// information. Apple expects you to use the strip command
// instead.
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
default:
// Other OSes may have different flags.
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
}
} }
} }
@@ -745,24 +688,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
config.Options.PrintCommands(config.Target.Linker, ldflags...) config.Options.PrintCommands(config.Target.Linker, ldflags...)
} }
if config.UseThinLTO() { if config.UseThinLTO() {
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU()) ldflags = append(ldflags,
if config.GOOS() == "windows" { "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
// Options for the MinGW wrapper for the lld COFF linker. "-plugin-opt=mcpu="+config.CPU(),
ldflags = append(ldflags, "-plugin-opt=O"+strconv.Itoa(optLevel),
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel), "-plugin-opt=thinlto")
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
// Options for the ELF linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
)
}
if config.CodeModel() != "default" { if config.CodeModel() != "default" {
ldflags = append(ldflags, ldflags = append(ldflags,
"-mllvm", "-code-model="+config.CodeModel()) "-mllvm", "-code-model="+config.CodeModel())
@@ -905,7 +835,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
return err return err
} }
case "esp32", "esp32-img", "esp32c3", "esp8266": case "esp32", "esp32c3", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM // Special format for the ESP family of chips (parsed by the ROM
// bootloader). // bootloader).
tmppath = filepath.Join(dir, "main"+outext) tmppath = filepath.Join(dir, "main"+outext)
@@ -937,7 +867,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
} }
return action(BuildResult{ return action(BuildResult{
Executable: executable,
Binary: tmppath, Binary: tmppath,
MainDir: lprogram.MainPkg().Dir, MainDir: lprogram.MainPkg().Dir,
ModuleRoot: moduleroot, ModuleRoot: moduleroot,
@@ -945,112 +874,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
}) })
} }
// createEmbedObjectFile creates a new object file with the given contents, for
// the embed package.
func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, compilerConfig *compiler.Config) (string, error) {
// TODO: this works for small files, but can be a problem for larger files.
// For larger files, it seems more appropriate to generate the object file
// manually without going through LLVM.
// On the other hand, generating DWARF like we do here can be difficult
// without assistance from LLVM.
// Create new LLVM module just for this file.
ctx := llvm.NewContext()
defer ctx.Dispose()
mod := ctx.NewModule("data")
defer mod.Dispose()
// Create data global.
value := ctx.ConstString(data, false)
globalName := "embed/file_" + hexSum
global := llvm.AddGlobal(mod, value.Type(), globalName)
global.SetInitializer(value)
global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetAlignment(1)
if compilerConfig.GOOS != "darwin" {
// MachO doesn't support COMDATs, while COFF requires it (to avoid
// "duplicate symbol" errors). ELF works either way.
// Therefore, only use a COMDAT on non-MachO systems (aka non-MacOS).
global.SetComdat(mod.Comdat(globalName))
}
// Add DWARF debug information to this global, so that it is
// correctly counted when compiling with the -size= flag.
dibuilder := llvm.NewDIBuilder(mod)
dibuilder.CreateCompileUnit(llvm.DICompileUnit{
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
File: sourceFile,
Dir: sourceDir,
Producer: "TinyGo",
Optimized: false,
})
ditype := dibuilder.CreateArrayType(llvm.DIArrayType{
SizeInBits: uint64(len(data)) * 8,
AlignInBits: 8,
ElementType: dibuilder.CreateBasicType(llvm.DIBasicType{
Name: "byte",
SizeInBits: 8,
Encoding: llvm.DW_ATE_unsigned_char,
}),
Subscripts: []llvm.DISubrange{
{
Lo: 0,
Count: int64(len(data)),
},
},
})
difile := dibuilder.CreateFile(sourceFile, sourceDir)
diglobalexpr := dibuilder.CreateGlobalVariableExpression(difile, llvm.DIGlobalVariableExpression{
Name: globalName,
File: difile,
Line: 1,
Type: ditype,
Expr: dibuilder.CreateExpression(nil),
AlignInBits: 8,
})
global.AddMetadata(0, diglobalexpr)
mod.AddNamedMetadataOperand("llvm.module.flags",
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
ctx.MDString("Debug Info Version"),
llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(),
}),
)
mod.AddNamedMetadataOperand("llvm.module.flags",
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
ctx.MDString("Dwarf Version"),
llvm.ConstInt(ctx.Int32Type(), 4, false).ConstantAsMetadata(),
}),
)
dibuilder.Finalize()
dibuilder.Destroy()
// Write this LLVM module out as an object file.
machine, err := compiler.NewTargetMachine(compilerConfig)
if err != nil {
return "", err
}
defer machine.Dispose()
outfile, err := os.CreateTemp(tmpdir, "embed-"+hexSum+"-*.o")
if err != nil {
return "", err
}
defer outfile.Close()
buf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return "", err
}
defer buf.Dispose()
_, err = outfile.Write(buf.Bytes())
if err != nil {
return "", err
}
return outfile.Name(), outfile.Close()
}
// optimizeProgram runs a series of optimizations and transformations that are // optimizeProgram runs a series of optimizations and transformations that are
// needed to convert a program to its final form. Some transformations are not // needed to convert a program to its final form. Some transformations are not
// optional and must be run as the compiler expects them to run. // optional and must be run as the compiler expects them to run.
@@ -1105,6 +928,17 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
return errors.New("verification failure after LLVM optimization passes") return errors.New("verification failure after LLVM optimization passes")
} }
// LLVM 11 by default tries to emit tail calls (even with the target feature
// disabled) unless it is explicitly disabled with a function attribute.
// This is a problem, as it tries to emit them and prints an error when it
// can't with this feature disabled.
// Because as of september 2020 tail calls are not yet widely supported,
// they need to be disabled until they are widely supported (at which point
// the +tail-call target feautre can be set).
if strings.HasPrefix(config.Triple(), "wasm") {
transform.DisableTailCalls(mod)
}
return nil return nil
} }
+1 -1
View File
@@ -26,7 +26,7 @@ func TestClangAttributes(t *testing.T) {
"cortex-m0", "cortex-m0",
"cortex-m0plus", "cortex-m0plus",
"cortex-m3", "cortex-m3",
"cortex-m33", //"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
"cortex-m4", "cortex-m4",
"cortex-m7", "cortex-m7",
"esp32c3", "esp32c3",
+3 -22
View File
@@ -1,11 +1,7 @@
package builder package builder
import ( import (
"os"
"path/filepath"
"strings" "strings"
"github.com/tinygo-org/tinygo/goenv"
) )
// These are the GENERIC_SOURCES according to CMakeList.txt. // These are the GENERIC_SOURCES according to CMakeList.txt.
@@ -40,6 +36,7 @@ var genericBuiltins = []string{
"divdf3.c", "divdf3.c",
"divdi3.c", "divdi3.c",
"divmoddi4.c", "divmoddi4.c",
"divmodsi4.c",
"divsc3.c", "divsc3.c",
"divsf3.c", "divsf3.c",
"divsi3.c", "divsi3.c",
@@ -75,7 +72,6 @@ var genericBuiltins = []string{
"floatunsisf.c", "floatunsisf.c",
"floatuntidf.c", "floatuntidf.c",
"floatuntisf.c", "floatuntisf.c",
"fp_mode.c",
//"int_util.c", //"int_util.c",
"lshrdi3.c", "lshrdi3.c",
"lshrti3.c", "lshrti3.c",
@@ -126,6 +122,7 @@ var genericBuiltins = []string{
"ucmpti2.c", "ucmpti2.c",
"udivdi3.c", "udivdi3.c",
"udivmoddi4.c", "udivmoddi4.c",
"udivmodsi4.c",
"udivmodti4.c", "udivmodti4.c",
"udivsi3.c", "udivsi3.c",
"udivti3.c", "udivti3.c",
@@ -152,14 +149,6 @@ var aeabiBuiltins = []string{
"arm/aeabi_memset.S", "arm/aeabi_memset.S",
"arm/aeabi_uidivmod.S", "arm/aeabi_uidivmod.S",
"arm/aeabi_uldivmod.S", "arm/aeabi_uldivmod.S",
// These two are not technically EABI builtins but are used by them and only
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
// other architectures.
// Most importantly, they have a different calling convention on AVR so
// should not be used on AVR.
"divmodsi4.c",
"udivmodsi4.c",
} }
// CompilerRT is a library with symbols required by programs compiled with LLVM. // CompilerRT is a library with symbols required by programs compiled with LLVM.
@@ -172,15 +161,7 @@ var CompilerRT = Library{
cflags: func(target, headerPath string) []string { cflags: func(target, headerPath string) []string {
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"} return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
}, },
sourceDir: func() string { sourceDir: "lib/compiler-rt/lib/builtins",
llvmDir := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project/compiler-rt/lib/builtins")
if _, err := os.Stat(llvmDir); err == nil {
// Release build.
return llvmDir
}
// Development build.
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
},
librarySources: func(target string) []string { librarySources: func(target string) []string {
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
+4 -3
View File
@@ -38,7 +38,6 @@
#include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Option/Arg.h" #include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h" #include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h" #include "llvm/Option/OptTable.h"
@@ -52,6 +51,7 @@
#include "llvm/Support/Process.h" #include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h" #include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h" #include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h" #include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h" #include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
@@ -120,6 +120,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue()) llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
.Case("none", llvm::DebugCompressionType::None) .Case("none", llvm::DebugCompressionType::None)
.Case("zlib", llvm::DebugCompressionType::Z) .Case("zlib", llvm::DebugCompressionType::Z)
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
.Default(llvm::DebugCompressionType::None); .Default(llvm::DebugCompressionType::None);
} }
@@ -381,7 +382,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI, T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
Opts.RelaxAll, Opts.IncrementalLinkerCompatible, Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ true)); /*DWARFMustBeAtTheEnd*/ true));
Str.get()->initSections(Opts.NoExecStack, *STI); Str.get()->InitSections(Opts.NoExecStack);
} }
// When -fembed-bitcode is passed to clang_as, a 1-byte marker // When -fembed-bitcode is passed to clang_as, a 1-byte marker
@@ -441,7 +442,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts,
return Failed; return Failed;
} }
static void LLVMErrorHandler(void *UserData, const char *Message, static void LLVMErrorHandler(void *UserData, const std::string &Message,
bool GenCrashDiag) { bool GenCrashDiag) {
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData); DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
+1 -1
View File
@@ -74,7 +74,7 @@ func LookupCommand(name string) (string, error) {
} }
return cmdName, nil return cmdName, nil
} }
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " ")) return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
} }
func execCommand(name string, args ...string) error { func execCommand(name string, args ...string) error {
+2 -2
View File
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err) return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
} }
if major != 1 || minor < 16 || minor > 18 { if major != 1 || minor < 15 || minor > 17 {
return nil, fmt.Errorf("requires go version 1.16 through 1.18, got go%d.%d", major, minor) return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
} }
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
+1 -1
View File
@@ -39,7 +39,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
// Link object file to dynamic library. // Link object file to dynamic library.
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx") platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
flags = []string{ flags = []string{
"-flavor", "darwin", "-flavor", "darwinnew",
"-demangle", "-demangle",
"-dynamic", "-dynamic",
"-dylib", "-dylib",
+3 -30
View File
@@ -15,7 +15,6 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"sort" "sort"
"strings"
) )
type espImageSegment struct { type espImageSegment struct {
@@ -79,31 +78,15 @@ func makeESPFirmareImage(infile, outfile, format string) error {
// An added benefit is that we don't need to check for errors all the time. // An added benefit is that we don't need to check for errors all the time.
outf := &bytes.Buffer{} outf := &bytes.Buffer{}
// Separate esp32 and esp32-img. The -img suffix indicates we should make an
// image, not just a binary to be flashed at 0x1000 for example.
chip := format
makeImage := false
if strings.HasSuffix(format, "-img") {
makeImage = true
chip = format[:len(format)-len("-img")]
}
if makeImage {
// The bootloader starts at 0x1000, or 4096.
// TinyGo doesn't use a separate bootloader and runs the entire
// application in the bootloader location.
outf.Write(make([]byte, 4096))
}
// Chip IDs. Source: // Chip IDs. Source:
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22 // https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
chip_id := map[string]uint16{ chip_id := map[string]uint16{
"esp32": 0x0000, "esp32": 0x0000,
"esp32c3": 0x0005, "esp32c3": 0x0005,
}[chip] }[format]
// Image header. // Image header.
switch chip { switch format {
case "esp32", "esp32c3": case "esp32", "esp32c3":
// Header format: // Header format:
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71 // https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
@@ -172,22 +155,12 @@ func makeESPFirmareImage(infile, outfile, format string) error {
outf.Write(make([]byte, 15-outf.Len()%16)) outf.Write(make([]byte, 15-outf.Len()%16))
outf.WriteByte(checksum) outf.WriteByte(checksum)
if chip != "esp8266" { if format != "esp8266" {
// SHA256 hash (to protect against image corruption, not for security). // SHA256 hash (to protect against image corruption, not for security).
hash := sha256.Sum256(outf.Bytes()) hash := sha256.Sum256(outf.Bytes())
outf.Write(hash[:]) outf.Write(hash[:])
} }
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
// image to be a certain size.
if makeImage {
// Use a default image size of 4MB.
grow := 4096*1024 - outf.Len()
if grow > 0 {
outf.Write(make([]byte, grow))
}
}
// Write the image to the output file. // Write the image to the output file.
return ioutil.WriteFile(outfile, outf.Bytes(), 0666) return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
} }
+5 -22
View File
@@ -24,8 +24,8 @@ 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
// The source directory. // The source directory, relative to TINYGOROOT.
sourceDir func() string sourceDir string
// The source files, relative to sourceDir. // The source files, relative to sourceDir.
librarySources func(target string) []string librarySources func(target string) []string
@@ -148,21 +148,12 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// However, ARM has not done this. // However, ARM has not done this.
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") { if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
args = append(args, "-march="+cpu) args = append(args, "-march="+cpu)
} else if strings.HasPrefix(target, "avr") {
args = append(args, "-mmcu="+cpu)
} else { } else {
args = append(args, "-mcpu="+cpu) args = append(args, "-mcpu="+cpu)
} }
} }
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables") args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables")
}
if strings.HasPrefix(target, "avr") {
// AVR defaults to C float and double both being 32-bit. This deviates
// from what most code (and certainly compiler-rt) expects. So we need
// to force the compiler to use 64-bit floating point numbers for
// double.
args = append(args, "-mdouble=64")
} }
if strings.HasPrefix(target, "riscv32-") { if strings.HasPrefix(target, "riscv32-") {
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128") args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
@@ -170,12 +161,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if strings.HasPrefix(target, "riscv64-") { if strings.HasPrefix(target, "riscv64-") {
args = append(args, "-march=rv64gc", "-mabi=lp64") args = append(args, "-march=rv64gc", "-mabi=lp64")
} }
if strings.HasPrefix(target, "xtensa") {
// Hack to work around an issue in the Xtensa port:
// https://github.com/espressif/llvm-project/issues/52
// Hopefully this will be fixed soon (LLVM 14).
args = append(args, "-D__ELF__")
}
var once sync.Once var once sync.Once
@@ -210,8 +195,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
}, },
} }
sourceDir := l.sourceDir()
// 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.
for _, path := range l.librarySources(target) { for _, path := range l.librarySources(target) {
@@ -220,7 +203,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
for strings.HasPrefix(cleanpath, "../") { for strings.HasPrefix(cleanpath, "../") {
cleanpath = cleanpath[3:] cleanpath = cleanpath[3:]
} }
srcpath := filepath.Join(sourceDir, path) srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path)
objpath := filepath.Join(dir, cleanpath+".o") objpath := filepath.Join(dir, cleanpath+".o")
os.MkdirAll(filepath.Dir(objpath), 0o777) os.MkdirAll(filepath.Dir(objpath), 0o777)
objs = append(objs, objpath) objs = append(objs, objpath)
@@ -244,7 +227,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// (It could be done in parallel with creating the ar file, but it probably // (It could be done in parallel with creating the ar file, but it probably
// won't make much of a difference in speed). // won't make much of a difference in speed).
if l.crt1Source != "" { if l.crt1Source != "" {
srcpath := filepath.Join(sourceDir, l.crt1Source) srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source)
job.dependencies = append(job.dependencies, &compileJob{ job.dependencies = append(job.dependencies, &compileJob{
description: "compile " + srcpath, description: "compile " + srcpath,
run: func(*compileJob) error { run: func(*compileJob) error {
+4 -4
View File
@@ -8,22 +8,22 @@ extern "C" {
bool tinygo_link_elf(int argc, char **argv) { bool tinygo_link_elf(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc); std::vector<const char*> args(argv, argv + argc);
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false); return lld::elf::link(args, false, llvm::outs(), llvm::errs());
} }
bool tinygo_link_macho(int argc, char **argv) { bool tinygo_link_macho(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc); std::vector<const char*> args(argv, argv + argc);
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false); return lld::macho::link(args, false, llvm::outs(), llvm::errs());
} }
bool tinygo_link_mingw(int argc, char **argv) { bool tinygo_link_mingw(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc); std::vector<const char*> args(argv, argv + argc);
return lld::mingw::link(args, llvm::outs(), llvm::errs(), false, false); return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
} }
bool tinygo_link_wasm(int argc, char **argv) { bool tinygo_link_wasm(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc); std::vector<const char*> args(argv, argv + argc);
return lld::wasm::link(args, llvm::outs(), llvm::errs(), false, false); return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
} }
} // external "C" } // external "C"
-1
View File
@@ -26,7 +26,6 @@ var MinGW = Library{
_, err = io.Copy(outf, inf) _, err = io.Copy(outf, inf)
return err return err
}, },
sourceDir: func() string { return "" }, // unused
cflags: func(target, headerPath string) []string { cflags: func(target, headerPath string) []string {
// No flags necessary because there are no files to compile. // No flags necessary because there are no files to compile.
return nil return nil
+1 -2
View File
@@ -104,7 +104,7 @@ var Musl = Library{
"-fno-stack-protector", "-fno-stack-protector",
} }
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") }, sourceDir: "lib/musl/src",
librarySources: func(target string) []string { librarySources: func(target string) []string {
arch := compileopts.MuslArchitecture(target) arch := compileopts.MuslArchitecture(target)
globs := []string{ globs := []string{
@@ -115,7 +115,6 @@ var Musl = Library{
"internal/libc.c", "internal/libc.c",
"internal/syscall_ret.c", "internal/syscall_ret.c",
"internal/vdso.c", "internal/vdso.c",
"legacy/*.c",
"malloc/*.c", "malloc/*.c",
"mman/*.c", "mman/*.c",
"signal/*.c", "signal/*.c",
+1 -1
View File
@@ -33,7 +33,7 @@ var Picolibc = Library{
"-I" + headerPath, "-I" + headerPath,
} }
}, },
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc") }, sourceDir: "lib/picolibc/newlib/libc",
librarySources: func(target string) []string { librarySources: func(target string) []string {
return picolibcSources return picolibcSources
}, },
+1 -54
View File
@@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"debug/dwarf" "debug/dwarf"
"debug/elf" "debug/elf"
"debug/macho"
"debug/pe" "debug/pe"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
@@ -117,7 +116,7 @@ var (
// alloc: heap allocations during init interpretation // alloc: heap allocations during init interpretation
// pack: data created when storing a constant in an interface for example // pack: data created when storing a constant in an interface for example
// string: buffer behind strings // string: buffer behind strings
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`) packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
// Reflect sidetables. Created by the reflect lowering pass. // Reflect sidetables. Created by the reflect lowering pass.
// See src/reflect/sidetables.go. // See src/reflect/sidetables.go.
@@ -369,58 +368,6 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
}) })
} }
} }
} else if file, err := macho.NewFile(f); err == nil {
// TODO: read DWARF information. On MacOS, DWARF debug information isn't
// stored in the executable but stays in the object files. The
// executable does however contain the object file paths that contain
// debug information.
// Read segments, for use while reading through sections.
segments := map[string]*macho.Segment{}
for _, load := range file.Loads {
switch load := load.(type) {
case *macho.Segment:
segments[load.Name] = load
}
}
// Read MachO sections.
for _, section := range file.Sections {
sectionType := section.Flags & 0xff
sectionFlags := section.Flags >> 8
segment := segments[section.Seg]
// For the constants used here, see:
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
// Section containing only instructions.
sections = append(sections, memorySection{
Address: section.Addr,
Size: uint64(section.Size),
Type: memoryCode,
})
} else if sectionType == 1 { // S_ZEROFILL
// Section filled with zeroes on demand.
sections = append(sections, memorySection{
Address: section.Addr,
Size: uint64(section.Size),
Type: memoryBSS,
})
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
// Protection doesn't allow writes, so mark this section read-only.
sections = append(sections, memorySection{
Address: section.Addr,
Size: uint64(section.Size),
Type: memoryROData,
})
} else {
// The rest is assumed to be regular data.
sections = append(sections, memorySection{
Address: section.Addr,
Size: uint64(section.Size),
Type: memoryData,
})
}
}
} else if file, err := pe.NewFile(f); err == nil { } else if file, err := pe.NewFile(f); err == nil {
// Read DWARF information. The error is intentionally ignored. // Read DWARF information. The error is intentionally ignored.
data, _ := file.DWARF() data, _ := file.DWARF()
+1 -1
View File
@@ -54,7 +54,7 @@ func RunTool(tool string, args ...string) error {
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf)) ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
case "ld.lld": case "ld.lld":
switch linker { switch linker {
case "darwin": case "darwinnew":
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf)) ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
case "elf": case "elf":
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf)) ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
+533 -367
View File
File diff suppressed because it is too large Load Diff
+199 -383
View File
@@ -72,11 +72,7 @@ var diagnosticSeverity = [...]string{
C.CXDiagnostic_Fatal: "fatal", C.CXDiagnostic_Fatal: "fatal",
} }
// Alias so that cgo.go (which doesn't import Clang related stuff and is in func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
// theory decoupled from Clang) can also use this type.
type clangCursor = C.GoCXCursor
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
index := C.clang_createIndex(0, 0) index := C.clang_createIndex(0, 0)
defer C.clang_disposeIndex(index) defer C.clang_disposeIndex(index)
@@ -123,8 +119,8 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic)) spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)] severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
location := C.clang_getDiagnosticLocation(diagnostic) location := C.clang_getDiagnosticLocation(diagnostic)
pos := f.getClangLocationPosition(location, unit) pos := p.getClangLocationPosition(location, unit)
f.addError(pos, severity+": "+spelling) p.addError(pos, severity+": "+spelling)
} }
for i := 0; i < numDiagnostics; i++ { for i := 0; i < numDiagnostics; i++ {
diagnostic := C.clang_getDiagnostic(unit, C.uint(i)) diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
@@ -139,7 +135,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
} }
// Extract information required by CGo. // Extract information required by CGo.
ref := storedRefs.Put(f) ref := storedRefs.Put(p)
defer storedRefs.Remove(ref) defer storedRefs.Remove(ref)
cursor := C.tinygo_clang_getTranslationUnitCursor(unit) cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref)) C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
@@ -159,64 +155,35 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size] data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
// Hash the contents if it isn't hashed yet. // Hash the contents if it isn't hashed yet.
if _, ok := f.visitedFiles[path]; !ok { if _, ok := p.visitedFiles[path]; !ok {
// already stored // already stored
sum := sha512.Sum512_224(data) sum := sha512.Sum512_224(data)
f.visitedFiles[path] = sum[:] p.visitedFiles[path] = sum[:]
} }
} }
inclusionCallbackRef := storedRefs.Put(inclusionCallback) inclusionCallbackRef := storedRefs.Put(inclusionCallback)
defer storedRefs.Remove(inclusionCallbackRef) defer storedRefs.Remove(inclusionCallbackRef)
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef)) C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
// Do all the C AST operations inside a callback. This makes sure that
// libclang related memory is only freed after it is not necessary anymore.
callback(f.names)
} }
// Convert the AST node under the given Clang cursor to a Go AST node and return //export tinygo_clang_globals_visitor
// it. func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) { p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
kind := C.tinygo_clang_getCursorKind(c) kind := C.tinygo_clang_getCursorKind(c)
pos := f.getCursorPosition(c) pos := p.getCursorPosition(c)
switch kind { switch kind {
case C.CXCursor_FunctionDecl: case C.CXCursor_FunctionDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if _, required := p.missingSymbols[name]; !required {
return C.CXChildVisit_Continue
}
cursorType := C.tinygo_clang_getCursorType(c) cursorType := C.tinygo_clang_getCursorType(c)
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c)) numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
obj := &ast.Object{ fn := &functionInfo{
Kind: ast.Fun, pos: pos,
Name: "C." + name, variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
}
args := make([]*ast.Field, numArgs)
decl := &ast.FuncDecl{
Doc: &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: pos - 1,
Text: "//export " + name,
},
},
},
Name: &ast.Ident{
NamePos: pos,
Name: "C." + name,
Obj: obj,
},
Type: &ast.FuncType{
Func: pos,
Params: &ast.FieldList{
Opening: pos,
List: args,
Closing: pos,
},
},
}
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
Slash: pos - 1,
Text: "//go:variadic",
})
} }
p.functions[name] = fn
for i := 0; i < numArgs; i++ { for i := 0; i < numArgs; i++ {
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i)) arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
argName := getString(C.tinygo_clang_getCursorSpelling(arg)) argName := getString(C.tinygo_clang_getCursorSpelling(arg))
@@ -224,108 +191,50 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
if argName == "" { if argName == "" {
argName = "$" + strconv.Itoa(i) argName = "$" + strconv.Itoa(i)
} }
args[i] = &ast.Field{ fn.args = append(fn.args, paramInfo{
Names: []*ast.Ident{ name: argName,
{ typeExpr: p.makeDecayingASTType(argType, pos),
NamePos: pos, })
Name: argName,
Obj: &ast.Object{
Kind: ast.Var,
Name: argName,
Decl: decl,
},
},
},
Type: f.makeDecayingASTType(argType, pos),
}
} }
resultType := C.tinygo_clang_getCursorResultType(c) resultType := C.tinygo_clang_getCursorResultType(c)
if resultType.kind != C.CXType_Void { if resultType.kind != C.CXType_Void {
decl.Type.Results = &ast.FieldList{ fn.results = &ast.FieldList{
List: []*ast.Field{ List: []*ast.Field{
{ {
Type: f.makeASTType(resultType, pos), Type: p.makeASTType(resultType, pos),
}, },
}, },
} }
} }
obj.Decl = decl case C.CXCursor_StructDecl:
return decl, nil typ := C.tinygo_clang_getCursorType(c)
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl: name := getString(C.tinygo_clang_getCursorSpelling(c))
typ := f.makeASTRecordType(c, pos) if _, required := p.missingSymbols["struct_"+name]; !required {
typeName := "C." + name return C.CXChildVisit_Continue
typeExpr := typ.typeExpr
if typ.unionSize != 0 {
// Convert to a single-field struct type.
typeExpr = f.makeUnionField(typ)
} }
obj := &ast.Object{ p.makeASTType(typ, pos)
Kind: ast.Typ,
Name: typeName,
}
typeSpec := &ast.TypeSpec{
Name: &ast.Ident{
NamePos: typ.pos,
Name: typeName,
Obj: obj,
},
Type: typeExpr,
}
obj.Decl = typeSpec
return typeSpec, typ
case C.CXCursor_TypedefDecl: case C.CXCursor_TypedefDecl:
typeName := "C." + name typedefType := C.tinygo_clang_getCursorType(c)
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c) name := getString(C.clang_getTypedefName(typedefType))
obj := &ast.Object{ if _, required := p.missingSymbols[name]; !required {
Kind: ast.Typ, return C.CXChildVisit_Continue
Name: typeName,
} }
typeSpec := &ast.TypeSpec{ p.makeASTType(typedefType, pos)
Name: &ast.Ident{
NamePos: pos,
Name: typeName,
Obj: obj,
},
Type: f.makeASTType(underlyingType, pos),
}
if underlyingType.kind != C.CXType_Enum {
typeSpec.Assign = pos
}
obj.Decl = typeSpec
return typeSpec, nil
case C.CXCursor_VarDecl: case C.CXCursor_VarDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if _, required := p.missingSymbols[name]; !required {
return C.CXChildVisit_Continue
}
cursorType := C.tinygo_clang_getCursorType(c) cursorType := C.tinygo_clang_getCursorType(c)
typeExpr := f.makeASTType(cursorType, pos) p.globals[name] = globalInfo{
gen := &ast.GenDecl{ typeExpr: p.makeASTType(cursorType, pos),
TokPos: pos, pos: pos,
Tok: token.VAR,
Lparen: token.NoPos,
Rparen: token.NoPos,
Doc: &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: pos - 1,
Text: "//go:extern " + name,
},
},
},
} }
obj := &ast.Object{
Kind: ast.Var,
Name: "C." + name,
}
valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{
NamePos: pos,
Name: "C." + name,
Obj: obj,
}},
Type: typeExpr,
}
obj.Decl = valueSpec
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
case C.CXCursor_MacroDefinition: case C.CXCursor_MacroDefinition:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if _, required := p.missingSymbols[name]; !required {
return C.CXChildVisit_Continue
}
sourceRange := C.tinygo_clang_getCursorExtent(c) sourceRange := C.tinygo_clang_getCursorExtent(c)
start := C.clang_getRangeStart(sourceRange) start := C.clang_getRangeStart(sourceRange)
end := C.clang_getRangeEnd(sourceRange) end := C.clang_getRangeEnd(sourceRange)
@@ -333,17 +242,17 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
var startOffset, endOffset C.unsigned var startOffset, endOffset C.unsigned
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset) C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
if file == nil { if file == nil {
f.addError(pos, "internal error: could not find file where macro is defined") p.addError(pos, "internal error: could not find file where macro is defined")
return nil, nil break
} }
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset) C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
if file != endFile { if file != endFile {
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file") p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
return nil, nil break
} }
if startOffset > endOffset { if startOffset > endOffset {
f.addError(pos, "internal error: start offset of macro is after end offset") p.addError(pos, "internal error: start offset of macro is after end offset")
return nil, nil break
} }
// read file contents and extract the relevant byte range // read file contents and extract the relevant byte range
@@ -351,94 +260,31 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
var size C.size_t var size C.size_t
sourcePtr := C.clang_getFileContents(tu, file, &size) sourcePtr := C.clang_getFileContents(tu, file, &size)
if endOffset >= C.uint(size) { if endOffset >= C.uint(size) {
f.addError(pos, "internal error: end offset of macro lies after end of file") p.addError(pos, "internal error: end offset of macro lies after end of file")
return nil, nil break
} }
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset]) source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
if !strings.HasPrefix(source, name) { if !strings.HasPrefix(source, name) {
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source)) p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
return nil, nil break
} }
value := source[len(name):] value := source[len(name):]
// Try to convert this #define into a Go constant expression. // Try to convert this #define into a Go constant expression.
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value) expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
if scannerError != nil { if scannerError != nil {
f.errors = append(f.errors, *scannerError) p.errors = append(p.errors, *scannerError)
return nil, nil
} }
if expr != nil {
gen := &ast.GenDecl{ // Parsing was successful.
TokPos: token.NoPos, p.constants[name] = constantInfo{expr, pos}
Tok: token.CONST,
Lparen: token.NoPos,
Rparen: token.NoPos,
} }
obj := &ast.Object{
Kind: ast.Con,
Name: "C." + name,
}
valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{
NamePos: pos,
Name: "C." + name,
Obj: obj,
}},
Values: []ast.Expr{expr},
}
obj.Decl = valueSpec
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
case C.CXCursor_EnumDecl: case C.CXCursor_EnumDecl:
obj := &ast.Object{ // Visit all enums, because the fields may be used even when the enum
Kind: ast.Typ, // type itself is not.
Name: "C." + name, typ := C.tinygo_clang_getCursorType(c)
} p.makeASTType(typ, pos)
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
// TODO: gc's CGo implementation uses types such as `uint32` for enums
// instead of types such as C.int, which are used here.
typeSpec := &ast.TypeSpec{
Name: &ast.Ident{
NamePos: pos,
Name: "C." + name,
Obj: obj,
},
Assign: pos,
Type: f.makeASTType(underlying, pos),
}
obj.Decl = typeSpec
return typeSpec, nil
case C.CXCursor_EnumConstantDecl:
value := C.tinygo_clang_getEnumConstantDeclValue(c)
expr := &ast.BasicLit{
ValuePos: pos,
Kind: token.INT,
Value: strconv.FormatInt(int64(value), 10),
}
gen := &ast.GenDecl{
TokPos: token.NoPos,
Tok: token.CONST,
Lparen: token.NoPos,
Rparen: token.NoPos,
}
obj := &ast.Object{
Kind: ast.Con,
Name: "C." + name,
}
valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{
NamePos: pos,
Name: "C." + name,
Obj: obj,
}},
Values: []ast.Expr{expr},
}
obj.Decl = valueSpec
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
default:
f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind))
return nil, nil
} }
return C.CXChildVisit_Continue
} }
func getString(clangString C.CXString) (s string) { func getString(clangString C.CXString) (s string) {
@@ -448,49 +294,6 @@ func getString(clangString C.CXString) (s string) {
return return
} }
//export tinygo_clang_globals_visitor
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile)
switch C.tinygo_clang_getCursorKind(c) {
case C.CXCursor_FunctionDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
f.names[name] = c
case C.CXCursor_StructDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if name != "" {
f.names["struct_"+name] = c
}
case C.CXCursor_UnionDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if name != "" {
f.names["union_"+name] = c
}
case C.CXCursor_TypedefDecl:
typedefType := C.tinygo_clang_getCursorType(c)
name := getString(C.clang_getTypedefName(typedefType))
f.names[name] = c
case C.CXCursor_VarDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
f.names[name] = c
case C.CXCursor_MacroDefinition:
name := getString(C.tinygo_clang_getCursorSpelling(c))
f.names[name] = c
case C.CXCursor_EnumDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if name != "" {
// Named enum, which can be referenced from Go using C.enum_foo.
f.names["enum_"+name] = c
}
// The enum fields are in global scope, so recurse to visit them.
return C.CXChildVisit_Recurse
case C.CXCursor_EnumConstantDecl:
// We arrive here because of the "Recurse" above.
name := getString(C.tinygo_clang_getCursorSpelling(c))
f.names[name] = c
}
return C.CXChildVisit_Continue
}
// getCursorPosition returns a usable token.Pos from a libclang cursor. // getCursorPosition returns a usable token.Pos from a libclang cursor.
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos { func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor)) return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
@@ -588,7 +391,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
// makeDecayingASTType does the same as makeASTType but takes care of decaying // makeDecayingASTType does the same as makeASTType but takes care of decaying
// types (arrays in function parameters, etc). It is otherwise identical to // types (arrays in function parameters, etc). It is otherwise identical to
// makeASTType. // makeASTType.
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr { func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
// Strip typedefs, if any. // Strip typedefs, if any.
underlyingType := typ underlyingType := typ
if underlyingType.kind == C.CXType_Typedef { if underlyingType.kind == C.CXType_Typedef {
@@ -614,15 +417,15 @@ func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
pointeeType := C.clang_getElementType(underlyingType) pointeeType := C.clang_getElementType(underlyingType)
return &ast.StarExpr{ return &ast.StarExpr{
Star: pos, Star: pos,
X: f.makeASTType(pointeeType, pos), X: p.makeASTType(pointeeType, pos),
} }
} }
return f.makeASTType(typ, pos) return p.makeASTType(typ, pos)
} }
// makeASTType return the ast.Expr for the given libclang type. In other words, // makeASTType return the ast.Expr for the given libclang type. In other words,
// it converts a libclang type to a type in the Go AST. // it converts a libclang type to a type in the Go AST.
func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr { func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
var typeName string var typeName string
switch typ.kind { switch typ.kind {
case C.CXType_Char_S, C.CXType_Char_U: case C.CXType_Char_S, C.CXType_Char_U:
@@ -683,7 +486,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
} }
return &ast.StarExpr{ return &ast.StarExpr{
Star: pos, Star: pos,
X: f.makeASTType(pointeeType, pos), X: p.makeASTType(pointeeType, pos),
} }
case C.CXType_ConstantArray: case C.CXType_ConstantArray:
return &ast.ArrayType{ return &ast.ArrayType{
@@ -693,7 +496,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
Kind: token.INT, Kind: token.INT,
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10), Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
}, },
Elt: f.makeASTType(C.clang_getElementType(typ), pos), Elt: p.makeASTType(C.clang_getElementType(typ), pos),
} }
case C.CXType_FunctionProto: case C.CXType_FunctionProto:
// Be compatible with gc, which uses the *[0]byte type for function // Be compatible with gc, which uses the *[0]byte type for function
@@ -714,21 +517,71 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
} }
case C.CXType_Typedef: case C.CXType_Typedef:
name := getString(C.clang_getTypedefName(typ)) name := getString(C.clang_getTypedefName(typ))
c := C.tinygo_clang_getTypeDeclaration(typ) if _, ok := p.typedefs[name]; !ok {
p.typedefs[name] = nil // don't recurse
c := C.tinygo_clang_getTypeDeclaration(typ)
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
expr := p.makeASTType(underlyingType, pos)
if strings.HasPrefix(name, "_Cgo_") {
expr := expr.(*ast.Ident)
typeSize := C.clang_Type_getSizeOf(underlyingType)
switch expr.Name {
case "C.char":
if typeSize != 1 {
// This happens for some very special purpose architectures
// (DSPs etc.) that are not currently targeted.
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
}
switch underlyingType.kind {
case C.CXType_Char_S:
expr.Name = "int8"
case C.CXType_Char_U:
expr.Name = "uint8"
}
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
switch typeSize {
case 1:
expr.Name = "int8"
case 2:
expr.Name = "int16"
case 4:
expr.Name = "int32"
case 8:
expr.Name = "int64"
}
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
switch typeSize {
case 1:
expr.Name = "uint8"
case 2:
expr.Name = "uint16"
case 4:
expr.Name = "uint32"
case 8:
expr.Name = "uint64"
}
}
}
p.typedefs[name] = &typedefInfo{
typeExpr: expr,
pos: pos,
}
}
return &ast.Ident{ return &ast.Ident{
NamePos: pos, NamePos: pos,
Name: f.getASTDeclName(name, c, false), Name: "C." + name,
} }
case C.CXType_Elaborated: case C.CXType_Elaborated:
underlying := C.clang_Type_getNamedType(typ) underlying := C.clang_Type_getNamedType(typ)
switch underlying.kind { switch underlying.kind {
case C.CXType_Record: case C.CXType_Record:
return f.makeASTType(underlying, pos) return p.makeASTType(underlying, pos)
case C.CXType_Enum: case C.CXType_Enum:
return f.makeASTType(underlying, pos) return p.makeASTType(underlying, pos)
default: default:
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind)) typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling)) p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
typeName = "<unknown>" typeName = "<unknown>"
} }
case C.CXType_Record: case C.CXType_Record:
@@ -746,46 +599,63 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
} }
if name == "" { if name == "" {
// Anonymous record, probably inside a typedef. // Anonymous record, probably inside a typedef.
clangLocation := C.tinygo_clang_getCursorLocation(cursor) typeInfo := p.makeASTRecordType(cursor, pos)
var file C.CXFile if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
var line C.unsigned // This record is a union or is a struct with bitfields, so we
var column C.unsigned // have to declare it as a named type (for getters/setters to
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil) // work).
location := token.Position{ p.anonStructNum++
Filename: getString(C.clang_getFileName(file)), cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
Line: int(line), p.elaboratedTypes[cgoName] = typeInfo
Column: int(column), return &ast.Ident{
NamePos: pos,
Name: "C." + cgoName,
}
} }
if location.Filename == "" || location.Line == 0 { return typeInfo.typeExpr
// Not sure when this would happen, but protect from it anyway.
f.addError(pos, "could not find file/line information")
}
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
} else { } else {
name = cgoRecordPrefix + name cgoName := cgoRecordPrefix + name
} if _, ok := p.elaboratedTypes[cgoName]; !ok {
return &ast.Ident{ p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
NamePos: pos, p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
Name: f.getASTDeclName(name, cursor, false), }
return &ast.Ident{
NamePos: pos,
Name: "C." + cgoName,
}
} }
case C.CXType_Enum: case C.CXType_Enum:
cursor := C.tinygo_clang_getTypeDeclaration(typ) cursor := C.tinygo_clang_getTypeDeclaration(typ)
name := getString(C.tinygo_clang_getCursorSpelling(cursor)) name := getString(C.tinygo_clang_getCursorSpelling(cursor))
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
if name == "" { if name == "" {
name = f.getUnnamedDeclName("_Ctype_enum___", cursor) // anonymous enum
ref := storedRefs.Put(p)
defer storedRefs.Remove(ref)
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
return p.makeASTType(underlying, pos)
} else { } else {
name = "enum_" + name // named enum
} if _, ok := p.enums[name]; !ok {
return &ast.Ident{ ref := storedRefs.Put(p)
NamePos: pos, defer storedRefs.Remove(ref)
Name: f.getASTDeclName(name, cursor, false), C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
p.enums[name] = enumInfo{
typeExpr: p.makeASTType(underlying, pos),
pos: pos,
}
}
return &ast.Ident{
NamePos: pos,
Name: "C.enum_" + name,
}
} }
} }
if typeName == "" { if typeName == "" {
// Report this as an error. // Report this as an error.
typeSpelling := getString(C.clang_getTypeSpelling(typ)) typeSpelling := getString(C.clang_getTypeSpelling(typ))
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind)) typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling)) p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
typeName = "C.<unknown>" typeName = "C.<unknown>"
} }
return &ast.Ident{ return &ast.Ident{
@@ -794,80 +664,9 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
} }
} }
// getIntegerType returns an AST node that defines types such as C.int.
func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec {
pos := p.getCursorPosition(cursor)
// Find a Go type that matches the size and signedness of the given C type.
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor)
var goName string
typeSize := C.clang_Type_getSizeOf(underlyingType)
switch name {
case "C.char":
if typeSize != 1 {
// This happens for some very special purpose architectures
// (DSPs etc.) that are not currently targeted.
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
}
switch underlyingType.kind {
case C.CXType_Char_S:
goName = "int8"
case C.CXType_Char_U:
goName = "uint8"
}
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
switch typeSize {
case 1:
goName = "int8"
case 2:
goName = "int16"
case 4:
goName = "int32"
case 8:
goName = "int64"
}
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
switch typeSize {
case 1:
goName = "uint8"
case 2:
goName = "uint16"
case 4:
goName = "uint32"
case 8:
goName = "uint64"
}
}
if goName == "" { // should not happen
p.addError(pos, "internal error: did not find Go type for C type "+name)
goName = "int"
}
// Construct an *ast.TypeSpec for this type.
obj := &ast.Object{
Kind: ast.Typ,
Name: name,
}
spec := &ast.TypeSpec{
Name: &ast.Ident{
NamePos: pos,
Name: name,
Obj: obj,
},
Type: &ast.Ident{
NamePos: pos,
Name: goName,
},
}
obj.Decl = spec
return spec
}
// makeASTRecordType parses a C record (struct or union) and translates it into // makeASTRecordType parses a C record (struct or union) and translates it into
// a Go struct type. // a Go struct type.
func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo { func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
fieldList := &ast.FieldList{ fieldList := &ast.FieldList{
Opening: pos, Opening: pos,
Closing: pos, Closing: pos,
@@ -877,11 +676,11 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
bitfieldNum := 0 bitfieldNum := 0
ref := storedRefs.Put(struct { ref := storedRefs.Put(struct {
fieldList *ast.FieldList fieldList *ast.FieldList
file *cgoFile pkg *cgoPackage
inBitfield *bool inBitfield *bool
bitfieldNum *int bitfieldNum *int
bitfieldList *[]bitfieldInfo bitfieldList *[]bitfieldInfo
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList}) }{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
defer storedRefs.Remove(ref) defer storedRefs.Remove(ref)
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref)) C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
renameFieldKeywords(fieldList) renameFieldKeywords(fieldList)
@@ -910,13 +709,13 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
} }
if bitfieldList != nil { if bitfieldList != nil {
// This is valid C... but please don't do this. // This is valid C... but please don't do this.
f.addError(pos, "bitfield in a union is not supported") p.addError(pos, "bitfield in a union is not supported")
} }
typ := C.tinygo_clang_getCursorType(cursor) typ := C.tinygo_clang_getCursorType(cursor)
alignInBytes := int64(C.clang_Type_getAlignOf(typ)) alignInBytes := int64(C.clang_Type_getAlignOf(typ))
sizeInBytes := int64(C.clang_Type_getSizeOf(typ)) sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
if sizeInBytes == 0 { if sizeInBytes == 0 {
f.addError(pos, "zero-length union is not supported") p.addError(pos, "zero-length union is not supported")
} }
typeInfo.unionSize = sizeInBytes typeInfo.unionSize = sizeInBytes
typeInfo.unionAlign = alignInBytes typeInfo.unionAlign = alignInBytes
@@ -924,7 +723,7 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
default: default:
cursorKind := C.tinygo_clang_getCursorKind(cursor) cursorKind := C.tinygo_clang_getCursorKind(cursor)
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind)) cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling)) p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
return &elaboratedTypeInfo{ return &elaboratedTypeInfo{
typeExpr: &ast.StructType{ typeExpr: &ast.StructType{
Struct: pos, Struct: pos,
@@ -938,17 +737,17 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int { func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct { passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
fieldList *ast.FieldList fieldList *ast.FieldList
file *cgoFile pkg *cgoPackage
inBitfield *bool inBitfield *bool
bitfieldNum *int bitfieldNum *int
bitfieldList *[]bitfieldInfo bitfieldList *[]bitfieldInfo
}) })
fieldList := passed.fieldList fieldList := passed.fieldList
f := passed.file p := passed.pkg
inBitfield := passed.inBitfield inBitfield := passed.inBitfield
bitfieldNum := passed.bitfieldNum bitfieldNum := passed.bitfieldNum
bitfieldList := passed.bitfieldList bitfieldList := passed.bitfieldList
pos := f.getCursorPosition(c) pos := p.getCursorPosition(c)
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind { switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
case C.CXCursor_FieldDecl: case C.CXCursor_FieldDecl:
// Expected. This is a regular field. // Expected. This is a regular field.
@@ -957,7 +756,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
return C.CXChildVisit_Continue return C.CXChildVisit_Continue
default: default:
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind)) cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling)) p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
return C.CXChildVisit_Continue return C.CXChildVisit_Continue
} }
name := getString(C.tinygo_clang_getCursorSpelling(c)) name := getString(C.tinygo_clang_getCursorSpelling(c))
@@ -968,14 +767,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
} }
typ := C.tinygo_clang_getCursorType(c) typ := C.tinygo_clang_getCursorType(c)
field := &ast.Field{ field := &ast.Field{
Type: f.makeASTType(typ, f.getCursorPosition(c)), Type: p.makeASTType(typ, p.getCursorPosition(c)),
} }
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name))) offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8) alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
bitfieldOffset := offsetof % alignOf bitfieldOffset := offsetof % alignOf
if bitfieldOffset != 0 { if bitfieldOffset != 0 {
if C.tinygo_clang_Cursor_isBitField(c) != 1 { if C.tinygo_clang_Cursor_isBitField(c) != 1 {
f.addError(pos, "expected a bitfield") p.addError(pos, "expected a bitfield")
return C.CXChildVisit_Continue return C.CXChildVisit_Continue
} }
if !*inBitfield { if !*inBitfield {
@@ -1022,6 +821,23 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
return C.CXChildVisit_Continue return C.CXChildVisit_Continue
} }
//export tinygo_clang_enum_visitor
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
name := getString(C.tinygo_clang_getCursorSpelling(c))
pos := p.getCursorPosition(c)
value := C.tinygo_clang_getEnumConstantDeclValue(c)
p.constants[name] = constantInfo{
expr: &ast.BasicLit{
ValuePos: pos,
Kind: token.INT,
Value: strconv.FormatInt(int64(value), 10),
},
pos: pos,
}
return C.CXChildVisit_Continue
}
//export tinygo_clang_inclusion_visitor //export tinygo_clang_inclusion_visitor
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) { func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile)) callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
+16
View File
@@ -0,0 +1,16 @@
//go:build !byollvm && llvm11
// +build !byollvm,llvm11
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@11/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@11/include
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@11/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
*/
import "C"
+16
View File
@@ -0,0 +1,16 @@
//go:build !byollvm && llvm12
// +build !byollvm,llvm12
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-12/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@12/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@12/include
#cgo freebsd CFLAGS: -I/usr/local/llvm12/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@12/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang
*/
import "C"
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build !byollvm && !llvm14 //go:build !byollvm && !llvm11 && !llvm12
// +build !byollvm,!llvm14 // +build !byollvm,!llvm11,!llvm12
package cgo package cgo
-16
View File
@@ -1,16 +0,0 @@
//go:build !byollvm && llvm14
// +build !byollvm,llvm14
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-14/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include
#cgo freebsd CFLAGS: -I/usr/local/llvm14/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang
*/
import "C"
+20 -13
View File
@@ -24,16 +24,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type ( type C.int16_t = int16
C.char uint8 type C.int32_t = int32
C.schar int8 type C.int64_t = int64
C.uchar uint8 type C.int8_t = int8
C.short int16 type C.uint16_t = uint16
C.ushort uint16 type C.uint32_t = uint32
C.int int32 type C.uint64_t = uint64
C.uint uint32 type C.uint8_t = uint8
C.long int32 type C.uintptr_t = uintptr
C.ulong uint32 type C.char uint8
C.longlong int64 type C.int int32
C.ulonglong uint64 type C.long int32
) type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
+22 -15
View File
@@ -24,19 +24,26 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
const C.foo = 3
const C.bar = C.foo const C.bar = C.foo
const C.foo = 3
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
+1 -1
View File
@@ -27,7 +27,7 @@ import "C"
//line errors.go:100 //line errors.go:100
var ( var (
// constant too large // constant too large
_ C.char = 2 << 10 _ C.uint8_t = 2 << 10
// z member does not exist // z member does not exist
_ C.point_t = C.point_t{z: 3} _ C.point_t = C.point_t{z: 3}
+24 -18
View File
@@ -6,7 +6,7 @@
// testdata/errors.go:19:26: unexpected token ), expected end of expression // testdata/errors.go:19:26: unexpected token ), expected end of expression
// Type checking errors after CGo processing: // Type checking errors after CGo processing:
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows) // testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
// testdata/errors.go:105: unknown field z in struct literal // testdata/errors.go:105: unknown field z in struct literal
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1 // testdata/errors.go:108: undeclared name: C.SOME_CONST_1
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows) // testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
@@ -38,23 +38,29 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type ( const C.SOME_CONST_3 = 1234
C.char uint8
C.schar int8 type C.int16_t = int16
C.uchar uint8 type C.int32_t = int32
C.short int16 type C.int64_t = int64
C.ushort uint16 type C.int8_t = int8
C.int int32 type C.uint16_t = uint16
C.uint uint32 type C.uint32_t = uint32
C.long int32 type C.uint64_t = uint64
C.ulong uint32 type C.uint8_t = uint8
C.longlong int64 type C.uintptr_t = uintptr
C.ulonglong uint64 type C.char uint8
) type C.int int32
type C._Ctype_struct___0 struct { type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
type C.point_t = struct {
x C.int x C.int
y C.int y C.int
} }
type C.point_t = C._Ctype_struct___0
const C.SOME_CONST_3 = 1234
+21 -14
View File
@@ -29,19 +29,26 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
const C.BAR = 3 const C.BAR = 3
const C.FOO_H = 1 const C.FOO_H = 1
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
+23 -18
View File
@@ -24,36 +24,41 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
//export foo //export foo
func C.foo(a C.int, b C.int) C.int func C.foo(a C.int, b C.int) C.int
var C.foo$funcaddr unsafe.Pointer
//export variadic0 //export variadic0
//go:variadic //go:variadic
func C.variadic0() func C.variadic0()
var C.variadic0$funcaddr unsafe.Pointer
//export variadic2 //export variadic2
//go:variadic //go:variadic
func C.variadic2(x C.int, y C.int) func C.variadic2(x C.int, y C.int)
var C.foo$funcaddr unsafe.Pointer
var C.variadic0$funcaddr unsafe.Pointer
var C.variadic2$funcaddr unsafe.Pointer var C.variadic2$funcaddr unsafe.Pointer
//go:extern someValue //go:extern someValue
var C.someValue C.int var C.someValue C.int
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
+106 -100
View File
@@ -24,126 +24,132 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length)) return C.__GoBytes(ptr, uintptr(length))
} }
type ( const C.option2A = 20
C.char uint8 const C.optionA = 0
C.schar int8 const C.optionB = 1
C.uchar uint8 const C.optionC = -5
C.short int16 const C.optionD = -4
C.ushort uint16 const C.optionE = 10
C.int int32 const C.optionF = 11
C.uint uint32 const C.optionG = 12
C.long int32 const C.unused1 = 5
C.ulong uint32
C.longlong int64 type C.int16_t = int16
C.ulonglong uint64 type C.int32_t = int32
) type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
type C.bitfield_t = C.struct_4
type C.myIntArray = [10]C.int
type C.myint = C.int type C.myint = C.int
type C._Ctype_struct___0 struct { type C.option2_t = C.uint
type C.option_t = C.enum_option
type C.point2d_t = struct {
x C.int x C.int
y C.int y C.int
} }
type C.point2d_t = C._Ctype_struct___0
type C.struct_point3d struct {
x C.int
y C.int
z C.int
}
type C.point3d_t = C.struct_point3d type C.point3d_t = C.struct_point3d
type C.struct_type1 struct { type C.struct_nested_t = struct {
_type C.int
__type C.int
___type C.int
}
type C.struct_type2 struct{ _type C.int }
type C._Ctype_union___1 struct{ i C.int }
type C.union1_t = C._Ctype_union___1
type C._Ctype_union___2 struct{ $union uint64 }
func (union *C._Ctype_union___2) unionfield_i() *C.int {
return (*C.int)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___2) unionfield_d() *float64 {
return (*float64)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___2) unionfield_s() *C.short {
return (*C.short)(unsafe.Pointer(&union.$union))
}
type C.union3_t = C._Ctype_union___2
type C.union_union2d struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_union2d) unionfield_d() *[2]float64 {
return (*[2]float64)(unsafe.Pointer(&union.$union))
}
type C.union2d_t = C.union_union2d
type C._Ctype_union___3 struct{ arr [10]C.uchar }
type C.unionarray_t = C._Ctype_union___3
type C._Ctype_union___5 struct{ $union [3]uint32 }
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
type C._Ctype_struct___4 struct {
begin C.point2d_t begin C.point2d_t
end C.point2d_t end C.point2d_t
tag C.int tag C.int
coord C._Ctype_union___5 coord C.union_2
} }
type C.struct_nested_t = C._Ctype_struct___4 type C.types_t = struct {
type C._Ctype_union___6 struct{ $union [2]uint64 }
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
return (*C.union3_t)(unsafe.Pointer(&union.$union))
}
type C.union_nested_t = C._Ctype_union___6
type C.enum_option = C.int
type C.option_t = C.enum_option
type C._Ctype_enum___7 = C.uint
type C.option2_t = C._Ctype_enum___7
type C._Ctype_struct___8 struct {
f float32 f float32
d float64 d float64
ptr *C.int ptr *C.int
} }
type C.types_t = C._Ctype_struct___8 type C.union1_t = struct{ i C.int }
type C.myIntArray = [10]C.int type C.union2d_t = C.union_union2d
type C._Ctype_struct___9 struct { type C.union3_t = C.union_1
type C.union_nested_t = C.union_3
type C.unionarray_t = struct{ arr [10]C.uchar }
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
}
func (s *C.struct_4) bitfield_b() C.uchar {
return s.__bitfield_1 >> 5 & 0x1
}
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
}
func (s *C.struct_4) bitfield_c() C.uchar {
return s.__bitfield_1 >> 6
}
func (s *C.struct_4) set_bitfield_c(value C.uchar,
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
type C.struct_4 struct {
start C.uchar start C.uchar
__bitfield_1 C.uchar __bitfield_1 C.uchar
d C.uchar d C.uchar
e C.uchar e C.uchar
} }
type C.struct_point3d struct {
x C.int
y C.int
z C.int
}
type C.struct_type1 struct {
_type C.int
__type C.int
___type C.int
}
type C.struct_type2 struct{ _type C.int }
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f } func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) { func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
}
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
return s.__bitfield_1 >> 5 & 0x1
}
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
}
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
return s.__bitfield_1 >> 6
}
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 } type C.union_1 struct{ $union uint64 }
type C.bitfield_t = C._Ctype_struct___9 func (union *C.union_2) unionfield_area() *C.point2d_t {
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
}
func (union *C.union_2) unionfield_solid() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
type C.union_2 struct{ $union [3]uint32 }
func (union *C.union_3) unionfield_point() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
func (union *C.union_3) unionfield_array() *C.unionarray_t {
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
}
func (union *C.union_3) unionfield_thing() *C.union3_t {
return (*C.union3_t)(unsafe.Pointer(&union.$union))
}
type C.union_3 struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_union2d) unionfield_d() *[2]float64 {
return (*[2]float64)(unsafe.Pointer(&union.$union))
}
type C.union_union2d struct{ $union [2]uint64 }
type C.enum_option C.int
type C.enum_unused C.uint
+14 -44
View File
@@ -10,7 +10,6 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/google/shlex"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
@@ -192,6 +191,15 @@ func (c *Config) UseThinLTO() bool {
// through a plugin, but it's too much hassle to set up. // through a plugin, but it's too much hassle to set up.
return false return false
} }
if len(parts) >= 2 && strings.HasPrefix(parts[2], "macos") {
// We use an external linker here at the moment.
return false
}
if len(parts) >= 2 && parts[2] == "windows" {
// Linker error (undefined runtime.trackedGlobalsBitmap) when linking
// for Windows. Disable it for now until that's figured out and fixed.
return false
}
// Other architectures support ThinLTO. // Other architectures support ThinLTO.
return true return true
} }
@@ -279,7 +287,6 @@ func (c *Config) CFlags() []string {
path, _ := c.LibcPath("picolibc") path, _ := c.LibcPath("picolibc")
cflags = append(cflags, cflags = append(cflags,
"--sysroot="+path, "--sysroot="+path,
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
"-isystem", filepath.Join(picolibcDir, "include"), "-isystem", filepath.Join(picolibcDir, "include"),
"-isystem", filepath.Join(picolibcDir, "tinystdio"), "-isystem", filepath.Join(picolibcDir, "tinystdio"),
) )
@@ -386,13 +393,6 @@ func (c *Config) BinaryFormat(ext string) string {
return c.Target.BinaryFormat return c.Target.BinaryFormat
} }
return "bin" return "bin"
case ".img":
// Image file. Only defined for the ESP32 at the moment, where it is a
// full (runnable) image that can be used in the Espressif QEMU fork.
if c.Target.BinaryFormat != "" {
return c.Target.BinaryFormat + "-img"
}
return "bin"
case ".hex": case ".hex":
// Similar to bin, but includes the start address and is thus usually a // Similar to bin, but includes the start address and is thus usually a
// better format. // better format.
@@ -493,43 +493,13 @@ func (c *Config) WasmAbi() string {
return c.Target.WasmAbi return c.Target.WasmAbi
} }
// EmulatorName is a shorthand to get the command for this emulator, something // Emulator returns the emulator target config
// like qemu-system-arm or simavr. func (c *Config) Emulator() []string {
func (c *Config) EmulatorName() string {
parts := strings.SplitN(c.Target.Emulator, " ", 2)
if len(parts) > 1 {
return parts[0]
}
return ""
}
// EmulatorFormat returns the binary format for the emulator and the associated
// file extension. An empty string means to pass directly whatever the linker
// produces directly without conversion (usually ELF format).
func (c *Config) EmulatorFormat() (format, fileExt string) {
switch {
case strings.Contains(c.Target.Emulator, "{img}"):
return "img", ".img"
default:
return "", ""
}
}
// Emulator returns a ready-to-run command to run the given binary in an
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
// the compiled binary.
func (c *Config) Emulator(format, binary string) ([]string, error) {
parts, err := shlex.Split(c.Target.Emulator)
if err != nil {
return nil, fmt.Errorf("could not parse emulator command: %w", err)
}
var emulator []string var emulator []string
for _, s := range parts { for _, s := range c.Target.Emulator {
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")) emulator = append(emulator, strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")))
s = strings.ReplaceAll(s, "{"+format+"}", binary)
emulator = append(emulator, s)
} }
return emulator, nil return emulator
} }
type TestConfig struct { type TestConfig struct {
+30 -13
View File
@@ -12,9 +12,11 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
) )
// Target specification for a given target. Used for bare metal targets. // Target specification for a given target. Used for bare metal targets.
@@ -42,8 +44,8 @@ type TargetSpec struct {
LDFlags []string `json:"ldflags"` LDFlags []string `json:"ldflags"`
LinkerScript string `json:"linkerscript"` LinkerScript string `json:"linkerscript"`
ExtraFiles []string `json:"extra-files"` ExtraFiles []string `json:"extra-files"`
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
Emulator string `json:"emulator"` Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
FlashCommand string `json:"flash-command"` FlashCommand string `json:"flash-command"`
GDB []string `json:"gdb"` GDB []string `json:"gdb"`
PortReset string `json:"flash-1200-bps-reset"` PortReset string `json:"flash-1200-bps-reset"`
@@ -57,7 +59,6 @@ type TargetSpec struct {
OpenOCDTarget string `json:"openocd-target"` OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"` OpenOCDTransport string `json:"openocd-transport"`
OpenOCDCommands []string `json:"openocd-commands"` OpenOCDCommands []string `json:"openocd-commands"`
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
JLinkDevice string `json:"jlink-device"` JLinkDevice string `json:"jlink-device"`
CodeModel string `json:"code-model"` CodeModel string `json:"code-model"`
RelocationModel string `json:"relocation-model"` RelocationModel string `json:"relocation-model"`
@@ -88,8 +89,19 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
if !src.IsNil() { if !src.IsNil() {
dst.Set(src) dst.Set(src)
} }
case reflect.Slice: // for slices, append the field case reflect.Slice: // for slices...
dst.Set(reflect.AppendSlice(dst, src)) if src.Len() > 0 { // ... if not empty ...
switch tag := field.Tag.Get("override"); tag {
case "copy":
// copy the field of child to spec
dst.Set(src)
case "append", "":
// or append the field of child to spec
dst.Set(reflect.AppendSlice(dst, src))
default:
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
}
}
default: default:
panic("unknown field type : " + kind.String()) panic("unknown field type : " + kind.String())
} }
@@ -246,7 +258,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87" spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "arm": case "arm":
spec.CPU = "generic" spec.CPU = "generic"
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables") spec.CFlags = append(spec.CFlags, "-fno-unwind-tables")
switch strings.Split(triple, "-")[0] { switch strings.Split(triple, "-")[0] {
case "armv5": case "armv5":
spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
@@ -265,7 +277,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
arch := strings.Split(triple, "-")[0] arch := strings.Split(triple, "-")[0]
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx") platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
spec.LDFlags = append(spec.LDFlags, spec.LDFlags = append(spec.LDFlags,
"-flavor", "darwin", "-flavor", "darwinnew",
"-dead_strip", "-dead_strip",
"-arch", arch, "-arch", arch,
"-platform_version", "macos", platformVersion, platformVersion, "-platform_version", "macos", platformVersion, platformVersion,
@@ -290,8 +302,13 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
"--image-base", "0x400000", "--image-base", "0x400000",
"--gc-sections", "--gc-sections",
"--no-insert-timestamp", "--no-insert-timestamp",
"--no-dynamicbase",
) )
llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
if llvmMajor >= 12 {
// This flag was added in LLVM 12. At the same time, LLVM 12
// switched the default from --dynamicbase to --no-dynamicbase.
spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase")
}
} else { } else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
} }
@@ -313,20 +330,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
case "386": case "386":
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case. // amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
if runtime.GOARCH != "amd64" { if runtime.GOARCH != "amd64" {
spec.Emulator = "qemu-i386 {}" spec.Emulator = []string{"qemu-i386"}
} }
case "amd64": case "amd64":
spec.Emulator = "qemu-x86_64 {}" spec.Emulator = []string{"qemu-x86_64"}
case "arm": case "arm":
spec.Emulator = "qemu-arm {}" spec.Emulator = []string{"qemu-arm"}
case "arm64": case "arm64":
spec.Emulator = "qemu-aarch64 {}" spec.Emulator = []string{"qemu-aarch64"}
} }
} }
} }
if goos != runtime.GOOS { if goos != runtime.GOOS {
if goos == "windows" { if goos == "windows" {
spec.Emulator = "wine {}" spec.Emulator = []string{"wine"}
} }
} }
return &spec, nil return &spec, nil
+5
View File
@@ -29,6 +29,7 @@ func TestOverrideProperties(t *testing.T) {
CPU: "baseCpu", CPU: "baseCpu",
CFlags: []string{"-base-foo", "-base-bar"}, CFlags: []string{"-base-foo", "-base-bar"},
BuildTags: []string{"bt1", "bt2"}, BuildTags: []string{"bt1", "bt2"},
Emulator: []string{"be1", "be2"},
DefaultStackSize: 42, DefaultStackSize: 42,
AutoStackSize: &baseAutoStackSize, AutoStackSize: &baseAutoStackSize,
} }
@@ -37,6 +38,7 @@ func TestOverrideProperties(t *testing.T) {
GOOS: "", GOOS: "",
CPU: "chlidCpu", CPU: "chlidCpu",
CFlags: []string{"-child-foo", "-child-bar"}, CFlags: []string{"-child-foo", "-child-bar"},
Emulator: []string{"ce1", "ce2"},
AutoStackSize: &childAutoStackSize, AutoStackSize: &childAutoStackSize,
DefaultStackSize: 64, DefaultStackSize: 64,
} }
@@ -55,6 +57,9 @@ func TestOverrideProperties(t *testing.T) {
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) { if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
t.Errorf("Overriding failed : got %v", base.BuildTags) t.Errorf("Overriding failed : got %v", base.BuildTags)
} }
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
t.Errorf("Overriding failed : got %v", base.Emulator)
}
if *base.AutoStackSize != false { if *base.AutoStackSize != false {
t.Errorf("Overriding failed : got %v", base.AutoStackSize) t.Errorf("Overriding failed : got %v", base.AutoStackSize)
} }
+24
View File
@@ -52,6 +52,30 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
ptr := b.getValue(call.Args[0]) ptr := b.getValue(call.Args[0])
old := b.getValue(call.Args[1]) old := b.getValue(call.Args[1])
newVal := b.getValue(call.Args[2]) newVal := b.getValue(call.Args[2])
if strings.HasSuffix(name, "64") {
if strings.HasPrefix(b.Triple, "thumb") {
// Work around a bug in LLVM, at least LLVM 11:
// https://reviews.llvm.org/D95891
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
if compareAndSwap.IsNil() {
// Declare the function if it isn't already declared.
i64Type := b.ctx.Int64Type()
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
}
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
// The __sync_val_compare_and_swap_8 function returns the old
// value. However, we shouldn't return the old value, we should
// return whether the compare/exchange was successful. This is
// easily done by comparing the returned (actual) old value with
// the expected old value passed to
// __sync_val_compare_and_swap_8.
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
return swapped, true
}
}
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true) tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
swapped := b.CreateExtractValue(tuple, 1, "") swapped := b.CreateExtractValue(tuple, 1, "")
return swapped, true return swapped, true
+41 -211
View File
@@ -9,7 +9,6 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"math/bits" "math/bits"
"path"
"path/filepath" "path/filepath"
"sort" "sort"
"strconv" "strconv"
@@ -21,10 +20,6 @@ import (
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
var typeParamUnderlyingType = func(t types.Type) types.Type {
return t
}
func init() { func init() {
llvm.InitializeAllTargets() llvm.InitializeAllTargets()
llvm.InitializeAllTargetMCs() llvm.InitializeAllTargetMCs()
@@ -81,7 +76,6 @@ type compilerContext struct {
program *ssa.Program program *ssa.Program
diagnostics []error diagnostics []error
astComments map[string]*ast.CommentGroup astComments map[string]*ast.CommentGroup
embedGlobals map[string][]*loader.EmbedFile
pkg *types.Package pkg *types.Package
packageDir string // directory for this package packageDir string // directory for this package
runtimePkg *types.Package runtimePkg *types.Package
@@ -256,7 +250,6 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) { func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
c := newCompilerContext(moduleName, machine, config, dumpSSA) c := newCompilerContext(moduleName, machine, config, dumpSSA)
c.packageDir = pkg.OriginalDir() c.packageDir = pkg.OriginalDir()
c.embedGlobals = pkg.EmbedGlobals
c.pkg = pkg.Pkg c.pkg = pkg.Pkg
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
c.program = ssaPkg.Prog c.program = ssaPkg.Prog
@@ -308,7 +301,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
}), }),
) )
c.dibuilder.Finalize() c.dibuilder.Finalize()
c.dibuilder.Destroy()
} }
return c.mod, c.diagnostics return c.mod, c.diagnostics
@@ -339,7 +331,6 @@ 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 {
goType = typeParamUnderlyingType(goType)
switch typ := goType.(type) { switch typ := goType.(type) {
case *types.Array: case *types.Array:
elemType := c.getLLVMType(typ.Elem()) elemType := c.getLLVMType(typ.Elem())
@@ -449,7 +440,6 @@ func (c *compilerContext) getDIType(typ types.Type) llvm.Metadata {
// createDIType creates a new DWARF type. Don't call this function directly, // createDIType creates a new DWARF type. Don't call this function directly,
// call getDIType instead. // call getDIType instead.
func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata { func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
typ = typeParamUnderlyingType(typ)
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) {
@@ -800,9 +790,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
for _, method := range methods { for _, method := range methods {
// Parse this method. // Parse this method.
fn := pkg.Prog.MethodValue(method) fn := pkg.Prog.MethodValue(method)
if fn == nil {
continue // probably a generic method
}
if fn.Blocks == nil { if fn.Blocks == nil {
continue // external function continue // external function
} }
@@ -827,9 +814,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
// Global variable. // Global variable.
info := c.getGlobalInfo(member) info := c.getGlobalInfo(member)
global := c.getGlobal(member) global := c.getGlobal(member)
if files, ok := c.embedGlobals[member.Name()]; ok { if !info.extern {
c.createEmbedGlobal(member, global, files)
} else if !info.extern {
global.SetInitializer(llvm.ConstNull(global.Type().ElementType())) global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
global.SetVisibility(llvm.HiddenVisibility) global.SetVisibility(llvm.HiddenVisibility)
if info.section != "" { if info.section != "" {
@@ -863,150 +848,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
} }
} }
// createEmbedGlobal creates an initializer for a //go:embed global variable.
func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) {
switch typ := member.Type().(*types.Pointer).Elem().Underlying().(type) {
case *types.Basic:
// String type.
if typ.Kind() != types.String {
// This is checked at the AST level, so should be unreachable.
panic("expected a string type")
}
if len(files) != 1 {
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
return
}
strObj := c.getEmbedFileString(files[0])
global.SetInitializer(strObj)
global.SetVisibility(llvm.HiddenVisibility)
case *types.Slice:
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
// This is checked at the AST level, so should be unreachable.
panic("expected a byte slice")
}
if len(files) != 1 {
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
return
}
file := files[0]
bufferValue := c.ctx.ConstString(string(file.Data), false)
bufferGlobal := llvm.AddGlobal(c.mod, bufferValue.Type(), c.pkg.Path()+"$embedslice")
bufferGlobal.SetInitializer(bufferValue)
bufferGlobal.SetLinkage(llvm.InternalLinkage)
bufferGlobal.SetAlignment(1)
slicePtr := llvm.ConstInBoundsGEP(bufferGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false),
})
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
global.SetInitializer(sliceObj)
global.SetVisibility(llvm.HiddenVisibility)
case *types.Struct:
// Assume this is an embed.FS struct:
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/embed/embed.go;l=148
// It looks like this:
// type FS struct {
// files *file
// }
// Make a slice of the files, as they will appear in the binary. They
// are sorted in a special way to allow for binary searches, see
// src/embed/embed.go for details.
dirset := map[string]struct{}{}
var allFiles []*loader.EmbedFile
for _, file := range files {
allFiles = append(allFiles, file)
dirname := file.Name
for {
dirname, _ = path.Split(path.Clean(dirname))
if dirname == "" {
break
}
if _, ok := dirset[dirname]; ok {
break
}
dirset[dirname] = struct{}{}
allFiles = append(allFiles, &loader.EmbedFile{
Name: dirname,
})
}
}
sort.Slice(allFiles, func(i, j int) bool {
dir1, name1 := path.Split(path.Clean(allFiles[i].Name))
dir2, name2 := path.Split(path.Clean(allFiles[j].Name))
if dir1 != dir2 {
return dir1 < dir2
}
return name1 < name2
})
// Make the backing array for the []files slice. This is a LLVM global.
embedFileStructType := c.getLLVMType(typ.Field(0).Type().(*types.Pointer).Elem().(*types.Slice).Elem())
var fileStructs []llvm.Value
for _, file := range allFiles {
fileStruct := llvm.ConstNull(embedFileStructType)
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
if file.Hash != "" {
data := c.getEmbedFileString(file)
fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
}
fileStructs = append(fileStructs, fileStruct)
}
sliceDataInitializer := llvm.ConstArray(embedFileStructType, fileStructs)
sliceDataGlobal := llvm.AddGlobal(c.mod, sliceDataInitializer.Type(), c.pkg.Path()+"$embedfsfiles")
sliceDataGlobal.SetInitializer(sliceDataInitializer)
sliceDataGlobal.SetLinkage(llvm.InternalLinkage)
sliceDataGlobal.SetGlobalConstant(true)
sliceDataGlobal.SetUnnamedAddr(true)
sliceDataGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceDataInitializer.Type()))
// Create the slice object itself.
// Because embed.FS refers to it as *[]embed.file instead of a plain
// []embed.file, we have to store this as a global.
slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false),
})
sliceLen := llvm.ConstInt(c.uintptrType, uint64(len(fileStructs)), false)
sliceInitializer := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
sliceGlobal := llvm.AddGlobal(c.mod, sliceInitializer.Type(), c.pkg.Path()+"$embedfsslice")
sliceGlobal.SetInitializer(sliceInitializer)
sliceGlobal.SetLinkage(llvm.InternalLinkage)
sliceGlobal.SetGlobalConstant(true)
sliceGlobal.SetUnnamedAddr(true)
sliceGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceInitializer.Type()))
// Define the embed.FS struct. It has only one field: the files (as a
// *[]embed.file).
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
global.SetInitializer(globalInitializer)
global.SetVisibility(llvm.HiddenVisibility)
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
}
}
// getEmbedFileString returns the (constant) string object with the contents of
// the given file. This is a llvm.Value of a regular Go string.
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
dataGlobalName := "embed/file_" + file.Hash
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
if dataGlobal.IsNil() {
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
}
strPtr := llvm.ConstInBoundsGEP(dataGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false),
})
strLen := llvm.ConstInt(c.uintptrType, file.Size, false)
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
}
// createFunction builds the LLVM IR implementation for this function. The // createFunction builds the LLVM IR implementation for this function. The
// function must not yet be defined, otherwise this function will create a // function must not yet be defined, otherwise this function will create a
// diagnostic. // diagnostic.
@@ -1116,10 +957,10 @@ func (b *builder) createFunction() {
} else { } else {
fieldOffsets := b.expandFormalParamOffsets(llvmType) fieldOffsets := b.expandFormalParamOffsets(llvmType)
for i, field := range fields { for i, field := range fields {
expr := b.dibuilder.CreateExpression([]uint64{ expr := b.dibuilder.CreateExpression([]int64{
0x1000, // DW_OP_LLVM_fragment 0x1000, // DW_OP_LLVM_fragment
fieldOffsets[i] * 8, // offset in bits int64(fieldOffsets[i]) * 8, // offset in bits
b.targetData.TypeAllocSize(field.Type()) * 8, // size in bits int64(b.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
}) })
b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock) b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
} }
@@ -1599,7 +1440,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.emitSV64Call(instr.Args) return b.emitSV64Call(instr.Args)
case strings.HasPrefix(name, "(device/riscv.CSR)."): case strings.HasPrefix(name, "(device/riscv.CSR)."):
return b.emitCSROperation(instr) return b.emitCSROperation(instr)
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall"): case strings.HasPrefix(name, "syscall.Syscall"):
return b.createSyscall(instr) return b.createSyscall(instr)
case strings.HasPrefix(name, "syscall.rawSyscallNoError"): case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
return b.createRawSyscallNoError(instr) return b.createRawSyscallNoError(instr)
@@ -1628,14 +1469,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
case *ssa.Function: case *ssa.Function:
// Regular function call. No context is necessary. // Regular function call. No context is necessary.
context = llvm.Undef(b.i8ptrType) context = llvm.Undef(b.i8ptrType)
if info.variadic && len(fn.Params) == 0 {
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
// Eventually we might be able to eliminate this special case
// entirely. For details, see:
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
fnType := llvm.FunctionType(callee.Type().ElementType().ReturnType(), nil, false)
callee = llvm.ConstBitCast(callee, llvm.PointerType(fnType, b.funcPtrAddrSpace))
}
case *ssa.MakeClosure: case *ssa.MakeClosure:
// A call on a func value, but the callee is trivial to find. For // A call on a func value, but the callee is trivial to find. For
// example: immediately applied functions. // example: immediately applied functions.
@@ -1721,11 +1554,7 @@ func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
// Determine the maximum allowed size for a slice. The biggest possible // Determine the maximum allowed size for a slice. The biggest possible
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so // pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
// divide by the element type to get the real maximum size. // divide by the element type to get the real maximum size.
elementSize := c.targetData.TypeAllocSize(elementType) maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
if elementSize == 0 {
elementSize = 1
}
maxSize := maxPointerValue / elementSize
// len(slice) is an int. Make sure the length remains small enough to fit in // len(slice) is an int. Make sure the length remains small enough to fit in
// an int. // an int.
@@ -2628,41 +2457,42 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
} }
// createConst creates a LLVM constant value from a Go constant. // createConst creates a LLVM constant value from a Go constant.
func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value { func (b *builder) createConst(expr *ssa.Const) llvm.Value {
switch typ := expr.Type().Underlying().(type) { switch typ := expr.Type().Underlying().(type) {
case *types.Basic: case *types.Basic:
llvmType := c.getLLVMType(typ) llvmType := b.getLLVMType(typ)
if typ.Info()&types.IsBoolean != 0 { if typ.Info()&types.IsBoolean != 0 {
b := constant.BoolVal(expr.Value)
n := uint64(0) n := uint64(0)
if constant.BoolVal(expr.Value) { if b {
n = 1 n = 1
} }
return llvm.ConstInt(llvmType, n, false) return llvm.ConstInt(llvmType, n, false)
} else if typ.Info()&types.IsString != 0 { } else if typ.Info()&types.IsString != 0 {
str := constant.StringVal(expr.Value) str := constant.StringVal(expr.Value)
strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false) strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
var strPtr llvm.Value var strPtr llvm.Value
if str != "" { if str != "" {
objname := c.pkg.Path() + "$string" objname := b.pkg.Path() + "$string"
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname) global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
global.SetInitializer(c.ctx.ConstString(str, false)) global.SetInitializer(b.ctx.ConstString(str, false))
global.SetLinkage(llvm.InternalLinkage) global.SetLinkage(llvm.InternalLinkage)
global.SetGlobalConstant(true) global.SetGlobalConstant(true)
global.SetUnnamedAddr(true) global.SetUnnamedAddr(true)
global.SetAlignment(1) global.SetAlignment(1)
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero}) strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
} else { } else {
strPtr = llvm.ConstNull(c.i8ptrType) strPtr = llvm.ConstNull(b.i8ptrType)
} }
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen}) strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
return strObj return strObj
} else if typ.Kind() == types.UnsafePointer { } else if typ.Kind() == types.UnsafePointer {
if !expr.IsNil() { if !expr.IsNil() {
value, _ := constant.Uint64Val(constant.ToInt(expr.Value)) value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType) return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
} }
return llvm.ConstNull(c.i8ptrType) return llvm.ConstNull(b.i8ptrType)
} else if typ.Info()&types.IsUnsigned != 0 { } else if typ.Info()&types.IsUnsigned != 0 {
n, _ := constant.Uint64Val(constant.ToInt(expr.Value)) n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstInt(llvmType, n, false) return llvm.ConstInt(llvmType, n, false)
@@ -2673,18 +2503,18 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
n, _ := constant.Float64Val(expr.Value) n, _ := constant.Float64Val(expr.Value)
return llvm.ConstFloat(llvmType, n) return llvm.ConstFloat(llvmType, n)
} else if typ.Kind() == types.Complex64 { } else if typ.Kind() == types.Complex64 {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32])) r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32])) i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)) cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0}) cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1}) cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx return cplx
} else if typ.Kind() == types.Complex128 { } else if typ.Kind() == types.Complex128 {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64])) r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64])) i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)) cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0}) cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1}) cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx return cplx
} else { } else {
panic("unknown constant of basic type: " + expr.String()) panic("unknown constant of basic type: " + expr.String())
@@ -2693,35 +2523,35 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
if expr.Value != nil { if expr.Value != nil {
panic("expected nil chan constant") panic("expected nil chan constant")
} }
return llvm.ConstNull(c.getLLVMType(expr.Type())) return llvm.ConstNull(b.getLLVMType(expr.Type()))
case *types.Signature: case *types.Signature:
if expr.Value != nil { if expr.Value != nil {
panic("expected nil signature constant") panic("expected nil signature constant")
} }
return llvm.ConstNull(c.getLLVMType(expr.Type())) return llvm.ConstNull(b.getLLVMType(expr.Type()))
case *types.Interface: case *types.Interface:
if expr.Value != nil { if expr.Value != nil {
panic("expected nil interface constant") panic("expected nil interface constant")
} }
// Create a generic nil interface with no dynamic type (typecode=0). // Create a generic nil interface with no dynamic type (typecode=0).
fields := []llvm.Value{ fields := []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstInt(b.uintptrType, 0, false),
llvm.ConstPointerNull(c.i8ptrType), llvm.ConstPointerNull(b.i8ptrType),
} }
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields) return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields)
case *types.Pointer: case *types.Pointer:
if expr.Value != nil { if expr.Value != nil {
panic("expected nil pointer constant") panic("expected nil pointer constant")
} }
return llvm.ConstPointerNull(c.getLLVMType(typ)) return llvm.ConstPointerNull(b.getLLVMType(typ))
case *types.Slice: case *types.Slice:
if expr.Value != nil { if expr.Value != nil {
panic("expected nil slice constant") panic("expected nil slice constant")
} }
elemType := c.getLLVMType(typ.Elem()) elemType := b.getLLVMType(typ.Elem())
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0)) llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
llvmLen := llvm.ConstInt(c.uintptrType, 0, false) llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
slice := c.ctx.ConstStruct([]llvm.Value{ slice := b.ctx.ConstStruct([]llvm.Value{
llvmPtr, // backing array llvmPtr, // backing array
llvmLen, // len llvmLen, // len
llvmLen, // cap llvmLen, // cap
@@ -2732,7 +2562,7 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
// I believe this is not allowed by the Go spec. // I believe this is not allowed by the Go spec.
panic("non-nil map constant") panic("non-nil map constant")
} }
llvmType := c.getLLVMType(typ) llvmType := b.getLLVMType(typ)
return llvm.ConstNull(llvmType) return llvm.ConstNull(llvmType)
default: default:
panic("unknown constant: " + expr.String()) panic("unknown constant: " + expr.String())
-18
View File
@@ -1,18 +0,0 @@
//go:build go1.18
// +build go1.18
package compiler
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
// support.
import "go/types"
func init() {
typeParamUnderlyingType = func(t types.Type) types.Type {
if t, ok := t.(*types.TypeParam); ok {
return t.Underlying()
}
return t
}
}
+16 -5
View File
@@ -28,11 +28,18 @@ type testCase struct {
func TestCompiler(t *testing.T) { func TestCompiler(t *testing.T) {
t.Parallel() t.Parallel()
// Determine LLVM version. // Check LLVM version.
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0]) llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if err != nil { if err != nil {
t.Fatal("could not parse LLVM version:", llvm.Version) t.Fatal("could not parse LLVM version:", llvm.Version)
} }
if llvmMajor < 11 {
// It is likely this version needs to be bumped in the future.
// The goal is to at least test the LLVM version that's used by default
// in TinyGo and (if possible without too many workarounds) also some
// previous versions.
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
}
// Determine Go minor version (e.g. 16 in go1.16.3). // Determine Go minor version (e.g. 16 in go1.16.3).
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT")) _, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
@@ -106,10 +113,9 @@ func TestCompiler(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("failed to create target machine:", err) t.Fatal("failed to create target machine:", err)
} }
defer machine.Dispose()
// Load entire program AST into memory. // Load entire program AST into memory.
lprogram, err := loader.Load(config, "./testdata/"+tc.file, config.ClangHeaders, types.Config{ lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
Sizes: Sizes(machine), Sizes: Sizes(machine),
}) })
if err != nil { if err != nil {
@@ -215,9 +221,14 @@ func filterIrrelevantIRLines(lines []string) []string {
if strings.HasPrefix(line, "source_filename = ") { if strings.HasPrefix(line, "source_filename = ") {
continue continue
} }
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") { if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
// Ignore attribute groups. These may change between LLVM versions.
// Right now test outputs are for LLVM 12 and higher.
continue
}
if llvmVersion < 13 && strings.HasPrefix(line, "target datalayout = ") {
// The datalayout string may vary betewen LLVM versions. // The datalayout string may vary betewen LLVM versions.
// Right now test outputs are for LLVM 14 and higher. // Right now test outputs are for LLVM 13 and higher.
continue continue
} }
out = append(out, line) out = append(out, line)
+1 -4
View File
@@ -98,10 +98,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
case llvm.IntegerTypeKind: case llvm.IntegerTypeKind:
constraints = append(constraints, "r") constraints = append(constraints, "r")
case llvm.PointerTypeKind: case llvm.PointerTypeKind:
// Memory references require a type in LLVM 14, probably as a constraints = append(constraints, "*m")
// preparation for opaque pointers.
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
return s
default: default:
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name) err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
return s return s
+45 -79
View File
@@ -152,27 +152,6 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
return structGlobal return structGlobal
} }
var basicTypes = [...]string{
types.Bool: "bool",
types.Int: "int",
types.Int8: "int8",
types.Int16: "int16",
types.Int32: "int32",
types.Int64: "int64",
types.Uint: "uint",
types.Uint8: "uint8",
types.Uint16: "uint16",
types.Uint32: "uint32",
types.Uint64: "uint64",
types.Uintptr: "uintptr",
types.Float32: "float32",
types.Float64: "float64",
types.Complex64: "complex64",
types.Complex128: "complex128",
types.String: "string",
types.UnsafePointer: "unsafe.Pointer",
}
// getTypeCodeName returns a name for this type that can be used in the // getTypeCodeName returns a name for this type that can be used in the
// interface lowering pass to assign type codes as expected by the reflect // interface lowering pass to assign type codes as expected by the reflect
// package. See getTypeCodeNum. // package. See getTypeCodeNum.
@@ -183,7 +162,48 @@ func getTypeCodeName(t types.Type) string {
case *types.Array: case *types.Array:
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem()) return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
case *types.Basic: case *types.Basic:
return "basic:" + basicTypes[t.Kind()] var kind string
switch t.Kind() {
case types.Bool:
kind = "bool"
case types.Int:
kind = "int"
case types.Int8:
kind = "int8"
case types.Int16:
kind = "int16"
case types.Int32:
kind = "int32"
case types.Int64:
kind = "int64"
case types.Uint:
kind = "uint"
case types.Uint8:
kind = "uint8"
case types.Uint16:
kind = "uint16"
case types.Uint32:
kind = "uint32"
case types.Uint64:
kind = "uint64"
case types.Uintptr:
kind = "uintptr"
case types.Float32:
kind = "float32"
case types.Float64:
kind = "float64"
case types.Complex64:
kind = "complex64"
case types.Complex128:
kind = "complex128"
case types.String:
kind = "string"
case types.UnsafePointer:
kind = "unsafeptr"
default:
panic("unknown basic type: " + t.Name())
}
return "basic:" + kind
case *types.Chan: case *types.Chan:
return "chan:" + getTypeCodeName(t.Elem()) return "chan:" + getTypeCodeName(t.Elem())
case *types.Interface: case *types.Interface:
@@ -571,77 +591,23 @@ func signature(sig *types.Signature) string {
if i > 0 { if i > 0 {
s += ", " s += ", "
} }
s += typestring(sig.Params().At(i).Type()) s += sig.Params().At(i).Type().String()
} }
s += ")" s += ")"
} }
if sig.Results().Len() == 0 { if sig.Results().Len() == 0 {
// keep as-is // keep as-is
} else if sig.Results().Len() == 1 { } else if sig.Results().Len() == 1 {
s += " " + typestring(sig.Results().At(0).Type()) s += " " + sig.Results().At(0).Type().String()
} else { } else {
s += " (" s += " ("
for i := 0; i < sig.Results().Len(); i++ { for i := 0; i < sig.Results().Len(); i++ {
if i > 0 { if i > 0 {
s += ", " s += ", "
} }
s += typestring(sig.Results().At(i).Type()) s += sig.Results().At(i).Type().String()
} }
s += ")" s += ")"
} }
return s return s
} }
// typestring returns a stable (human-readable) type string for the given type
// that can be used for interface equality checks. It is almost (but not
// exactly) the same as calling t.String(). The main difference is some
// normalization around `byte` vs `uint8` for example.
func typestring(t types.Type) string {
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
switch t := t.(type) {
case *types.Array:
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
case *types.Basic:
return basicTypes[t.Kind()]
case *types.Chan:
switch t.Dir() {
case types.SendRecv:
return "chan (" + typestring(t.Elem()) + ")"
case types.SendOnly:
return "chan<- (" + typestring(t.Elem()) + ")"
case types.RecvOnly:
return "<-chan (" + typestring(t.Elem()) + ")"
default:
panic("unknown channel direction")
}
case *types.Interface:
methods := make([]string, t.NumMethods())
for i := range methods {
method := t.Method(i)
methods[i] = method.Name() + signature(method.Type().(*types.Signature))
}
return "interface{" + strings.Join(methods, ";") + "}"
case *types.Map:
return "map[" + typestring(t.Key()) + "]" + typestring(t.Elem())
case *types.Named:
return t.String()
case *types.Pointer:
return "*" + typestring(t.Elem())
case *types.Signature:
return "func" + signature(t)
case *types.Slice:
return "[]" + typestring(t.Elem())
case *types.Struct:
fields := make([]string, t.NumFields())
for i := range fields {
field := t.Field(i)
fields[i] = field.Name() + " " + typestring(field.Type())
if tag := t.Tag(i); tag != "" {
fields[i] += " " + strconv.Quote(tag)
}
}
return "struct{" + strings.Join(fields, ";") + "}"
default:
panic("unknown type: " + t.String())
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
return fmt.Errorf("type %q uses global context", t.String()) return fmt.Errorf("type %q uses global context", t.String())
default: default:
// we used some other context by accident // we used some other context by accident
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.String(), t.Context(), c.ctx) return fmt.Errorf("type %q uses context %v instead of the main context %v", t.Context(), c.ctx)
} }
// if this is a composite type, check the components of the type // if this is a composite type, check the components of the type
-2
View File
@@ -34,7 +34,6 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
ctx := t.Context() ctx := t.Context()
targetData := llvm.NewTargetData(mod.DataLayout()) targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
alloca = CreateEntryBlockAlloca(builder, t, name) alloca = CreateEntryBlockAlloca(builder, t, name)
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast") bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
@@ -47,7 +46,6 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value { func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
ctx := mod.Context() ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout()) targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
alloca := CreateEntryBlockAlloca(builder, t, name) alloca := CreateEntryBlockAlloca(builder, t, name)
+2 -4
View File
@@ -15,9 +15,8 @@ import (
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value { func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
ctx := mod.Context() ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout()) targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
uintptrType := ctx.IntType(targetData.PointerSize() * 8) uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
valueTypes := make([]llvm.Type, len(values)) valueTypes := make([]llvm.Type, len(values))
for i, value := range values { for i, value := range values {
@@ -128,9 +127,8 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needs
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value { func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
ctx := mod.Context() ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout()) targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
uintptrType := ctx.IntType(targetData.PointerSize() * 8) uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
packedType := ctx.StructType(valueTypes, false) packedType := ctx.StructType(valueTypes, false)
+1 -13
View File
@@ -10,13 +10,6 @@ import (
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
// constants for hashmap algorithms; must match src/runtime/hashmap.go
const (
hashmapAlgorithmBinary = iota
hashmapAlgorithmString
hashmapAlgorithmInterface
)
// createMakeMap creates a new map object (runtime.hashmap) by allocating and // createMakeMap creates a new map object (runtime.hashmap) by allocating and
// initializing an appropriately sized object. // initializing an appropriately sized object.
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) { func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
@@ -24,27 +17,22 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
keyType := mapType.Key().Underlying() keyType := mapType.Key().Underlying()
llvmValueType := b.getLLVMType(mapType.Elem().Underlying()) llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
var llvmKeyType llvm.Type var llvmKeyType llvm.Type
var alg uint64 // must match values in src/runtime/hashmap.go
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// String keys. // String keys.
llvmKeyType = b.getLLVMType(keyType) llvmKeyType = b.getLLVMType(keyType)
alg = hashmapAlgorithmString
} else if hashmapIsBinaryKey(keyType) { } else if hashmapIsBinaryKey(keyType) {
// Trivially comparable keys. // Trivially comparable keys.
llvmKeyType = b.getLLVMType(keyType) llvmKeyType = b.getLLVMType(keyType)
alg = hashmapAlgorithmBinary
} else { } else {
// All other keys. Implemented as map[interface{}]valueType for ease of // All other keys. Implemented as map[interface{}]valueType for ease of
// implementation. // implementation.
llvmKeyType = b.getLLVMRuntimeType("_interface") llvmKeyType = b.getLLVMRuntimeType("_interface")
alg = hashmapAlgorithmInterface
} }
keySize := b.targetData.TypeAllocSize(llvmKeyType) keySize := b.targetData.TypeAllocSize(llvmKeyType)
valueSize := b.targetData.TypeAllocSize(llvmValueType) valueSize := b.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false) llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false) llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
sizeHint := llvm.ConstInt(b.uintptrType, 8, false) sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
if expr.Reserve != nil { if expr.Reserve != nil {
sizeHint = b.getValue(expr.Reserve) sizeHint = b.getValue(expr.Reserve)
var err error var err error
@@ -53,7 +41,7 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
return llvm.Value{}, err return llvm.Value{}, err
} }
} }
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "") hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
return hashmap, nil return hashmap, nil
} }
-4
View File
@@ -45,11 +45,7 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
if t.Info()&types.IsString != 0 { if t.Info()&types.IsString != 0 {
return s.PtrSize return s.PtrSize
} }
case *types.Signature:
// Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned
return s.PtrSize
} }
a := s.Sizeof(T) // may be 0 a := s.Sizeof(T) // may be 0
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
if a < 1 { if a < 1 {
+1 -1
View File
@@ -191,7 +191,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
// should be created right away. // should be created right away.
// The exception is the package initializer, which does appear in the // The exception is the package initializer, which does appear in the
// *ssa.Package members and so shouldn't be created here. // *ssa.Package members and so shouldn't be created here.
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" { if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
irbuilder := c.ctx.NewBuilder() irbuilder := c.ctx.NewBuilder()
b := newBuilder(c, irbuilder, fn) b := newBuilder(c, irbuilder, fn)
b.createFunction() b.createFunction()
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'basic.go' ; ModuleID = 'basic.go'
source_filename = "basic.go" source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%main.kv = type { float } %main.kv = type { float }
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'channel.go' ; ModuleID = 'channel.go'
source_filename = "channel.go" source_filename = "channel.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'float.go' ; ModuleID = 'float.go'
source_filename = "float.go" source_filename = "float.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'func.go' ; ModuleID = 'func.go'
source_filename = "func.go" source_filename = "func.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'gc.go' ; ModuleID = 'gc.go'
source_filename = "gc.go" source_filename = "gc.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 } %runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'go1.17.go' ; ModuleID = 'go1.17.go'
source_filename = "go1.17.go" source_filename = "go1.17.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'goroutine.go' ; ModuleID = 'goroutine.go'
source_filename = "goroutine.go" source_filename = "goroutine.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
+2 -2
View File
@@ -1,6 +1,6 @@
; ModuleID = 'interface.go' ; ModuleID = 'interface.go'
source_filename = "interface.go" source_filename = "interface.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 } %runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
@@ -128,5 +128,5 @@ declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"
attributes #0 = { nounwind } attributes #0 = { nounwind }
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" } attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" } attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" } attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) byte" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) byte" }
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" } attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'intrinsics.go' ; ModuleID = 'intrinsics.go'
source_filename = "intrinsics.go" source_filename = "intrinsics.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'pointer.go' ; ModuleID = 'pointer.go'
source_filename = "pointer.go" source_filename = "pointer.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'pragma.go' ; ModuleID = 'pragma.go'
source_filename = "pragma.go" source_filename = "pragma.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
@extern_global = external global [0 x i8], align 1 @extern_global = external global [0 x i8], align 1
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'slice.go' ; ModuleID = 'slice.go'
source_filename = "slice.go" source_filename = "slice.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'string.go' ; ModuleID = 'string.go'
source_filename = "string.go" source_filename = "string.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._string = type { i8*, i32 } %runtime._string = type { i8*, i32 }
+3 -3
View File
@@ -1,6 +1,6 @@
module github.com/tinygo-org/tinygo module github.com/tinygo-org/tinygo
go 1.16 go 1.15
require ( require (
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a
@@ -13,7 +13,7 @@ require (
github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-colorable v0.1.8
go.bug.st/serial v1.1.3 go.bug.st/serial v1.1.3
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
golang.org/x/tools v0.1.11 golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1
) )
+16 -15
View File
@@ -40,44 +40,45 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.bug.st/serial v1.1.3 h1:YEBxJa9pKS9Wdg46B/jiaKbvvbUrjhZZZITfJHEJhaE= go.bug.st/serial v1.1.3 h1:YEBxJa9pKS9Wdg46B/jiaKbvvbUrjhZZZITfJHEJhaE=
go.bug.st/serial v1.1.3/go.mod h1:8TT7u/SwwNIpJ8QaG4s+HTjFt9ReXs2cdOU7ZEk50Dk= go.bug.st/serial v1.1.3/go.mod h1:8TT7u/SwwNIpJ8QaG4s+HTjFt9ReXs2cdOU7ZEk50Dk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/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-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 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=
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f h1:9/J+NpFTpAhYcbh1mC4dr9W/aAPnwrUh8dmq21xWdSM= tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1 h1:6G8AxueDdqobCEqQrmHPLaEH1AZ1p6Y7rGElDNT7N98=
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0= tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+3 -3
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo. // Version of TinyGo.
// Update this value before release of new version of software. // Update this value before release of new version of software.
const Version = "0.24.0-dev" const Version = "0.23.0-dev"
var ( var (
// This variable is set at build time using -ldflags parameters. // This variable is set at build time using -ldflags parameters.
@@ -58,9 +58,9 @@ func GorootVersionString(goroot string) (string, error) {
return string(data), nil return string(data), nil
} else if data, err := ioutil.ReadFile(filepath.Join( } else if data, err := ioutil.ReadFile(filepath.Join(
goroot, "src", "internal", "buildcfg", "zbootstrap.go")); err == nil { goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
r := regexp.MustCompile("const version = `(.*)`") r := regexp.MustCompile("const TheVersion = `(.*)`")
matches := r.FindSubmatch(data) matches := r.FindSubmatch(data)
if len(matches) != 2 { if len(matches) != 2 {
return "", errors.New("Invalid go version output:\n" + string(data)) return "", errors.New("Invalid go version output:\n" + string(data))
+1 -4
View File
@@ -18,7 +18,6 @@ var (
errUnsupportedInst = errors.New("interp: unsupported instruction") errUnsupportedInst = errors.New("interp: unsupported instruction")
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)") errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
errMapAlreadyCreated = errors.New("interp: map already created") errMapAlreadyCreated = errors.New("interp: map already created")
errLoopUnrolled = errors.New("interp: loop unrolled")
) )
// This is one of the errors that can be returned from toLLVMValue when the // This is one of the errors that can be returned from toLLVMValue when the
@@ -27,9 +26,7 @@ var (
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size") var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
func isRecoverableError(err error) bool { func isRecoverableError(err error) bool {
return err == errIntegerAsPointer || err == errUnsupportedInst || return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
err == errLoopUnrolled
} }
// ErrorLine is one line in a traceback. The position may be missing. // ErrorLine is one line in a traceback. The position may be missing.
+3 -18
View File
@@ -50,17 +50,10 @@ func newRunner(mod llvm.Module, debug bool) *runner {
return &r return &r
} }
// Dispose deallocates all alloated LLVM resources.
func (r *runner) dispose() {
r.targetData.Dispose()
r.targetData = llvm.TargetData{}
}
// Run evaluates runtime.initAll function as much as possible at compile time. // Run evaluates runtime.initAll function as much as possible at compile time.
// Set debug to true if it should print output while running. // Set debug to true if it should print output while running.
func Run(mod llvm.Module, debug bool) error { func Run(mod llvm.Module, debug bool) error {
r := newRunner(mod, debug) r := newRunner(mod, debug)
defer r.dispose()
initAll := mod.NamedFunction("runtime.initAll") initAll := mod.NamedFunction("runtime.initAll")
bb := initAll.EntryBasicBlock() bb := initAll.EntryBasicBlock()
@@ -128,10 +121,7 @@ func Run(mod llvm.Module, debug bool) error {
r.builder.CreateCall(fn, []llvm.Value{i8undef}, "") r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
// Make sure that any globals touched by the package // Make sure that any globals touched by the package
// initializer, won't be accessed by later package initializers. // initializer, won't be accessed by later package initializers.
err := r.markExternalLoad(fn) r.markExternalLoad(fn)
if err != nil {
return fmt.Errorf("failed to interpret package %s: %w", r.pkgName, err)
}
continue continue
} }
return callErr return callErr
@@ -203,7 +193,6 @@ func RunFunc(fn llvm.Value, debug bool) error {
// Create and initialize *runner object. // Create and initialize *runner object.
mod := fn.GlobalParent() mod := fn.GlobalParent()
r := newRunner(mod, debug) r := newRunner(mod, debug)
defer r.dispose()
initName := fn.Name() initName := fn.Name()
if !strings.HasSuffix(initName, ".init") { if !strings.HasSuffix(initName, ".init") {
return errorAt(fn, "interp: unexpected function name (expected *.init)") return errorAt(fn, "interp: unexpected function name (expected *.init)")
@@ -299,16 +288,12 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
// variable. Another package initializer might read from the same global // variable. Another package initializer might read from the same global
// variable. By marking this function as being run at runtime, that load // variable. By marking this function as being run at runtime, that load
// instruction will need to be run at runtime instead of at compile time. // instruction will need to be run at runtime instead of at compile time.
func (r *runner) markExternalLoad(llvmValue llvm.Value) error { func (r *runner) markExternalLoad(llvmValue llvm.Value) {
mem := memoryView{r: r} mem := memoryView{r: r}
err := mem.markExternalLoad(llvmValue) mem.markExternalLoad(llvmValue)
if err != nil {
return err
}
for index, obj := range mem.objects { for index, obj := range mem.objects {
if obj.marked > r.objects[index].marked { if obj.marked > r.objects[index].marked {
r.objects[index].marked = obj.marked r.objects[index].marked = obj.marked
} }
} }
return nil
} }
+1 -13
View File
@@ -3,7 +3,6 @@ package interp
import ( import (
"io/ioutil" "io/ioutil"
"os" "os"
"strconv"
"strings" "strings"
"testing" "testing"
@@ -11,12 +10,6 @@ import (
) )
func TestInterp(t *testing.T) { func TestInterp(t *testing.T) {
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
if err != nil {
// Note: this should never happen and if it does, it will always happen
// for a particular build because llvm.Version is a constant.
panic(err)
}
for _, name := range []string{ for _, name := range []string{
"basic", "basic",
"phi", "phi",
@@ -26,10 +19,7 @@ func TestInterp(t *testing.T) {
"revert", "revert",
"alloc", "alloc",
} { } {
name := name // make local to this closure name := name // make tc local to this closure
if name == "slice-copy" && llvmVersion < 14 {
continue
}
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
t.Parallel() t.Parallel()
runTest(t, "testdata/"+name) runTest(t, "testdata/"+name)
@@ -40,7 +30,6 @@ func TestInterp(t *testing.T) {
func runTest(t *testing.T, pathPrefix string) { func runTest(t *testing.T, pathPrefix string) {
// Read the input IR. // Read the input IR.
ctx := llvm.NewContext() ctx := llvm.NewContext()
defer ctx.Dispose()
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll") buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
if err != nil { if err != nil {
@@ -50,7 +39,6 @@ func runTest(t *testing.T, pathPrefix string) {
if err != nil { if err != nil {
t.Fatalf("could not load module:\n%v", err) t.Fatalf("could not load module:\n%v", err)
} }
defer mod.Dispose()
// Perform the transform. // Perform the transform.
err = Run(mod, false) err = Run(mod, false)
+4 -56
View File
@@ -24,39 +24,15 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
locals[i] = param locals[i] = param
} }
// Track what blocks have run instructions at runtime.
// This is used to prevent unrolling.
var runtimeBlocks map[int]struct{}
// Start with the first basic block and the first instruction. // Start with the first basic block and the first instruction.
// Branch instructions may modify both bb and instIndex when branching. // Branch instructions may modify both bb and instIndex when branching.
bb := fn.blocks[0] bb := fn.blocks[0]
currentBB := 0 currentBB := 0
lastBB := -1 // last basic block is undefined, only defined after a branch lastBB := -1 // last basic block is undefined, only defined after a branch
var operands []value var operands []value
startRTInsts := len(mem.instructions)
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ { for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
if instIndex == 0 { if instIndex == 0 {
// This is the start of a new basic block. // This is the start of a new basic block.
if len(mem.instructions) != startRTInsts {
if _, ok := runtimeBlocks[lastBB]; ok {
// This loop has been unrolled.
// Avoid doing this, as it can result in a large amount of extra machine code.
// This currently uses the branch from the last block, as there is no available information to give a better location.
lastBBInsts := fn.blocks[lastBB].instructions
return nil, mem, r.errorAt(lastBBInsts[len(lastBBInsts)-1], errLoopUnrolled)
}
// Flag the last block as having run stuff at runtime.
if runtimeBlocks == nil {
runtimeBlocks = make(map[int]struct{})
}
runtimeBlocks[lastBB] = struct{}{}
// Reset the block-start runtime instructions counter.
startRTInsts = len(mem.instructions)
}
// There may be PHI nodes that need to be resolved. Resolve all PHI // There may be PHI nodes that need to be resolved. Resolve all PHI
// nodes before continuing with regular instructions. // nodes before continuing with regular instructions.
// PHI nodes need to be treated specially because they can have a // PHI nodes need to be treated specially because they can have a
@@ -105,28 +81,12 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if inst.opcode != llvm.PHI { if inst.opcode != llvm.PHI {
for _, v := range inst.operands { for _, v := range inst.operands {
if v, ok := v.(localValue); ok { if v, ok := v.(localValue); ok {
index, ok := fn.locals[v.value] if localVal := locals[fn.locals[v.value]]; localVal == nil {
if !ok {
// This is a localValue that is not local to the
// function. An example would be an inline assembly call
// operand.
isRuntimeInst = true
break
}
localVal := locals[index]
if localVal == nil {
// Trying to read a function-local value before it is
// set.
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined")) return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
} else { } else {
operands = append(operands, localVal) operands = append(operands, localVal)
if _, ok := localVal.(localValue); ok { if _, ok := localVal.(localValue); ok {
// The function-local value is still just a
// localValue (which can't be interpreted at compile
// time). Not sure whether this ever happens in
// practice.
isRuntimeInst = true isRuntimeInst = true
break
} }
continue continue
} }
@@ -259,9 +219,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if err != nil { if err != nil {
return nil, mem, err return nil, mem, err
} }
case callFn.name == "internal/task.Pause":
// Task scheduling isn't possible at compile time.
return nil, mem, r.errorAt(inst, errUnsupportedRuntimeInst)
case callFn.name == "runtime.nanotime" && r.pkgName == "time": case callFn.name == "runtime.nanotime" && r.pkgName == "time":
// The time package contains a call to runtime.nanotime. // The time package contains a call to runtime.nanotime.
// This appears to be to work around a limitation in Windows // This appears to be to work around a limitation in Windows
@@ -969,18 +926,12 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
args := operands[:len(operands)-1] args := operands[:len(operands)-1]
for _, arg := range args { for _, arg := range args {
if arg.Type().TypeKind() == llvm.PointerTypeKind { if arg.Type().TypeKind() == llvm.PointerTypeKind {
err := mem.markExternalStore(arg) mem.markExternalStore(arg)
if err != nil {
return r.errorAt(inst, err)
}
} }
} }
result = r.builder.CreateCall(llvmFn, args, inst.name) result = r.builder.CreateCall(llvmFn, args, inst.name)
case llvm.Load: case llvm.Load:
err := mem.markExternalLoad(operands[0]) mem.markExternalLoad(operands[0])
if err != nil {
return r.errorAt(inst, err)
}
result = r.builder.CreateLoad(operands[0], inst.name) result = r.builder.CreateLoad(operands[0], inst.name)
if inst.llvmInst.IsVolatile() { if inst.llvmInst.IsVolatile() {
result.SetVolatile(true) result.SetVolatile(true)
@@ -989,10 +940,7 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
result.SetOrdering(ordering) result.SetOrdering(ordering)
} }
case llvm.Store: case llvm.Store:
err := mem.markExternalStore(operands[1]) mem.markExternalStore(operands[1])
if err != nil {
return r.errorAt(inst, err)
}
result = r.builder.CreateStore(operands[0], operands[1]) result = r.builder.CreateStore(operands[0], operands[1])
if inst.llvmInst.IsVolatile() { if inst.llvmInst.IsVolatile() {
result.SetVolatile(true) result.SetVolatile(true)
+14 -41
View File
@@ -17,7 +17,6 @@ package interp
import ( import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"math" "math"
"math/big" "math/big"
"strconv" "strconv"
@@ -105,28 +104,28 @@ func (mv *memoryView) revert() {
// means that the interpreter can still read from it, but cannot write to it as // means that the interpreter can still read from it, but cannot write to it as
// that would mean the external read (done at runtime) reads from a state that // that would mean the external read (done at runtime) reads from a state that
// would not exist had the whole initialization been done at runtime. // would not exist had the whole initialization been done at runtime.
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) error { func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) {
return mv.markExternal(llvmValue, 1) mv.markExternal(llvmValue, 1)
} }
// markExternalStore marks the given LLVM value as having an external write. // markExternalStore marks the given LLVM value as having an external write.
// This means that the interpreter can no longer read from it or write to it, as // This means that the interpreter can no longer read from it or write to it, as
// that would happen in a different order than if all initialization were // that would happen in a different order than if all initialization were
// happening at runtime. // happening at runtime.
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) error { func (mv *memoryView) markExternalStore(llvmValue llvm.Value) {
return mv.markExternal(llvmValue, 2) mv.markExternal(llvmValue, 2)
} }
// markExternal is a helper for markExternalLoad and markExternalStore, and // markExternal is a helper for markExternalLoad and markExternalStore, and
// should not be called directly. // should not be called directly.
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error { func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
if llvmValue.IsUndef() || llvmValue.IsNull() { if llvmValue.IsUndef() || llvmValue.IsNull() {
// Null and undef definitely don't contain (valid) pointers. // Null and undef definitely don't contain (valid) pointers.
return nil return
} }
if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() { if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
// These are considered external by default, there is nothing to mark. // These are considered external by default, there is nothing to mark.
return nil return
} }
if !llvmValue.IsAGlobalValue().IsNil() { if !llvmValue.IsAGlobalValue().IsNil() {
@@ -145,10 +144,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
// Using mark '2' (which means read/write access) because // Using mark '2' (which means read/write access) because
// even from an object that is only read from, the resulting // even from an object that is only read from, the resulting
// loaded pointer can be written to. // loaded pointer can be written to.
err := mv.markExternal(initializer, 2) mv.markExternal(initializer, 2)
if err != nil {
return err
}
} }
} else { } else {
// This is a function. Go through all instructions and mark all // This is a function. Go through all instructions and mark all
@@ -174,10 +170,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
for i := 0; i < numOperands; i++ { for i := 0; i < numOperands; i++ {
// Using mark '2' (which means read/write access) // Using mark '2' (which means read/write access)
// because this might be a store instruction. // because this might be a store instruction.
err := mv.markExternal(inst.Operand(i), 2) mv.markExternal(inst.Operand(i), 2)
if err != nil {
return err
}
} }
} }
} }
@@ -186,22 +179,9 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
} else if !llvmValue.IsAConstantExpr().IsNil() { } else if !llvmValue.IsAConstantExpr().IsNil() {
switch llvmValue.Opcode() { switch llvmValue.Opcode() {
case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr: case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
err := mv.markExternal(llvmValue.Operand(0), mark) mv.markExternal(llvmValue.Operand(0), mark)
if err != nil {
return err
}
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
// Integer binary operators. Mark both operands.
err := mv.markExternal(llvmValue.Operand(0), mark)
if err != nil {
return err
}
err = mv.markExternal(llvmValue.Operand(1), mark)
if err != nil {
return err
}
default: default:
return fmt.Errorf("interp: unknown constant expression '%s'", instructionNameMap[llvmValue.Opcode()]) panic("interp: unknown constant expression")
} }
} else if !llvmValue.IsAInlineAsm().IsNil() { } else if !llvmValue.IsAInlineAsm().IsNil() {
// Inline assembly can modify globals but only exported globals. Let's // Inline assembly can modify globals but only exported globals. Let's
@@ -216,25 +196,18 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
numElements := llvmType.StructElementTypesCount() numElements := llvmType.StructElementTypesCount()
for i := 0; i < numElements; i++ { for i := 0; i < numElements; i++ {
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)}) element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
err := mv.markExternal(element, mark) mv.markExternal(element, mark)
if err != nil {
return err
}
} }
case llvm.ArrayTypeKind: case llvm.ArrayTypeKind:
numElements := llvmType.ArrayLength() numElements := llvmType.ArrayLength()
for i := 0; i < numElements; i++ { for i := 0; i < numElements; i++ {
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)}) element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
err := mv.markExternal(element, mark) mv.markExternal(element, mark)
if err != nil {
return err
}
} }
default: default:
return errors.New("interp: unknown type kind in markExternalValue") panic("interp: unknown type kind in markExternalValue")
} }
} }
return nil
} }
// hasExternalLoadOrStore returns true if this object has an external load or // hasExternalLoadOrStore returns true if this object has an external load or
-45
View File
@@ -3,8 +3,6 @@ target triple = "x86_64--linux"
declare void @externalCall(i64) declare void @externalCall(i64)
declare i64 @ptrHash(i8* nocapture)
@foo.knownAtRuntime = global i64 0 @foo.knownAtRuntime = global i64 0
@bar.knownAtRuntime = global i64 0 @bar.knownAtRuntime = global i64 0
@baz.someGlobal = external global [3 x {i64, i32}] @baz.someGlobal = external global [3 x {i64, i32}]
@@ -12,8 +10,6 @@ declare i64 @ptrHash(i8* nocapture)
@x.atomicNum = global i32 0 @x.atomicNum = global i32 0
@x.volatileNum = global i32 0 @x.volatileNum = global i32 0
@y.ready = global i32 0 @y.ready = global i32 0
@z.bloom = global i64 0
@z.arr = global [32 x i8] zeroinitializer
define void @runtime.initAll() unnamed_addr { define void @runtime.initAll() unnamed_addr {
entry: entry:
@@ -23,7 +19,6 @@ entry:
call void @main.init(i8* undef) call void @main.init(i8* undef)
call void @x.init(i8* undef) call void @x.init(i8* undef)
call void @y.init(i8* undef) call void @y.init(i8* undef)
call void @z.init(i8* undef)
ret void ret void
} }
@@ -77,43 +72,3 @@ loop:
end: end:
ret void ret void
} }
define internal void @z.init(i8* %context) unnamed_addr {
%bloom = bitcast i64* @z.bloom to i8*
; This can be safely expanded.
call void @z.setArr(i8* %bloom, i64 1, i8* %bloom)
; This call should be reverted to prevent unrolling.
call void @z.setArr(i8* bitcast ([32 x i8]* @z.arr to i8*), i64 32, i8* %bloom)
ret void
}
define internal void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
entry:
br label %loop
loop:
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
%idx = sub i64 %prev, 1
%elem = getelementptr i8, i8* %arr, i64 %idx
call void @z.set(i8* %elem, i8* %context)
%done = icmp eq i64 %idx, 0
br i1 %done, label %end, label %loop
end:
ret void
}
define internal void @z.set(i8* %ptr, i8* %context) unnamed_addr {
; Insert the pointer into the Bloom filter.
%hash = call i64 @ptrHash(i8* %ptr)
%index = lshr i64 %hash, 58
%bit = shl i64 1, %index
%bloom = bitcast i8* %context to i64*
%old = load i64, i64* %bloom
%new = or i64 %old, %bit
store i64 %new, i64* %bloom
ret void
}
-33
View File
@@ -8,13 +8,9 @@ target triple = "x86_64--linux"
@x.atomicNum = local_unnamed_addr global i32 0 @x.atomicNum = local_unnamed_addr global i32 0
@x.volatileNum = global i32 0 @x.volatileNum = global i32 0
@y.ready = local_unnamed_addr global i32 0 @y.ready = local_unnamed_addr global i32 0
@z.bloom = global i64 0
@z.arr = global [32 x i8] zeroinitializer
declare void @externalCall(i64) local_unnamed_addr declare void @externalCall(i64) local_unnamed_addr
declare i64 @ptrHash(i8* nocapture) local_unnamed_addr
define void @runtime.initAll() unnamed_addr { define void @runtime.initAll() unnamed_addr {
entry: entry:
call fastcc void @baz.init(i8* undef) call fastcc void @baz.init(i8* undef)
@@ -28,8 +24,6 @@ entry:
%y = load volatile i32, i32* @x.volatileNum, align 4 %y = load volatile i32, i32* @x.volatileNum, align 4
store volatile i32 %y, i32* @x.volatileNum, align 4 store volatile i32 %y, i32* @x.volatileNum, align 4
call fastcc void @y.init(i8* undef) call fastcc void @y.init(i8* undef)
call fastcc void @z.set(i8* bitcast (i64* @z.bloom to i8*), i8* bitcast (i64* @z.bloom to i8*))
call fastcc void @z.setArr(i8* getelementptr inbounds ([32 x i8], [32 x i8]* @z.arr, i32 0, i32 0), i64 32, i8* bitcast (i64* @z.bloom to i8*))
ret void ret void
} }
@@ -54,30 +48,3 @@ loop: ; preds = %loop, %entry
end: ; preds = %loop end: ; preds = %loop
ret void ret void
} }
define internal fastcc void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
entry:
br label %loop
loop: ; preds = %loop, %entry
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
%idx = sub i64 %prev, 1
%elem = getelementptr i8, i8* %arr, i64 %idx
call fastcc void @z.set(i8* %elem, i8* %context)
%done = icmp eq i64 %idx, 0
br i1 %done, label %end, label %loop
end: ; preds = %loop
ret void
}
define internal fastcc void @z.set(i8* %ptr, i8* %context) unnamed_addr {
%hash = call i64 @ptrHash(i8* %ptr)
%index = lshr i64 %hash, 58
%bit = shl i64 1, %index
%bloom = bitcast i8* %context to i64*
%old = load i64, i64* %bloom, align 8
%new = or i64 %old, %bit
store i64 %new, i64* %bloom, align 8
ret void
}
+4 -1
View File
@@ -1,6 +1,8 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux" target triple = "x86_64--linux"
@"main$alloc.1" = internal unnamed_addr constant [6 x i8] c"\05\00{\00\00\04", align 8
declare void @runtime.printuint8(i8) local_unnamed_addr declare void @runtime.printuint8(i8) local_unnamed_addr
declare void @runtime.printint16(i16) local_unnamed_addr declare void @runtime.printint16(i16) local_unnamed_addr
@@ -15,6 +17,7 @@ entry:
call void @runtime.printuint8(i8 3) call void @runtime.printuint8(i8 3)
call void @runtime.printuint8(i8 3) call void @runtime.printuint8(i8 3)
call void @runtime.printint16(i16 5) call void @runtime.printint16(i16 5)
call void @runtime.printint16(i16 5) %int16SliceDst.val = load i16, i16* bitcast ([6 x i8]* @"main$alloc.1" to i16*), align 2
call void @runtime.printint16(i16 %int16SliceDst.val)
ret void ret void
} }
+1
Submodule lib/compiler-rt added at 5bc79797e1
-1
View File
@@ -230,7 +230,6 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
"device/": false, "device/": false,
"examples/": false, "examples/": false,
"internal/": true, "internal/": true,
"internal/fuzz/": false,
"internal/bytealg/": false, "internal/bytealg/": false,
"internal/reflectlite/": false, "internal/reflectlite/": false,
"internal/task/": false, "internal/task/": false,
+13 -283
View File
@@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"go/ast" "go/ast"
"go/constant"
"go/parser" "go/parser"
"go/scanner" "go/scanner"
"go/token" "go/token"
@@ -16,20 +15,16 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"unicode"
"github.com/tinygo-org/tinygo/cgo" "github.com/tinygo-org/tinygo/cgo"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
) )
var addInstances func(*types.Info)
// Program holds all packages and some metadata about the program as a whole. // Program holds all packages and some metadata about the program as a whole.
type Program struct { type Program struct {
config *compileopts.Config config *compileopts.Config
@@ -66,9 +61,6 @@ type PackageJSON struct {
CgoFiles []string CgoFiles []string
CFiles []string CFiles []string
// Embedded files
EmbedFiles []string
// Dependency information // Dependency information
Imports []string Imports []string
ImportMap map[string]string ImportMap map[string]string
@@ -85,28 +77,19 @@ type PackageJSON struct {
type Package struct { type Package struct {
PackageJSON PackageJSON
program *Program program *Program
Files []*ast.File Files []*ast.File
FileHashes map[string][]byte FileHashes map[string][]byte
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used) CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
CGoHeaders []string // text above 'import "C"' lines CGoHeaders []string // text above 'import "C"' lines
EmbedGlobals map[string][]*EmbedFile Pkg *types.Package
Pkg *types.Package info types.Info
info types.Info
}
type EmbedFile struct {
Name string
Size uint64
Hash string // hash of the file (as a hex string)
NeedsData bool // true if this file is embedded as a byte slice
Data []byte // contents of this file (only if NeedsData is set)
} }
// Load loads the given package with all dependencies (including the runtime // Load loads the given package with all dependencies (including the runtime
// package). Call .Parse() afterwards to parse all Go files (including CGo // package). Call .Parse() afterwards to parse all Go files (including CGo
// processing, if necessary). // processing, if necessary).
func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) { func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, typeChecker types.Config) (*Program, error) {
goroot, err := GetCachedGoroot(config) goroot, err := GetCachedGoroot(config)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -135,7 +118,7 @@ func Load(config *compileopts.Config, inputPkg string, clangHeaders string, type
if config.TestConfig.CompileTestBinary { if config.TestConfig.CompileTestBinary {
extraArgs = append(extraArgs, "-test") extraArgs = append(extraArgs, "-test")
} }
cmd, err := List(config, extraArgs, []string{inputPkg}) cmd, err := List(config, extraArgs, inputPkgs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -154,9 +137,8 @@ func Load(config *compileopts.Config, inputPkg string, clangHeaders string, type
decoder := json.NewDecoder(buf) decoder := json.NewDecoder(buf)
for { for {
pkg := &Package{ pkg := &Package{
program: p, program: p,
FileHashes: make(map[string][]byte), FileHashes: make(map[string][]byte),
EmbedGlobals: make(map[string][]*EmbedFile),
info: types.Info{ info: types.Info{
Types: make(map[ast.Expr]types.TypeAndValue), Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object), Defs: make(map[*ast.Ident]types.Object),
@@ -166,9 +148,6 @@ func Load(config *compileopts.Config, inputPkg string, clangHeaders string, type
Selections: make(map[*ast.SelectorExpr]*types.Selection), Selections: make(map[*ast.SelectorExpr]*types.Selection),
}, },
} }
if addInstances != nil {
addInstances(&pkg.info)
}
err := decoder.Decode(&pkg.PackageJSON) err := decoder.Decode(&pkg.PackageJSON)
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
@@ -378,15 +357,15 @@ func (p *Package) Check() error {
return nil // already typechecked return nil // already typechecked
} }
// Prepare some state used during type checking.
var typeErrors []error var typeErrors []error
checker := p.program.typeChecker // make a copy, because it will be modified checker := p.program.typeChecker // make a copy, because it will be modified
checker.Error = func(err error) { checker.Error = func(err error) {
typeErrors = append(typeErrors, err) typeErrors = append(typeErrors, err)
} }
checker.Importer = p
// Do typechecking of the package. // Do typechecking of the package.
checker.Importer = p
packageName := p.ImportPath packageName := p.ImportPath
if p == p.program.MainPkg() { if p == p.program.MainPkg() {
if p.Name != "main" { if p.Name != "main" {
@@ -403,12 +382,6 @@ func (p *Package) Check() error {
return Errors{p, typeErrors} return Errors{p, typeErrors}
} }
p.Pkg = typesPkg p.Pkg = typesPkg
p.extractEmbedLines(checker.Error)
if len(typeErrors) != 0 {
return Errors{p, typeErrors}
}
return nil return nil
} }
@@ -467,249 +440,6 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
return files, nil return files, nil
} }
// extractEmbedLines finds all //go:embed lines in the package and matches them
// against EmbedFiles from `go list`.
func (p *Package) extractEmbedLines(addError func(error)) {
for _, file := range p.Files {
// Check for an `import "embed"` line at the start of the file.
// //go:embed lines are only valid if the given file itself imports the
// embed package. It is not valid if it is only imported in a separate
// Go file.
hasEmbed := false
for _, importSpec := range file.Imports {
if importSpec.Path.Value == `"embed"` {
hasEmbed = true
}
}
for _, decl := range file.Decls {
switch decl := decl.(type) {
case *ast.GenDecl:
if decl.Tok != token.VAR {
continue
}
for _, spec := range decl.Specs {
spec := spec.(*ast.ValueSpec)
var doc *ast.CommentGroup
if decl.Lparen == token.NoPos {
// Plain 'var' declaration, like:
// //go:embed hello.txt
// var hello string
doc = decl.Doc
} else {
// Bigger 'var' declaration like:
// var (
// //go:embed hello.txt
// hello string
// )
doc = spec.Doc
}
if doc == nil {
continue
}
// Look for //go:embed comments.
var allPatterns []string
for _, comment := range doc.List {
if comment.Text != "//go:embed" && !strings.HasPrefix(comment.Text, "//go:embed ") {
continue
}
if !hasEmbed {
addError(types.Error{
Fset: p.program.fset,
Pos: comment.Pos() + 2,
Msg: "//go:embed only allowed in Go files that import \"embed\"",
})
// Continue, because otherwise we might run into
// issues below.
continue
}
patterns, err := p.parseGoEmbed(comment.Text[len("//go:embed"):], comment.Slash)
if err != nil {
addError(err)
continue
}
if len(patterns) == 0 {
addError(types.Error{
Fset: p.program.fset,
Pos: comment.Pos() + 2,
Msg: "usage: //go:embed pattern...",
})
continue
}
for _, pattern := range patterns {
// Check that the pattern is well-formed.
// It must be valid: the Go toolchain has already
// checked for invalid patterns. But let's check
// anyway to be sure.
if _, err := path.Match(pattern, ""); err != nil {
addError(types.Error{
Fset: p.program.fset,
Pos: comment.Pos(),
Msg: "invalid pattern syntax",
})
continue
}
allPatterns = append(allPatterns, pattern)
}
}
if len(allPatterns) != 0 {
// This is a //go:embed global. Do a few more checks.
if len(spec.Names) != 1 {
addError(types.Error{
Fset: p.program.fset,
Pos: spec.Names[1].NamePos,
Msg: "//go:embed cannot apply to multiple vars",
})
}
if spec.Values != nil {
addError(types.Error{
Fset: p.program.fset,
Pos: spec.Values[0].Pos(),
Msg: "//go:embed cannot apply to var with initializer",
})
}
globalName := spec.Names[0].Name
globalType := p.Pkg.Scope().Lookup(globalName).Type()
valid, byteSlice := isValidEmbedType(globalType)
if !valid {
addError(types.Error{
Fset: p.program.fset,
Pos: spec.Type.Pos(),
Msg: "//go:embed cannot apply to var of type " + globalType.String(),
})
}
// Match all //go:embed patterns against the embed files
// provided by `go list`.
for _, name := range p.EmbedFiles {
for _, pattern := range allPatterns {
if matchPattern(pattern, name) {
p.EmbedGlobals[globalName] = append(p.EmbedGlobals[globalName], &EmbedFile{
Name: name,
NeedsData: byteSlice,
})
break
}
}
}
}
}
}
}
}
}
// matchPattern returns true if (and only if) the given pattern would match the
// filename. The pattern could also match a parent directory of name, in which
// case hidden files do not match.
func matchPattern(pattern, name string) bool {
// Match this file.
matched, _ := path.Match(pattern, name)
if matched {
return true
}
// Match parent directories.
dir := name
for {
dir, _ = path.Split(dir)
if dir == "" {
return false
}
dir = path.Clean(dir)
if matched, _ := path.Match(pattern, dir); matched {
// Pattern matches the directory.
suffix := name[len(dir):]
if strings.Contains(suffix, "/_") || strings.Contains(suffix, "/.") {
// Pattern matches a hidden file.
// Hidden files are included when listed directly as a
// pattern, but not when they are part of a directory tree.
// Source:
// > If a pattern names a directory, all files in the
// > subtree rooted at that directory are embedded
// > (recursively), except that files with names beginning
// > with . or _ are excluded.
return false
}
return true
}
}
}
// parseGoEmbed is like strings.Fields but for a //go:embed line. It parses
// regular fields and quoted fields (that may contain spaces).
func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, err error) {
args = strings.TrimSpace(args)
initialLen := len(args)
for args != "" {
patternPos := pos + token.Pos(initialLen-len(args))
switch args[0] {
case '`', '"', '\\':
// Parse the next pattern using the Go scanner.
// This is perhaps a bit overkill, but it does correctly implement
// parsing of the various Go strings.
var sc scanner.Scanner
fset := &token.FileSet{}
file := fset.AddFile("", 0, len(args))
sc.Init(file, []byte(args), nil, 0)
_, tok, lit := sc.Scan()
if tok != token.STRING || sc.ErrorCount != 0 {
// Calculate start of token
return nil, types.Error{
Fset: p.program.fset,
Pos: patternPos,
Msg: "invalid quoted string in //go:embed",
}
}
pattern := constant.StringVal(constant.MakeFromLiteral(lit, tok, 0))
patterns = append(patterns, pattern)
args = strings.TrimLeftFunc(args[len(lit):], unicode.IsSpace)
default:
// The value is just a regular value.
// Split it at the first white space.
index := strings.IndexFunc(args, unicode.IsSpace)
if index < 0 {
index = len(args)
}
pattern := args[:index]
patterns = append(patterns, pattern)
args = strings.TrimLeftFunc(args[len(pattern):], unicode.IsSpace)
}
if _, err := path.Match(patterns[len(patterns)-1], ""); err != nil {
return nil, types.Error{
Fset: p.program.fset,
Pos: patternPos,
Msg: "invalid pattern syntax",
}
}
}
return patterns, nil
}
// isValidEmbedType returns whether the given Go type can be used as a
// //go:embed type. This is only true for embed.FS, strings, and byte slices.
// The second return value indicates that this is a byte slice, and therefore
// the contents of the file needs to be passed to the compiler.
func isValidEmbedType(typ types.Type) (valid, byteSlice bool) {
if typ.Underlying() == types.Typ[types.String] {
// string type
return true, false
}
if sliceType, ok := typ.Underlying().(*types.Slice); ok {
if elemType, ok := sliceType.Elem().Underlying().(*types.Basic); ok && elemType.Kind() == types.Byte {
// byte slice type
return true, true
}
}
if namedType, ok := typ.(*types.Named); ok && namedType.String() == "embed.FS" {
// embed.FS type
return true, false
}
return false, false
}
// Import implements types.Importer. It loads and parses packages it encounters // Import implements types.Importer. It loads and parses packages it encounters
// along the way, if needed. // along the way, if needed.
func (p *Package) Import(to string) (*types.Package, error) { func (p *Package) Import(to string) (*types.Package, error) {
-18
View File
@@ -1,18 +0,0 @@
//go:build go1.18
// +build go1.18
package loader
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
// support.
import (
"go/ast"
"go/types"
)
func init() {
addInstances = func(info *types.Info) {
info.Instances = make(map[*ast.Ident]types.Instance)
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ 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) prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
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 -239
View File
@@ -1,9 +1,7 @@
package main package main
import ( import (
"bufio"
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"flag" "flag"
@@ -201,26 +199,8 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
return false, err return false, err
} }
// Pass test flags to the test binary.
var flags []string
if testVerbose {
flags = append(flags, "-test.v")
}
if testShort {
flags = append(flags, "-test.short")
}
if testRunRegexp != "" {
flags = append(flags, "-test.run="+testRunRegexp)
}
if testBenchRegexp != "" {
flags = append(flags, "-test.bench="+testBenchRegexp)
}
if testBenchTime != "" {
flags = append(flags, "-test.benchtime="+testBenchTime)
}
passed := false passed := false
err = buildAndRun(pkgName, config, os.Stdout, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
if testCompileOnly || outpath != "" { if testCompileOnly || outpath != "" {
// Write test binary to the specified file name. // Write test binary to the specified file name.
if outpath == "" { if outpath == "" {
@@ -236,52 +216,27 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
return nil return nil
} }
// Tests are always run in the package directory.
cmd.Dir = result.MainDir
// Wasmtime needs a few extra flags to work.
if config.EmulatorName() == "wasmtime" {
// Add directories to the module root, but skip the current working
// directory which is already added by buildAndRun.
dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
var args []string
for _, d := range dirs[1:] {
args = append(args, "--dir="+d)
}
// create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR
tmpdir, err := ioutil.TempDir("", "tinygotmp")
if err != nil {
return fmt.Errorf("failed to create temporary directory: %w", err)
}
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
// TODO: add option to not delete temp dir for debugging?
defer os.RemoveAll(tmpdir)
// Insert new argments at the front of the command line argments.
args = append(args, cmd.Args[1:]...)
cmd.Args = append(cmd.Args[:1:1], args...)
}
// Run the test. // Run the test.
config.Options.Semaphore <- struct{}{}
defer func() {
<-config.Options.Semaphore
}()
start := time.Now() start := time.Now()
err = cmd.Run() var err error
passed, err = runPackageTest(config, stdout, stderr, result, testVerbose, testShort, testRunRegexp, testBenchRegexp, testBenchTime)
if err != nil {
return err
}
duration := time.Since(start) duration := time.Since(start)
// Print the result. // Print the result.
importPath := strings.TrimSuffix(result.ImportPath, ".test") importPath := strings.TrimSuffix(result.ImportPath, ".test")
passed = err == nil
if passed { if passed {
fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds()) fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
} else { } else {
fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds()) fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
} }
if _, ok := err.(*exec.ExitError); ok { return nil
// Binary exited with a non-zero exit code, which means the test
// failed.
return nil
}
return err
}) })
if err, ok := err.(loader.NoTestFilesError); ok { if err, ok := err.(loader.NoTestFilesError); ok {
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath) fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath)
@@ -304,6 +259,81 @@ func dirsToModuleRoot(maindir, modroot string) []string {
return dirs return dirs
} }
// runPackageTest runs a test binary that was previously built. The return
// values are whether the test passed and any errors encountered while trying to
// run the binary.
func runPackageTest(config *compileopts.Config, stdout, stderr io.Writer, result builder.BuildResult, testVerbose, testShort bool, testRunRegexp string, testBenchRegexp string, testBenchTime string) (bool, error) {
var cmd *exec.Cmd
emulator := config.Emulator()
if len(emulator) == 0 {
// Run directly.
var flags []string
if testVerbose {
flags = append(flags, "-test.v")
}
if testShort {
flags = append(flags, "-test.short")
}
if testRunRegexp != "" {
flags = append(flags, "-test.run="+testRunRegexp)
}
if testBenchRegexp != "" {
flags = append(flags, "-test.bench="+testBenchRegexp)
}
if testBenchTime != "" {
flags = append(flags, "-test.benchtime="+testBenchTime)
}
cmd = executeCommand(config.Options, result.Binary, flags...)
} else {
// Run in an emulator.
args := append(emulator[1:], result.Binary)
if emulator[0] == "wasmtime" {
// create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR
tmpdir, err := ioutil.TempDir("", "tinygotmp")
if err != nil {
return false, &commandError{"failed to create temporary directory", "tinygotmp", err}
}
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
// TODO: add option to not delete temp dir for debugging?
defer os.RemoveAll(tmpdir)
// allow reading from directories up to module root
for _, d := range dirsToModuleRoot(result.MainDir, result.ModuleRoot) {
args = append(args, "--dir="+d)
}
// mark end of wasmtime arguments and start of program ones: --
args = append(args, "--")
if testVerbose {
args = append(args, "-test.v")
}
if testShort {
args = append(args, "-test.short")
}
if testRunRegexp != "" {
args = append(args, "-test.run="+testRunRegexp)
}
if testBenchRegexp != "" {
args = append(args, "-test.bench="+testBenchRegexp)
}
}
cmd = executeCommand(config.Options, emulator[0], args...)
}
cmd.Dir = result.MainDir
cmd.Stdout = stdout
cmd.Stderr = stderr
err := cmd.Run()
if err != nil {
if _, ok := err.(*exec.ExitError); ok {
// Binary exited with a non-zero exit code, which means the test
// failed.
return false, nil
}
return false, &commandError{"failed to run compiled binary", result.Binary, err}
}
return true, nil
}
// Flash builds and flashes the built binary to the given serial port. // Flash builds and flashes the built binary to the given serial port.
func Flash(pkgName, port string, options *compileopts.Options) error { func Flash(pkgName, port string, options *compileopts.Options) error {
config, err := builder.NewConfig(options) config, err := builder.NewConfig(options)
@@ -423,11 +453,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
if err != nil { if err != nil {
return err return err
} }
exit := " reset exit" args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
if config.Target.OpenOCDVerify != nil && *config.Target.OpenOCDVerify {
exit = " verify" + exit
}
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+exit)
cmd := executeCommand(config.Options, "openocd", args...) cmd := executeCommand(config.Options, "openocd", args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -484,19 +510,18 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
return err return err
} }
format, fileExt := config.EmulatorFormat() return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
// Find a good way to run GDB. // Find a good way to run GDB.
gdbInterface, openocdInterface := config.Programmer() gdbInterface, openocdInterface := config.Programmer()
switch gdbInterface { switch gdbInterface {
case "msd", "command", "": case "msd", "command", "":
emulator := config.EmulatorName() emulator := config.Emulator()
if emulator != "" { if len(emulator) != 0 {
if emulator == "mgba" { if emulator[0] == "mgba" {
gdbInterface = "mgba" gdbInterface = "mgba"
} else if emulator == "simavr" { } else if emulator[0] == "simavr" {
gdbInterface = "simavr" gdbInterface = "simavr"
} else if strings.HasPrefix(emulator, "qemu-system-") { } else if strings.HasPrefix(emulator[0], "qemu-system-") {
gdbInterface = "qemu" gdbInterface = "qemu"
} else { } else {
// Assume QEMU as an emulator. // Assume QEMU as an emulator.
@@ -515,10 +540,6 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
port := "" port := ""
var gdbCommands []string var gdbCommands []string
var daemon *exec.Cmd var daemon *exec.Cmd
emulator, err := config.Emulator(format, result.Binary)
if err != nil {
return err
}
switch gdbInterface { switch gdbInterface {
case "native": case "native":
// Run GDB directly. // Run GDB directly.
@@ -568,29 +589,33 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
} }
case "qemu": case "qemu":
port = ":1234" port = ":1234"
emulator := config.Emulator()
// Run in an emulator. // Run in an emulator.
args := append(emulator[1:], "-s", "-S") args := append(emulator[1:], result.Binary, "-s", "-S")
daemon = executeCommand(config.Options, emulator[0], args...) daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "qemu-user": case "qemu-user":
port = ":1234" port = ":1234"
emulator := config.Emulator()
// Run in an emulator. // Run in an emulator.
args := append(emulator[1:], "-g", "1234") args := append(emulator[1:], "-g", "1234", result.Binary)
daemon = executeCommand(config.Options, emulator[0], args...) daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "mgba": case "mgba":
port = ":2345" port = ":2345"
emulator := config.Emulator()
// Run in an emulator. // Run in an emulator.
args := append(emulator[1:], "-g") args := append(emulator[1:], result.Binary, "-g")
daemon = executeCommand(config.Options, emulator[0], args...) daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "simavr": case "simavr":
port = ":1234" port = ":1234"
emulator := config.Emulator()
// Run in an emulator. // Run in an emulator.
args := append(emulator[1:], "-g") args := append(emulator[1:], "-g", result.Binary)
daemon = executeCommand(config.Options, emulator[0], args...) daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
@@ -635,7 +660,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
// Construct and execute a gdb or lldb command. // Construct and execute a gdb or lldb command.
// By default: gdb -ex run <binary> // By default: gdb -ex run <binary>
// Exit the debugger with Ctrl-D. // Exit the debugger with Ctrl-D.
params := []string{result.Executable} params := []string{result.Binary}
switch debugger { switch debugger {
case "gdb": case "gdb":
if port != "" { if port != "" {
@@ -667,9 +692,9 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err = cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return &commandError{"failed to run " + cmdName + " with", result.Executable, err} return &commandError{"failed to run " + cmdName + " with", result.Binary, err}
} }
return nil return nil
}) })
@@ -679,134 +704,44 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
// the options, it will run the program directly on the host or will run it in // the options, it will run the program directly on the host or will run it in
// an emulator. For example, -target=wasm will cause the binary to be run inside // an emulator. For example, -target=wasm will cause the binary to be run inside
// of a WebAssembly VM. // of a WebAssembly VM.
func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error { func Run(pkgName string, options *compileopts.Options) error {
config, err := builder.NewConfig(options) config, err := builder.NewConfig(options)
if err != nil { if err != nil {
return err return err
} }
return buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
return cmd.Run() emulator := config.Emulator()
}) if len(emulator) == 0 {
} // Run directly.
cmd := executeCommand(config.Options, result.Binary)
// buildAndRun builds and runs the given program, writing output to stdout and cmd.Stdout = os.Stdout
// errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and cmd.Stderr = os.Stderr
// passes command line arguments and evironment variables in a way appropriate err := cmd.Run()
// for the given emulator.
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) error {
// Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment,
// we need to pass command line arguments and environment variables through
// global variables (built into the binary directly) instead of the
// conventional way.
needsEnvInVars := config.GOOS() == "js"
for _, tag := range config.BuildTags() {
if tag == "baremetal" {
needsEnvInVars = true
}
}
var args, env []string
if needsEnvInVars {
runtimeGlobals := make(map[string]string)
if len(cmdArgs) != 0 {
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
}
if len(environmentVars) != 0 {
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
}
if len(runtimeGlobals) != 0 {
// This sets the global variables like they would be set with
// `-ldflags="-X=runtime.osArgs=first\x00second`.
// The runtime package has two variables (osArgs and osEnv) that are
// both strings, from which the parameters and environment variables
// are read.
config.Options.GlobalValues = map[string]map[string]string{
"runtime": runtimeGlobals,
}
}
} else if config.EmulatorName() == "wasmtime" {
// Wasmtime needs some special flags to pass environment variables
// and allow reading from the current directory.
args = append(args, "--dir=.")
for _, v := range environmentVars {
args = append(args, "--env", v)
}
if len(cmdArgs) != 0 {
// mark end of wasmtime arguments and start of program ones: --
args = append(args, "--")
args = append(args, cmdArgs...)
}
} else {
// Pass environment variables and command line parameters as usual.
// This also works on qemu-aarch64 etc.
args = cmdArgs
env = environmentVars
}
format, fileExt := config.EmulatorFormat()
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
// If needed, set a timeout on the command. This is done in tests so
// they don't waste resources on a stalled test.
var ctx context.Context
if timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(context.Background(), timeout)
defer cancel()
}
// Set up the command.
var name string
if config.Target.Emulator == "" {
name = result.Binary
} else {
emulator, err := config.Emulator(format, result.Binary)
if err != nil { if err != nil {
return err if err, ok := err.(*exec.ExitError); ok && err.Exited() {
// Workaround for QEMU which always exits with an error.
return nil
}
return &commandError{"failed to run compiled binary", result.Binary, err}
} }
name = emulator[0] return nil
emuArgs := append([]string(nil), emulator[1:]...)
args = append(emuArgs, args...)
}
var cmd *exec.Cmd
if ctx != nil {
cmd = exec.CommandContext(ctx, name, args...)
} else { } else {
cmd = exec.Command(name, args...) // Run in an emulator.
} args := append(emulator[1:], result.Binary)
cmd.Env = env cmd := executeCommand(config.Options, emulator[0], args...)
cmd.Stdout = os.Stdout
// Configure stdout/stderr. The stdout may go to a buffer, not a real cmd.Stderr = os.Stderr
// stdout. err := cmd.Run()
cmd.Stdout = stdout if err != nil {
cmd.Stderr = os.Stderr if err, ok := err.(*exec.ExitError); ok && err.Exited() {
if config.EmulatorName() == "simavr" { // Workaround for QEMU which always exits with an error.
cmd.Stdout = nil // don't print initial load commands return nil
cmd.Stderr = stdout }
} return &commandError{"failed to run emulator with", result.Binary, err}
// If this is a test, reserve CPU time for it so that increased
// parallelism doesn't blow up memory usage. If this isn't a test but
// simply `tinygo run`, then it is practically a no-op.
config.Options.Semaphore <- struct{}{}
defer func() {
<-config.Options.Semaphore
}()
// Run binary.
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(cmd.Path, cmd.Args...)
}
err := run(cmd, result)
if err != nil {
if ctx != nil && ctx.Err() == context.DeadlineExceeded {
stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout)))
err = ctx.Err()
} }
return &commandError{"failed to run compiled binary", result.Binary, err} return nil
} }
return nil
}) })
} }
@@ -1253,35 +1188,6 @@ func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
return map[string]map[string]string(globalVarValues), nil return map[string]map[string]string(globalVarValues), nil
} }
// getListOfPackages returns a standard list of packages for a given list that might
// include wildards using `go list`.
// For example [./...] => ["pkg1", "pkg1/pkg12", "pkg2"]
func getListOfPackages(pkgs []string, options *compileopts.Options) ([]string, error) {
config, err := builder.NewConfig(options)
if err != nil {
return nil, err
}
cmd, err := loader.List(config, nil, pkgs)
if err != nil {
return nil, fmt.Errorf("failed to run `go list`: %w", err)
}
outputBuf := bytes.NewBuffer(nil)
cmd.Stdout = outputBuf
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return nil, err
}
var pkgNames []string
sc := bufio.NewScanner(outputBuf)
for sc.Scan() {
pkgNames = append(pkgNames, sc.Text())
}
return pkgNames, nil
}
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.") fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
@@ -1501,13 +1407,13 @@ func main() {
handleCompilerError(err) handleCompilerError(err)
} }
case "run": case "run":
if flag.NArg() < 1 { if flag.NArg() != 1 {
fmt.Fprintln(os.Stderr, "No package specified.") fmt.Fprintln(os.Stderr, "No package specified.")
usage(command) usage(command)
os.Exit(1) os.Exit(1)
} }
pkgName := filepath.ToSlash(flag.Arg(0)) pkgName := filepath.ToSlash(flag.Arg(0))
err := Run(pkgName, options, flag.Args()[1:]) err := Run(pkgName, options)
handleCompilerError(err) handleCompilerError(err)
case "test": case "test":
var pkgNames []string var pkgNames []string
@@ -1517,21 +1423,14 @@ func main() {
if len(pkgNames) == 0 { if len(pkgNames) == 0 {
pkgNames = []string{"."} pkgNames = []string{"."}
} }
if outpath != "" && len(pkgNames) > 1 {
explicitPkgNames, err := getListOfPackages(pkgNames, options)
if err != nil {
fmt.Printf("cannot resolve packages: %v\n", err)
os.Exit(1)
}
if outpath != "" && len(explicitPkgNames) > 1 {
fmt.Println("cannot use -o flag with multiple packages") fmt.Println("cannot use -o flag with multiple packages")
os.Exit(1) os.Exit(1)
} }
fail := make(chan struct{}, 1) fail := make(chan struct{}, 1)
var wg sync.WaitGroup var wg sync.WaitGroup
bufs := make([]testOutputBuf, len(explicitPkgNames)) bufs := make([]testOutputBuf, len(pkgNames))
for i := range bufs { for i := range bufs {
bufs[i].done = make(chan struct{}) bufs[i].done = make(chan struct{})
} }
@@ -1557,7 +1456,7 @@ func main() {
// Build and run the tests concurrently. // Build and run the tests concurrently.
// This uses an additional semaphore to reduce the memory usage. // This uses an additional semaphore to reduce the memory usage.
testSema := make(chan struct{}, cap(options.Semaphore)) testSema := make(chan struct{}, cap(options.Semaphore))
for i, pkgName := range explicitPkgNames { for i, pkgName := range pkgNames {
pkgName := pkgName pkgName := pkgName
buf := &bufs[i] buf := &bufs[i]
testSema <- struct{}{} testSema <- struct{}{}
@@ -1610,7 +1509,7 @@ func main() {
os.Exit(1) os.Exit(1)
return return
} }
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" { if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
// This doesn't look like a regular target file, but rather like // This doesn't look like a regular target file, but rather like
// a parent target (such as targets/cortex-m.json). // a parent target (such as targets/cortex-m.json).
continue continue
+143 -90
View File
@@ -6,6 +6,7 @@ package main
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
@@ -13,7 +14,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"reflect" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"strings" "strings"
@@ -49,7 +50,6 @@ func TestBuild(t *testing.T) {
"calls.go", "calls.go",
"cgo/", "cgo/",
"channel.go", "channel.go",
"embed/",
"float.go", "float.go",
"gc.go", "gc.go",
"goroutines.go", "goroutines.go",
@@ -66,6 +66,7 @@ func TestBuild(t *testing.T) {
"stdlib.go", "stdlib.go",
"string.go", "string.go",
"structs.go", "structs.go",
"testing.go",
"zeroalloc.go", "zeroalloc.go",
} }
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT")) _, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
@@ -75,12 +76,6 @@ func TestBuild(t *testing.T) {
if minor >= 17 { if minor >= 17 {
tests = append(tests, "go1.17.go") tests = append(tests, "go1.17.go")
} }
if minor >= 18 {
tests = append(tests, "generics.go")
tests = append(tests, "testing_go118.go")
} else {
tests = append(tests, "testing.go")
}
if *testTarget != "" { if *testTarget != "" {
// This makes it possible to run one specific test (instead of all), // This makes it possible to run one specific test (instead of all),
@@ -149,7 +144,46 @@ func TestBuild(t *testing.T) {
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0> // LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
// This bug is non-deterministic. // This bug is non-deterministic.
t.Skip("skipped due to non-deterministic backend bugs") t.Skip("skipped due to non-deterministic backend bugs")
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
var avrTests []string
for _, t := range tests {
switch t {
case "atomic.go":
// Requires GCC 11.2.0 or above for interface comparison.
// https://github.com/gcc-mirror/gcc/commit/f30dd607669212de135dec1f1d8a93b8954c327c
case "reflect.go":
// Reflect tests do not work due to type code issues.
case "gc.go":
// Does not pass due to high mark false positive rate.
case "json.go", "stdlib.go", "testing.go":
// Breaks interp.
case "map.go":
// Reflect size calculation crashes.
case "binop.go":
// Interface comparison results are inverted.
case "channel.go":
// Freezes after recv from closed channel.
case "float.go", "math.go", "print.go":
// Stuck in runtime.printfloat64.
case "interface.go":
// Several comparison tests fail.
case "cgo/":
// CGo does not work on AVR.
default:
avrTests = append(avrTests, t)
}
}
runPlatTests(optionsFromTarget("simavr", sema), avrTests, t)
}) })
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
@@ -181,44 +215,13 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
} }
for _, name := range tests { for _, name := range tests {
if options.Target == "simavr" {
// Not all tests are currently supported on AVR.
// Skip the ones that aren't.
switch name {
case "reflect.go":
// Reflect tests do not work due to type code issues.
continue
case "gc.go":
// Does not pass due to high mark false positive rate.
continue
case "json.go", "stdlib.go", "testing.go", "testing_go118.go":
// Breaks interp.
continue
case "channel.go":
// Freezes after recv from closed channel.
continue
case "math.go":
// Stuck somewhere, not sure what's happening.
continue
case "cgo/":
// CGo does not work on AVR.
continue
default:
}
}
name := name // redefine to avoid race condition name := name // redefine to avoid race condition
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
t.Parallel() t.Parallel()
runTest(name, options, t, nil, nil) runTest(name, options, t, nil, nil)
}) })
} }
if !strings.HasPrefix(spec.Emulator, "simavr ") { if len(spec.Emulator) == 0 || spec.Emulator[0] != "simavr" {
t.Run("env.go", func(t *testing.T) { t.Run("env.go", func(t *testing.T) {
t.Parallel() t.Parallel()
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"}) runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
@@ -252,8 +255,8 @@ func emuCheck(t *testing.T, options compileopts.Options) {
if err != nil { if err != nil {
t.Fatal("failed to load target spec:", err) t.Fatal("failed to load target spec:", err)
} }
if spec.Emulator != "" { if len(spec.Emulator) != 0 {
_, err := exec.LookPath(strings.SplitN(spec.Emulator, " ", 2)[0]) _, err := exec.LookPath(spec.Emulator[0])
if err != nil { if err != nil {
if errors.Is(err, exec.ErrNotFound) { if errors.Is(err, exec.ErrNotFound) {
t.Skipf("emulator not installed: %q", spec.Emulator[0]) t.Skipf("emulator not installed: %q", spec.Emulator[0])
@@ -317,27 +320,117 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
t.Fatal("could not read expected output file:", err) t.Fatal("could not read expected output file:", err)
} }
config, err := builder.NewConfig(&options) // Create a temporary directory for test output files.
tmpdir := t.TempDir()
// Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment,
// we need to pass command line arguments and environment variables through
// global variables (built into the binary directly) instead of the
// conventional way.
spec, err := compileopts.LoadTarget(&options)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal("failed to load target spec:", err)
}
needsEnvInVars := spec.GOOS == "js"
for _, tag := range spec.BuildTags {
if tag == "baremetal" {
needsEnvInVars = true
}
}
if needsEnvInVars {
runtimeGlobals := make(map[string]string)
if len(cmdArgs) != 0 {
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
}
if len(environmentVars) != 0 {
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
}
if len(runtimeGlobals) != 0 {
// This sets the global variables like they would be set with
// `-ldflags="-X=runtime.osArgs=first\x00second`.
// The runtime package has two variables (osArgs and osEnv) that are
// both strings, from which the parameters and environment variables
// are read.
options.GlobalValues = map[string]map[string]string{
"runtime": runtimeGlobals,
}
}
} }
// Build the test binary. // Build the test binary.
stdout := &bytes.Buffer{} binary := filepath.Join(tmpdir, "test")
err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error { if spec.GOOS == "windows" {
return cmd.Run() binary += ".exe"
}) }
err = Build("./"+path, binary, &options)
if err != nil { if err != nil {
printCompilerError(t.Log, err) printCompilerError(t.Log, err)
t.Fail() t.Fail()
return return
} }
// Reserve CPU time for the test to run.
// This attempts to ensure that the test is not CPU-starved.
options.Semaphore <- struct{}{}
defer func() { <-options.Semaphore }()
// Create the test command, taking care of emulators etc.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
var cmd *exec.Cmd
// make sure any special vars in the emulator definition are rewritten
config := compileopts.Config{Target: spec}
emulator := config.Emulator()
if len(emulator) == 0 {
cmd = exec.CommandContext(ctx, binary)
} else {
args := append(emulator[1:], binary)
cmd = exec.CommandContext(ctx, emulator[0], args...)
}
if len(emulator) != 0 && emulator[0] == "wasmtime" {
// Allow reading from the current directory.
cmd.Args = append(cmd.Args, "--dir=.")
for _, v := range environmentVars {
cmd.Args = append(cmd.Args, "--env", v)
}
cmd.Args = append(cmd.Args, cmdArgs...)
} else {
if !needsEnvInVars {
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
cmd.Env = append(cmd.Env, environmentVars...)
}
}
// Run the test.
stdout := &bytes.Buffer{}
if len(emulator) != 0 && emulator[0] == "simavr" {
cmd.Stdout = os.Stderr
cmd.Stderr = stdout
} else {
cmd.Stdout = stdout
cmd.Stderr = os.Stderr
}
err = cmd.Start()
if err != nil {
t.Fatal("failed to start:", err)
}
err = cmd.Wait()
if cerr := ctx.Err(); cerr == context.DeadlineExceeded {
stdout.WriteString("--- test ran too long, terminating...\n")
err = cerr
}
// putchar() prints CRLF, convert it to LF. // putchar() prints CRLF, convert it to LF.
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1) actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
if config.EmulatorName() == "simavr" { if len(emulator) != 0 && emulator[0] == "simavr" {
// Strip simavr log formatting. // Strip simavr log formatting.
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1) actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1) actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
@@ -522,46 +615,6 @@ func ioLogger(t *testing.T, wg *sync.WaitGroup) io.WriteCloser {
return w return w
} }
func TestGetListOfPackages(t *testing.T) {
opts := optionsFromTarget("", sema)
tests := []struct {
pkgs []string
expectedPkgs []string
expectesError bool
}{
{
pkgs: []string{"./tests/testing/recurse/..."},
expectedPkgs: []string{
"github.com/tinygo-org/tinygo/tests/testing/recurse",
"github.com/tinygo-org/tinygo/tests/testing/recurse/subdir",
},
},
{
pkgs: []string{"./tests/testing/pass"},
expectedPkgs: []string{
"github.com/tinygo-org/tinygo/tests/testing/pass",
},
},
{
pkgs: []string{"./tests/testing"},
expectesError: true,
},
}
for _, test := range tests {
actualPkgs, err := getListOfPackages(test.pkgs, &opts)
if err != nil && !test.expectesError {
t.Errorf("unexpected error: %v", err)
} else if err == nil && test.expectesError {
t.Error("expected error, but got none")
}
if !reflect.DeepEqual(test.expectedPkgs, actualPkgs) {
t.Errorf("expected two slices to be equal, expected %v got %v", test.expectedPkgs, actualPkgs)
}
}
}
// This TestMain is necessary because TinyGo may also be invoked to run certain // This TestMain is necessary because TinyGo may also be invoked to run certain
// LLVM tools in a separate process. Not capturing these invocations would lead // LLVM tools in a separate process. Not capturing these invocations would lead
// to recursive tests. // to recursive tests.
+47 -21
View File
@@ -142,14 +142,18 @@ func (mpu *MPU_Type) Enable(enable bool) {
if enable { if enable {
mpu.CTRL.Set(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk) mpu.CTRL.Set(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk)
SystemControl.SHCSR.SetBits(SCB_SHCSR_MEMFAULTENA_Msk) SystemControl.SHCSR.SetBits(SCB_SHCSR_MEMFAULTENA_Msk)
arm.Asm("dsb 0xF") arm.AsmFull(`
arm.Asm("isb 0xF") dsb 0xF
isb 0xF
`, nil)
enableDcache(true) enableDcache(true)
enableIcache(true) enableIcache(true)
} else { } else {
enableIcache(false) enableIcache(false)
enableDcache(false) enableDcache(false)
arm.Asm("dmb 0xF") arm.AsmFull(`
dmb 0xF
`, nil)
SystemControl.SHCSR.ClearBits(SCB_SHCSR_MEMFAULTENA_Msk) SystemControl.SHCSR.ClearBits(SCB_SHCSR_MEMFAULTENA_Msk)
mpu.CTRL.ClearBits(MPU_CTRL_ENABLE_Msk) mpu.CTRL.ClearBits(MPU_CTRL_ENABLE_Msk)
} }
@@ -184,21 +188,31 @@ func (mpu *MPU_Type) SetRASR(size RegionSize, access AccessPerms, ext Extension,
func enableIcache(enable bool) { func enableIcache(enable bool) {
if enable != SystemControl.CCR.HasBits(SCB_CCR_IC_Msk) { if enable != SystemControl.CCR.HasBits(SCB_CCR_IC_Msk) {
if enable { if enable {
arm.Asm("dsb 0xF") arm.AsmFull(`
arm.Asm("isb 0xF") dsb 0xF
isb 0xF
`, nil)
SystemControl.ICIALLU.Set(0) SystemControl.ICIALLU.Set(0)
arm.Asm("dsb 0xF") arm.AsmFull(`
arm.Asm("isb 0xF") dsb 0xF
isb 0xF
`, nil)
SystemControl.CCR.SetBits(SCB_CCR_IC_Msk) SystemControl.CCR.SetBits(SCB_CCR_IC_Msk)
arm.Asm("dsb 0xF") arm.AsmFull(`
arm.Asm("isb 0xF") dsb 0xF
isb 0xF
`, nil)
} else { } else {
arm.Asm("dsb 0xF") arm.AsmFull(`
arm.Asm("isb 0xF") dsb 0xF
isb 0xF
`, nil)
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk) SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
SystemControl.ICIALLU.Set(0) SystemControl.ICIALLU.Set(0)
arm.Asm("dsb 0xF") arm.AsmFull(`
arm.Asm("isb 0xF") dsb 0xF
isb 0xF
`, nil)
} }
} }
} }
@@ -213,7 +227,9 @@ func enableDcache(enable bool) {
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) { if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
if enable { if enable {
SystemControl.CSSELR.Set(0) SystemControl.CSSELR.Set(0)
arm.Asm("dsb 0xF") arm.AsmFull(`
dsb 0xF
`, nil)
ccsidr := SystemControl.CCSIDR.Get() ccsidr := SystemControl.CCSIDR.Get()
sets := (ccsidr & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos sets := (ccsidr & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos
for sets != 0 { for sets != 0 {
@@ -226,15 +242,23 @@ func enableDcache(enable bool) {
} }
sets-- sets--
} }
arm.Asm("dsb 0xF") arm.AsmFull(`
dsb 0xF
`, nil)
SystemControl.CCR.SetBits(SCB_CCR_DC_Msk) SystemControl.CCR.SetBits(SCB_CCR_DC_Msk)
arm.Asm("dsb 0xF") arm.AsmFull(`
arm.Asm("isb 0xF") dsb 0xF
isb 0xF
`, nil)
} else { } else {
SystemControl.CSSELR.Set(0) SystemControl.CSSELR.Set(0)
arm.Asm("dsb 0xF") arm.AsmFull(`
dsb 0xF
`, nil)
SystemControl.CCR.ClearBits(SCB_CCR_DC_Msk) SystemControl.CCR.ClearBits(SCB_CCR_DC_Msk)
arm.Asm("dsb 0xF") arm.AsmFull(`
dsb 0xF
`, nil)
dcacheCcsidr.Set(SystemControl.CCSIDR.Get()) dcacheCcsidr.Set(SystemControl.CCSIDR.Get())
dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos) dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
for dcacheSets.Get() != 0 { for dcacheSets.Get() != 0 {
@@ -247,8 +271,10 @@ func enableDcache(enable bool) {
} }
dcacheSets.Set(dcacheSets.Get() - 1) dcacheSets.Set(dcacheSets.Get() - 1)
} }
arm.Asm("dsb 0xF") arm.AsmFull(`
arm.Asm("isb 0xF") dsb 0xF
isb 0xF
`, nil)
} }
} }
} }
-49
View File
@@ -1,49 +0,0 @@
// Reads multiple rp2040 ADC channels concurrently. Including the internal temperature sensor
package main
import (
"fmt"
"machine"
"time"
)
type celsius float32
func (c celsius) String() string {
return fmt.Sprintf("%4.1f℃", c)
}
// rp2040 ADC is 12 bits. Reading are shifted <<4 to fill the 16-bit range.
var adcReading [3]uint16
func readADC(a machine.ADC, w time.Duration, i int) {
for {
adcReading[i] = a.Get()
time.Sleep(w)
}
}
func main() {
machine.InitADC()
a0 := machine.ADC{machine.ADC0} // GPIO26 input
a1 := machine.ADC{machine.ADC1} // GPIO27 input
a2 := machine.ADC{machine.ADC2} // GPIO28 input
t := machine.ADC_TEMP_SENSOR // Internal Temperature sensor
// Configure sets the GPIOs to PinAnalog mode
a0.Configure(machine.ADCConfig{})
a1.Configure(machine.ADCConfig{})
a2.Configure(machine.ADCConfig{})
// Configure powers on the temperature sensor
t.Configure(machine.ADCConfig{})
// Safe to read concurrently
go readADC(a0, 10*time.Millisecond, 0)
go readADC(a1, 17*time.Millisecond, 1)
go readADC(a2, 29*time.Millisecond, 2)
for {
fmt.Printf("ADC0: %5d ADC1: %5d ADC2: %5d Temp: %v\n\r", adcReading[0], adcReading[1], adcReading[2], celsius(float32(t.ReadTemperature())/1000))
time.Sleep(1000 * time.Millisecond)
}
}
-21
View File
@@ -1,21 +0,0 @@
package main
import (
"machine"
"machine/usb/hid/keyboard"
"time"
)
func main() {
button := machine.BUTTON
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
kb := keyboard.New()
for {
if !button.Get() {
kb.Write([]byte("tinygo"))
time.Sleep(200 * time.Millisecond)
}
}
}
-37
View File
@@ -1,37 +0,0 @@
package main
import (
"machine"
"machine/usb/hid/mouse"
"time"
)
func main() {
button := machine.BUTTON
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
mouse := mouse.New()
for {
if !button.Get() {
for j := 0; j < 5; j++ {
for i := 0; i < 100; i++ {
mouse.Move(1, 0)
time.Sleep(1 * time.Millisecond)
}
for i := 0; i < 100; i++ {
mouse.Move(0, 1)
time.Sleep(1 * time.Millisecond)
}
for i := 0; i < 100; i++ {
mouse.Move(-1, -1)
time.Sleep(1 * time.Millisecond)
}
}
time.Sleep(100 * time.Millisecond)
}
}
}
-54
View File
@@ -1,54 +0,0 @@
package main
import (
"fmt"
"machine"
"machine/usb/midi"
"time"
)
func main() {
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
button := machine.BUTTON
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
m := midi.New()
m.SetCallback(func(b []byte) {
led.Set(!led.Get())
fmt.Printf("% X\r\n", b)
m.Write(b)
})
prev := true
chords := []struct {
name string
keys []byte
}{
{name: "C ", keys: []byte{60, 64, 67}},
{name: "G ", keys: []byte{55, 59, 62}},
{name: "Am", keys: []byte{57, 60, 64}},
{name: "F ", keys: []byte{53, 57, 60}},
}
index := 0
for {
current := button.Get()
if prev != current {
led.Set(current)
if current {
for _, c := range chords[index].keys {
m.Write([]byte{0x08, 0x80, c, 0x40})
}
index = (index + 1) % len(chords)
} else {
for _, c := range chords[index].keys {
m.Write([]byte{0x09, 0x90, c, 0x40})
}
}
prev = current
}
time.Sleep(10 * time.Millisecond)
}
}
-133
View File
@@ -1,133 +0,0 @@
// Package fuzz is a shim to allow compilation against Go 1.18.
// It only defines a single type to work with testing/internal/testdeps,
// and hide all the other new dependencies from this package.
package fuzz
import (
"context"
"errors"
"io"
"reflect"
"time"
)
// CorpusEntry represents an individual input for fuzzing.
//
// We must use an equivalent type in the testing and testing/internal/testdeps
// packages, but testing can't import this package directly, and we don't want
// to export this type from testing. Instead, we use the same struct type and
// use a type alias (not a defined type) for convenience.
type CorpusEntry = struct {
Parent string
// Path is the path of the corpus file, if the entry was loaded from disk.
// For other entries, including seed values provided by f.Add, Path is the
// name of the test, e.g. seed#0 or its hash.
Path string
// Data is the raw input data. Data should only be populated for seed
// values. For on-disk corpus files, Data will be nil, as it will be loaded
// from disk using Path.
Data []byte
// Values is the unmarshaled values from a corpus file.
Values []any
Generation int
// IsSeed indicates whether this entry is part of the seed corpus.
IsSeed bool
}
// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing.
// The zero value is valid for each field unless specified otherwise.
type CoordinateFuzzingOpts struct {
// Log is a writer for logging progress messages and warnings.
// If nil, io.Discard will be used instead.
Log io.Writer
// Timeout is the amount of wall clock time to spend fuzzing after the corpus
// has loaded. If zero, there will be no time limit.
Timeout time.Duration
// Limit is the number of random values to generate and test. If zero,
// there will be no limit on the number of generated values.
Limit int64
// MinimizeTimeout is the amount of wall clock time to spend minimizing
// after discovering a crasher. If zero, there will be no time limit. If
// MinimizeTimeout and MinimizeLimit are both zero, then minimization will
// be disabled.
MinimizeTimeout time.Duration
// MinimizeLimit is the maximum number of calls to the fuzz function to be
// made while minimizing after finding a crash. If zero, there will be no
// limit. Calls to the fuzz function made when minimizing also count toward
// Limit. If MinimizeTimeout and MinimizeLimit are both zero, then
// minimization will be disabled.
MinimizeLimit int64
// parallel is the number of worker processes to run in parallel. If zero,
// CoordinateFuzzing will run GOMAXPROCS workers.
Parallel int
// Seed is a list of seed values added by the fuzz target with testing.F.Add
// and in testdata.
Seed []CorpusEntry
// Types is the list of types which make up a corpus entry.
// Types must be set and must match values in Seed.
Types []reflect.Type
// CorpusDir is a directory where files containing values that crash the
// code being tested may be written. CorpusDir must be set.
CorpusDir string
// CacheDir is a directory containing additional "interesting" values.
// The fuzzer may derive new values from these, and may write new values here.
CacheDir string
}
// CoordinateFuzzing creates several worker processes and communicates with
// them to test random inputs that could trigger crashes and expose bugs.
// The worker processes run the same binary in the same directory with the
// same environment variables as the coordinator process. Workers also run
// with the same arguments as the coordinator, except with the -test.fuzzworker
// flag prepended to the argument list.
//
// If a crash occurs, the function will return an error containing information
// about the crash, which can be reported to the user.
func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) {
return errors.New("not implemented")
}
// ReadCorpus reads the corpus from the provided dir. The returned corpus
// entries are guaranteed to match the given types. Any malformed files will
// be saved in a MalformedCorpusError and returned, along with the most recent
// error.
func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) {
return nil, errors.New("not implemented")
}
// CheckCorpus verifies that the types in vals match the expected types
// provided.
func CheckCorpus(vals []any, types []reflect.Type) error {
return errors.New("not implemented")
}
func ResetCoverage() {}
func SnapshotCoverage() {}
// RunFuzzWorker is called in a worker process to communicate with the
// coordinator process in order to fuzz random inputs. RunFuzzWorker loops
// until the coordinator tells it to stop.
//
// fn is a wrapper on the fuzz function. It may return an error to indicate
// a given input "crashed". The coordinator will also record a crasher if
// the function times out or terminates the process.
//
// RunFuzzWorker returns an error if it could not communicate with the
// coordinator process.
func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error {
return errors.New("not implemented")
}
-17
View File
@@ -1,12 +1,7 @@
#ifdef __MACH__
.global _tinygo_startTask
_tinygo_startTask:
#else
.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.
@@ -28,25 +23,13 @@ tinygo_startTask:
blr x19 blr x19
// After return, exit this goroutine. This is a tail call. // After return, exit this goroutine. This is a tail call.
#ifdef __MACH__
b _tinygo_pause
#else
b tinygo_pause b tinygo_pause
#endif
.cfi_endproc .cfi_endproc
#ifndef __MACH__
.size tinygo_startTask, .-tinygo_startTask .size tinygo_startTask, .-tinygo_startTask
#endif
#ifdef __MACH__
.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:
// x0 = newStack uintptr // x0 = newStack uintptr
// x1 = oldStack *uintptr // x1 = oldStack *uintptr
-81
View File
@@ -1,81 +0,0 @@
//go:build badger2040
// +build badger2040
// This contains the pin mappings for the Badger 2040 Connect board.
//
// For more information, see: https://shop.pimoroni.com/products/badger-2040
// Also
// - Badger 2040 schematic: https://cdn.shopify.com/s/files/1/0174/1800/files/badger_2040_schematic.pdf?v=1645702148
//
package machine
const (
LED Pin = GPIO25
BUTTON_A Pin = GPIO12
BUTTON_B Pin = GPIO13
BUTTON_C Pin = GPIO14
BUTTON_UP Pin = GPIO15
BUTTON_DOWN Pin = GPIO11
BUTTON_USER Pin = GPIO23
EPD_BUSY_PIN Pin = GPIO26
EPD_RESET_PIN Pin = GPIO21
EPD_DC_PIN Pin = GPIO20
EPD_CS_PIN Pin = GPIO17
EPD_SCK_PIN Pin = GPIO18
EPD_SDO_PIN Pin = GPIO19
VBUS_DETECT Pin = GPIO24
BATTERY Pin = GPIO29
ENABLE_3V3 Pin = GPIO10
)
// I2C pins
const (
I2C0_SDA_PIN Pin = GPIO4
I2C0_SCL_PIN Pin = GPIO5
I2C1_SDA_PIN Pin = NoPin
I2C1_SCL_PIN Pin = NoPin
)
// SPI pins.
const (
SPI0_SCK_PIN Pin = GPIO18
SPI0_SDO_PIN Pin = GPIO19
SPI0_SDI_PIN Pin = GPIO16
SPI1_SCK_PIN Pin = NoPin
SPI1_SDO_PIN Pin = NoPin
SPI1_SDI_PIN Pin = NoPin
)
// QSPI pins¿?
const (
/* TODO
SPI0_SD0_PIN Pin = QSPI_SD0
SPI0_SD1_PIN Pin = QSPI_SD1
SPI0_SD2_PIN Pin = QSPI_SD2
SPI0_SD3_PIN Pin = QSPI_SD3
SPI0_SCK_PIN Pin = QSPI_SCLKGPIO6
SPI0_CS_PIN Pin = QSPI_CS
*/
)
// Onboard crystal oscillator frequency, in MHz.
const (
xoscFreq = 12 // MHz
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Badger 2040"
usb_STRING_MANUFACTURER = "Pimoroni"
)
var (
usb_VID uint16 = 0x2e8a
usb_PID uint16 = 0x0003
)
-11
View File
@@ -60,14 +60,3 @@ const (
// Default Serial In Bus 1 for SPI communications // Default Serial In Bus 1 for SPI communications
SPI1_SDI_PIN = GPIO12 // Rx SPI1_SDI_PIN = GPIO12 // Rx
) )
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit Feather RP2040"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x80F1
)
-3
View File
@@ -42,9 +42,6 @@ const (
SPIQ = IO17 SPIQ = IO17
U0RXD = IO20 U0RXD = IO20
U0TXD = IO21 U0TXD = IO21
UART_TX_PIN = U0TXD
UART_RX_PIN = U0RXD
) )
const ( const (
+1 -1
View File
@@ -49,7 +49,7 @@ const (
D36 = PA19 // ESP32 SPI SDO 1[3] PWM EXTI3 D36 = PA19 // ESP32 SPI SDO 1[3] PWM EXTI3
D37 = NoPin // USB Host enable D37 = NoPin // USB Host enable
D38 = PA24 // USB DM D38 = PA24 // USB DM
D39 = PA25 // USB DP D39 = PA27 // USB DP
D40 = PA03 // DAC/VREFP D40 = PA03 // DAC/VREFP
D41 = PB10 // Flash QSPI SCK D41 = PB10 // Flash QSPI SCK
D42 = PB11 // Flash QSPI CS D42 = PB11 // Flash QSPI CS
-56
View File
@@ -1,56 +0,0 @@
//go:build nrf52840 || circuitplay_bluefruit || reelboard || clue || itsybitsy_nrf52840
// +build nrf52840 circuitplay_bluefruit reelboard clue itsybitsy_nrf52840
package machine
// Hardware pins
const (
P0_00 Pin = 0
P0_01 Pin = 1
P0_02 Pin = 2
P0_03 Pin = 3
P0_04 Pin = 4
P0_05 Pin = 5
P0_06 Pin = 6
P0_07 Pin = 7
P0_08 Pin = 8
P0_09 Pin = 9
P0_10 Pin = 10
P0_11 Pin = 11
P0_12 Pin = 12
P0_13 Pin = 13
P0_14 Pin = 14
P0_15 Pin = 15
P0_16 Pin = 16
P0_17 Pin = 17
P0_18 Pin = 18
P0_19 Pin = 19
P0_20 Pin = 20
P0_21 Pin = 21
P0_22 Pin = 22
P0_23 Pin = 23
P0_24 Pin = 24
P0_25 Pin = 25
P0_26 Pin = 26
P0_27 Pin = 27
P0_28 Pin = 28
P0_29 Pin = 29
P0_30 Pin = 30
P0_31 Pin = 31
P1_00 Pin = 32
P1_01 Pin = 33
P1_02 Pin = 34
P1_03 Pin = 35
P1_04 Pin = 36
P1_05 Pin = 37
P1_06 Pin = 38
P1_07 Pin = 39
P1_08 Pin = 40
P1_09 Pin = 41
P1_10 Pin = 42
P1_11 Pin = 43
P1_12 Pin = 44
P1_13 Pin = 45
P1_14 Pin = 46
P1_15 Pin = 47
)
-11
View File
@@ -64,14 +64,3 @@ const (
// Default Serial In Bus 1 for SPI communications // Default Serial In Bus 1 for SPI communications
SPI1_SDI_PIN = GPIO12 // Rx SPI1_SDI_PIN = GPIO12 // Rx
) )
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Raspberry Pi Pico"
usb_STRING_MANUFACTURER = "Raspberry Pi"
)
var (
usb_VID uint16 = 0x2E8A
usb_PID uint16 = 0x0003
)
-66
View File
@@ -237,8 +237,6 @@ var (
} }
) )
// #==================================================================#
// | SPI |
// #===========#==========#===============#===========================# // #===========#==========#===============#===========================#
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt | // | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
// #===========#==========#===============#=================-=========# // #===========#==========#===============#=================-=========#
@@ -263,70 +261,6 @@ const (
SPI3_CS_PIN = D36 SPI3_CS_PIN = D36
) )
var (
SPI0 = SPI1 // SPI0 is an alias of SPI1 (LPSPI4)
SPI1 = &_SPI1
_SPI1 = SPI{
Bus: nxp.LPSPI4,
muxSDI: muxSelect{ // D12 (PB1 [B0_01])
mux: nxp.IOMUXC_LPSPI4_SDI_SELECT_INPUT_DAISY_GPIO_B0_01_ALT3,
sel: &nxp.IOMUXC.LPSPI4_SDI_SELECT_INPUT,
},
muxSDO: muxSelect{ // D11 (PB2 [B0_02])
mux: nxp.IOMUXC_LPSPI4_SDO_SELECT_INPUT_DAISY_GPIO_B0_02_ALT3,
sel: &nxp.IOMUXC.LPSPI4_SDO_SELECT_INPUT,
},
muxSCK: muxSelect{ // D13 (PB3 [B0_03])
mux: nxp.IOMUXC_LPSPI4_SCK_SELECT_INPUT_DAISY_GPIO_B0_03_ALT3,
sel: &nxp.IOMUXC.LPSPI4_SCK_SELECT_INPUT,
},
muxCS: muxSelect{ // D10 (PB0 [B0_00])
mux: nxp.IOMUXC_LPSPI4_PCS0_SELECT_INPUT_DAISY_GPIO_B0_00_ALT3,
sel: &nxp.IOMUXC.LPSPI4_PCS0_SELECT_INPUT,
},
}
SPI2 = &_SPI2
_SPI2 = SPI{
Bus: nxp.LPSPI3,
muxSDI: muxSelect{ // D1 (PA2 [AD_B0_02])
mux: nxp.IOMUXC_LPSPI3_SDI_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT7,
sel: &nxp.IOMUXC.LPSPI3_SDI_SELECT_INPUT,
},
muxSDO: muxSelect{ // D26 (PA30 [AD_B1_14])
mux: nxp.IOMUXC_LPSPI3_SDO_SELECT_INPUT_DAISY_GPIO_AD_B1_14_ALT2,
sel: &nxp.IOMUXC.LPSPI3_SDO_SELECT_INPUT,
},
muxSCK: muxSelect{ // D27 (PA31 [AD_B1_15])
mux: nxp.IOMUXC_LPSPI3_SCK_SELECT_INPUT_DAISY_GPIO_AD_B1_15,
sel: &nxp.IOMUXC.LPSPI3_SCK_SELECT_INPUT,
},
muxCS: muxSelect{ // D0 (PA3 [AD_B0_03])
mux: nxp.IOMUXC_LPSPI3_PCS0_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT7,
sel: &nxp.IOMUXC.LPSPI3_PCS0_SELECT_INPUT,
},
}
SPI3 = &_SPI3
_SPI3 = SPI{
Bus: nxp.LPSPI1,
muxSDI: muxSelect{ // D34 (PC15 [SD_B0_03])
mux: nxp.IOMUXC_LPSPI1_SDI_SELECT_INPUT_DAISY_GPIO_SD_B0_03_ALT4,
sel: &nxp.IOMUXC.LPSPI1_SDI_SELECT_INPUT,
},
muxSDO: muxSelect{ // D35 (PC14 [SD_B0_02])
mux: nxp.IOMUXC_LPSPI1_SDO_SELECT_INPUT_DAISY_GPIO_SD_B0_02_ALT4,
sel: &nxp.IOMUXC.LPSPI1_SDO_SELECT_INPUT,
},
muxSCK: muxSelect{ // D37 (PC12 [SD_B0_00])
mux: nxp.IOMUXC_LPSPI1_SCK_SELECT_INPUT_DAISY_GPIO_SD_B0_00_ALT4,
sel: &nxp.IOMUXC.LPSPI1_SCK_SELECT_INPUT,
},
muxCS: muxSelect{ // D36 (PC13 [SD_B0_01])
mux: nxp.IOMUXC_LPSPI1_PCS0_SELECT_INPUT_DAISY_GPIO_SD_B0_01_ALT4,
sel: &nxp.IOMUXC.LPSPI1_PCS0_SELECT_INPUT,
},
}
)
// #====================================================# // #====================================================#
// | I2C | // | I2C |
// #===========#==========#=============#===============# // #===========#==========#=============#===============#
-81
View File
@@ -1,81 +0,0 @@
//go:build thingplus_rp2040
// +build thingplus_rp2040
package machine
const (
LED = GPIO25
// Onboard crystal oscillator frequency, in MHz.
xoscFreq = 12 // MHz
)
// GPIO Pins
const (
GP0 Pin = GPIO0 // TX
GP1 Pin = GPIO1 // RX
GP2 Pin = GPIO2 // SCK
GP3 Pin = GPIO3 // COPI
GP4 Pin = GPIO4 // CIPO
GP6 Pin = GPIO6 // SDA
GP7 Pin = GPIO7 // SCL (connected to GPIO23 as well)
GP8 Pin = GPIO8 // WS2812 RGB LED
GP9 Pin = GPIO9 // muSDcard DATA3 / CS
GP10 Pin = GPIO10 // muSDcard DATA2
GP11 Pin = GPIO11 // muSDcard DATA1
GP12 Pin = GPIO12 // muSDcard DATA0 / CIPO
GP14 Pin = GPIO14 // muSDcard CLK /SCLK
GP15 Pin = GPIO15 // muSDcard CMD / COPI
GP16 Pin = GPIO16 // 16
GP17 Pin = GPIO17 // 17
GP18 Pin = GPIO18 // 18
GP19 Pin = GPIO19 // 19
GP20 Pin = GPIO20 // 20
GP21 Pin = GPIO21 // 21
GP22 Pin = GPIO22 // 22
GP23 Pin = GPIO23 // Connected to GPIO7
GP25 Pin = GPIO25 // Status blue LED
GP26 Pin = GPIO26 // ADC0
GP27 Pin = GPIO27 // ADC1
GP28 Pin = GPIO28 // ADC2
GP29 Pin = GPIO29 // ADC3
)
// Analog pins
const (
A0 = GPIO26
A1 = GPIO27
A2 = GPIO28
A3 = GPIO29
)
// I2C Pins.
const (
I2C0_SCL_PIN = GPIO6 // N/A
I2C0_SDA_PIN = GPIO7 // N/A
I2C1_SDA_PIN = GPIO6
I2C1_SCL_PIN = GPIO7
SDA_PIN = I2C1_SDA_PIN
SCL_PIN = I2C1_SCL_PIN
)
// SPI default pins
const (
// Default Serial Clock Bus 0 for SPI communications
SPI0_SCK_PIN = GPIO2
// Default Serial Out Bus 0 for SPI communications
SPI0_SDO_PIN = GPIO3 // Tx
// Default Serial In Bus 0 for SPI communications
SPI0_SDI_PIN = GPIO4 // Rx
// Default Serial Clock Bus 1 for SPI communications to muSDcard
SPI1_SCK_PIN = GPIO14
// Default Serial Out Bus 1 for SPI communications to muSDcard
SPI1_SDO_PIN = GPIO15 // Tx
// Default Serial In Bus 1 for SPI communications to muSDcard
SPI1_SDI_PIN = GPIO12 // Rx
)
+7 -7
View File
@@ -355,20 +355,20 @@ var (
// I2C pins // I2C pins
const ( const (
SDA1_PIN = PA17 // SDA: SERCOM3/PAD[0] SDA0_PIN = PIN_WIRE_SDA // SDA: SERCOM3/PAD[0]
SCL1_PIN = PA16 // SCL: SERCOM3/PAD[1] SCL0_PIN = PIN_WIRE_SCL // SCL: SERCOM3/PAD[1]
SDA0_PIN = PA13 // SDA: SERCOM4/PAD[0] SDA1_PIN = PIN_WIRE1_SDA // SDA: SERCOM4/PAD[0]
SCL0_PIN = PA12 // SCL: SERCOM4/PAD[1] SCL1_PIN = PIN_WIRE1_SCL // SCL: SERCOM4/PAD[1]
SDA_PIN = SDA1_PIN SDA_PIN = SDA0_PIN
SCL_PIN = SCL1_PIN SCL_PIN = SCL0_PIN
) )
// I2C on the Wio Terminal // I2C on the Wio Terminal
var ( var (
I2C0 = sercomI2CM4 I2C0 = sercomI2CM4
I2C1 = sercomI2CM3 I2C1 = sercomI2CM4
) )
// SPI pins // SPI pins

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