mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 20883b9fac | |||
| 99b129d366 | |||
| 303acf5ed6 | |||
| 7d1ce24be5 | |||
| 774c86e21b | |||
| 0cabd4de69 | |||
| 56bbc5bf6d | |||
| 0b44d0bcc5 | |||
| 6275b3a8d1 | |||
| 34b50efdcd | |||
| 42088f938e | |||
| c60c36f0a8 | |||
| e3aa13c2a6 | |||
| b0366743cd | |||
| c54bdf8b51 | |||
| 1fd4669bee | |||
| e36dfe58ac | |||
| 138befd8a2 | |||
| 3d13f9cfe1 | |||
| c4191da2a5 | |||
| 3fdd1a9249 | |||
| 869baca117 | |||
| 9c3e479432 | |||
| 6e480e189d | |||
| dcff8b6478 | |||
| 6862942c99 | |||
| abc478c294 | |||
| 66f76833ad | |||
| 24976a6974 | |||
| e76729c6f3 | |||
| 35c8707867 | |||
| 16e30c97d8 | |||
| 9841ac4acd | |||
| c299386906 | |||
| afcc9d6608 | |||
| 9f06798cb9 | |||
| ce407ddf1b | |||
| b689f14bb2 | |||
| 4141a9f431 | |||
| 52a20cf3bb | |||
| 1aac8a0cb1 | |||
| ba634c4cc7 | |||
| 681be2d4e3 | |||
| 01b917fb11 | |||
| d274e299ac | |||
| fcdaa83368 | |||
| 289e1aa197 | |||
| af02c09b56 | |||
| 801bd2a7ff | |||
| 9c8ef5c48f | |||
| ab41c79de7 | |||
| d3d4acaadf | |||
| 676a78776a | |||
| 8a54615a09 | |||
| 74fe213b15 | |||
| 2e9c3a1d8d | |||
| 5502182642 | |||
| f4b4dd8d62 | |||
| 9f5bd2c460 | |||
| d6cdf8ca28 | |||
| 6c5409bd17 | |||
| cca0eab3da | |||
| e161d5a82c | |||
| 1fb47a2670 | |||
| f1210caba8 | |||
| 5279bebf57 | |||
| 7842e6940d | |||
| 06f231468d | |||
| 3d6921b0e1 | |||
| 9612af466b | |||
| 868933e67c | |||
| f3bdebe2a6 | |||
| 5bae55d755 | |||
| 0bad2c9ff2 | |||
| 5642d72fbe | |||
| 92ed645a11 | |||
| 32a5d46c57 | |||
| d8cc48b09b | |||
| 9bd36597d6 | |||
| d85ac4b3cc | |||
| 3010466c55 | |||
| 36db75b366 | |||
| a5e2b27884 | |||
| a5cf704d83 | |||
| a90865506d | |||
| a867b56e5f | |||
| f159429152 | |||
| a848d720db | |||
| dbc438b2ae | |||
| c4d642007e | |||
| da0161d6ab | |||
| 5af4c073cd | |||
| 154c7c691b | |||
| 65caf777dd | |||
| a4d0877cf0 | |||
| 6ec868710b | |||
| 086645153e | |||
| cf930f652e | |||
| 0dcff35d3a | |||
| 75f13491b6 | |||
| 533d2c9ce0 | |||
| 2f4200a01b | |||
| 939b393325 | |||
| a5ee1ba4b3 | |||
| fb0bb69f62 |
+24
-29
@@ -80,12 +80,12 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-v3
|
||||
- wasi-libc-sysroot-v4
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-v3
|
||||
key: wasi-libc-sysroot-v4
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
test-linux:
|
||||
@@ -108,13 +108,13 @@ commands:
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-systemclang-v2
|
||||
- wasi-libc-sysroot-systemclang-v3
|
||||
- run: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-systemclang-v2
|
||||
key: wasi-libc-sysroot-systemclang-v3
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
|
||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest XTENSA=0
|
||||
- run: make tinygo-test
|
||||
@@ -132,6 +132,7 @@ commands:
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
libc6-dev-armel-cross \
|
||||
@@ -153,7 +154,7 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v1-assert
|
||||
- llvm-build-11-linux-v2-assert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -168,7 +169,7 @@ commands:
|
||||
make ASSERT=1 llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v1-assert
|
||||
key: llvm-build-11-linux-v2-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- run: make ASSERT=1
|
||||
@@ -190,6 +191,7 @@ commands:
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
libc6-dev-armel-cross \
|
||||
@@ -211,7 +213,7 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v1-noassert
|
||||
- llvm-build-11-linux-v2-noassert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -226,7 +228,7 @@ commands:
|
||||
make llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v1-noassert
|
||||
key: llvm-build-11-linux-v2-noassert
|
||||
paths:
|
||||
llvm-build
|
||||
- build-wasi-libc
|
||||
@@ -268,8 +270,8 @@ commands:
|
||||
- run:
|
||||
name: "Install dependencies"
|
||||
command: |
|
||||
curl https://dl.google.com/go/go1.15.5.darwin-amd64.tar.gz -o go1.15.5.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.15.5.darwin-amd64.tar.gz
|
||||
curl https://dl.google.com/go/go1.16.darwin-amd64.tar.gz -o go1.16.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.16.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||
- install-xtensa-toolchain:
|
||||
@@ -290,7 +292,7 @@ commands:
|
||||
- llvm-project
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-macos-v1
|
||||
- llvm-build-11-macos-v2
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -302,17 +304,17 @@ commands:
|
||||
make llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-macos-v1
|
||||
key: llvm-build-11-macos-v2
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-macos-v2
|
||||
- wasi-libc-sysroot-macos-v3
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-macos-v2
|
||||
key: wasi-libc-sysroot-macos-v3
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run:
|
||||
@@ -340,18 +342,6 @@ commands:
|
||||
- /go/pkg/mod
|
||||
|
||||
jobs:
|
||||
test-llvm9-go111:
|
||||
docker:
|
||||
- image: circleci/golang:1.11-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "9"
|
||||
test-llvm10-go112:
|
||||
docker:
|
||||
- image: circleci/golang:1.12-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "10"
|
||||
test-llvm10-go113:
|
||||
docker:
|
||||
- image: circleci/golang:1.13-buster
|
||||
@@ -370,6 +360,12 @@ jobs:
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "11"
|
||||
test-llvm11-go116:
|
||||
docker:
|
||||
- image: circleci/golang:1.16-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "11"
|
||||
assert-test-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.14-stretch
|
||||
@@ -391,11 +387,10 @@ jobs:
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
- test-llvm9-go111
|
||||
- test-llvm10-go112
|
||||
- test-llvm10-go113
|
||||
- test-llvm10-go114
|
||||
- test-llvm11-go115
|
||||
- test-llvm11-go116
|
||||
- build-linux
|
||||
- build-macos
|
||||
- assert-test-linux
|
||||
|
||||
@@ -20,3 +20,6 @@
|
||||
[submodule "lib/picolibc"]
|
||||
path = lib/picolibc
|
||||
url = https://github.com/keith-packard/picolibc.git
|
||||
[submodule "lib/stm32-svd"]
|
||||
path = lib/stm32-svd
|
||||
url = https://github.com/tinygo-org/stm32-svd
|
||||
|
||||
+1
-6
@@ -23,7 +23,7 @@ different guide:
|
||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||
|
||||
* Go (1.11+)
|
||||
* Go (1.13+)
|
||||
* Standard build tools (gcc/clang)
|
||||
* git
|
||||
* CMake
|
||||
@@ -43,11 +43,6 @@ You can also store LLVM outside of the TinyGo root directory by setting the
|
||||
`LLVM_BUILDDIR`, `CLANG_SRC` and `LLD_SRC` make variables, but that is not
|
||||
covered by this guide.
|
||||
|
||||
TinyGo uses Go modules, so if you clone TinyGo inside your GOPATH (and are using
|
||||
Go below 1.13), make sure that Go modules are enabled:
|
||||
|
||||
export GO111MODULE=on
|
||||
|
||||
## Build LLVM, Clang, LLD
|
||||
|
||||
Before starting the build, you may want to set the following environment
|
||||
|
||||
@@ -1,3 +1,93 @@
|
||||
0.17.0
|
||||
|
||||
---
|
||||
* **command line**
|
||||
- switch to LLVM 11 for static builds
|
||||
- support gdb debugging with AVR
|
||||
- add support for additional openocd commands
|
||||
- add `-x` flag to print commands
|
||||
- use LLVM 11 by default when linking LLVM dynamically
|
||||
- update go-llvm to use LLVM 11 on macOS
|
||||
- bump go.bug.st/serial to version 1.1.2
|
||||
- do not build LLVM with libxml to work around a bugo on macOS
|
||||
- add support for Go 1.16
|
||||
- support gdb daemonization on Windows
|
||||
- remove support for LLVM 9, to fix CI
|
||||
- kill OpenOCD if it does not exit with a regular quit signal
|
||||
- support `-ocd-output` on Windows
|
||||
* **compiler**
|
||||
- `builder`: parallelize most of the build
|
||||
- `builder`: remove unused cacheKey parameter
|
||||
- `builder`: add -mcpu flag while building libraries
|
||||
- `builder`: wait for running jobs to finish
|
||||
- `cgo`: add support for variadic functions
|
||||
- `compiler`: fix undefined behavior in wordpack
|
||||
- `compiler`: fix incorrect "exported function" panic
|
||||
- `compiler`: fix non-int integer constants (fixing a crash)
|
||||
- `compiler`: refactor and add tests
|
||||
- `compiler`: emit a nil check when slicing an array pointer
|
||||
- `compiler`: saturate float-to-int conversions
|
||||
- `compiler`: test float to int conversions and fix upper-bound calculation
|
||||
- `compiler`: support all kinds of deferred builtins
|
||||
- `compiler`: remove ir package
|
||||
- `compiler`: remove unnecessary main.main call workaround
|
||||
- `compiler`: move the setting of attributes to getFunction
|
||||
- `compiler`: create runtime types lazily when needed
|
||||
- `compiler`: move settings to a separate Config struct
|
||||
- `compiler`: work around an ARM backend bug in LLVM
|
||||
- `interp`: rewrite entire package
|
||||
- `interp`: fix alignment of untyped globals
|
||||
- `loader`: use name "main" for the main package
|
||||
- `loader`: support imports from vendor directories
|
||||
- `stacksize`: add support for DW_CFA_offset_extended
|
||||
- `transform`: show better error message in coroutines lowering
|
||||
* **standard library**
|
||||
- `machine`: accept configuration struct for ADC parameters
|
||||
- `machine`: make I2C.Configure signature consistent
|
||||
- `reflect`: implement PtrTo
|
||||
- `runtime`: refactor to simplify stack switching
|
||||
- `runtime`: put metadata at the top end of the heap
|
||||
* **targets**
|
||||
- `atsam`: add a length check to findPinPadMapping
|
||||
- `atsam`: improve USBCDC
|
||||
- `atsam`: avoid infinite loop when USBCDC is disconnected
|
||||
- `avr`: add SPI support for Atmega based chips
|
||||
- `avr`: use Clang for compiling C and assembly files
|
||||
- `esp32`: implement task based scheduler
|
||||
- `esp32`: enable the FPU
|
||||
- `esp8266`: implement task based scheduler
|
||||
- `esp`: add compiler-rt library
|
||||
- `esp`: add picolibc
|
||||
- `nrf`: refactor code a bit to reduce duplication
|
||||
- `nrf`: use SPIM peripheral instead of the legacy SPI peripheral
|
||||
- `nrf`: update nrfx submodule to latest commit
|
||||
- `nrf52840`: ensure that USB CDC interface is only initialized once
|
||||
- `nrf52840`: improve USBCDC
|
||||
- `stm32`: use stm32-rs SVDs which are of much higher quality
|
||||
- `stm32`: harmonization of UART logic
|
||||
- `stm32`: replace I2C addressable interface with simpler type
|
||||
- `stm32`: fix i2c and add stm32f407 i2c
|
||||
- `stm32`: revert change that adds support for channels in interrupts
|
||||
- `wasm`: implement a growable heap
|
||||
- `wasm`: fix typo in wasm_exec.js, syscall/js.valueLoadString()
|
||||
- `wasm`: Namespaced Wasm Imports so they don't conflict across modules, or reserved LLVM IR
|
||||
- `wasi`: support env variables based on libc
|
||||
- `wasi`: specify wasi-libc in a different way, to improve error message
|
||||
* **boards**
|
||||
- `matrixportal-m4`: add support for board Adafruit Matrix Portal M4
|
||||
- `mkr1000`: add this board
|
||||
- `nucleo-f722ze`: add this board
|
||||
- `clue`: correct volume name and add alias for release version of Adafruit Clue board
|
||||
- `p1am-100`: add support for the P1AM-100 (similar to Arduino MKR)
|
||||
- `microbit-v2`: add initial support based on work done by @alankrantas thank you!
|
||||
- `lgt92`: support for STM32L0 MCUs and Dragino LGT92 device
|
||||
- `nicenano`: nice!nano board support
|
||||
- `circuitplay-bluefruit`: correct internal I2C pin mapping
|
||||
- `clue`: correct for lack of low frequency crystal
|
||||
- `digispark`: split off attiny85 target
|
||||
- `nucleo-l552ze`: implementation with CLOCK, LED, and UART
|
||||
- `nrf52840-mdk-usb-dongle`: add this board
|
||||
|
||||
0.16.0
|
||||
---
|
||||
|
||||
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
# TinyGo base stage installs the most recent Go 1.15.x, LLVM 10 and the TinyGo compiler itself.
|
||||
# TinyGo base stage installs the most recent Go 1.15.x, LLVM 11 and the TinyGo compiler itself.
|
||||
FROM golang:1.15 AS tinygo-base
|
||||
|
||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
|
||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y llvm-10-dev libclang-10-dev lld-10 git
|
||||
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
|
||||
|
||||
COPY . /tinygo
|
||||
|
||||
@@ -29,7 +29,7 @@ COPY --from=tinygo-base /tinygo/targets /tinygo/targets
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y make clang-10 libllvm10 lld-10 && \
|
||||
apt-get install -y make clang-11 libllvm11 lld-11 && \
|
||||
make wasi-libc
|
||||
|
||||
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
|
||||
@@ -61,7 +61,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils make clang-10 && \
|
||||
apt-get install -y apt-utils make clang-11 && \
|
||||
make gen-device-nrf && make gen-device-stm32
|
||||
|
||||
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||
@@ -73,7 +73,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils make clang-10 binutils-avr gcc-avr avr-libc && \
|
||||
apt-get install -y apt-utils make clang-11 binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device
|
||||
|
||||
CMD ["tinygo"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Copyright (c) 2018-2020 TinyGo Authors. All rights reserved.
|
||||
Copyright (c) 2018-2021 TinyGo Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of the Go standard library.
|
||||
Copyright (c) 2009-2020 The Go Authors. All rights reserved.
|
||||
Copyright (c) 2009-2021 The Go Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
||||
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
||||
|
||||
@@ -54,39 +54,53 @@ ifeq ($(OS),Windows_NT)
|
||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||
CGO_LDFLAGS_EXTRA += -lversion
|
||||
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/liblibclang.a
|
||||
LIBCLANG_NAME = libclang
|
||||
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
LIBCLANG_NAME = clang
|
||||
else ifeq ($(shell uname -s),FreeBSD)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
LIBCLANG_NAME = clang
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
else
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
LIBCLANG_NAME = clang
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
endif
|
||||
|
||||
CLANG_LIBS = $(START_GROUP) -lclangAnalysis -lclangARCMigrate -lclangAST -lclangASTMatchers -lclangBasic -lclangCodeGen -lclangCrossTU -lclangDriver -lclangDynamicASTMatchers -lclangEdit -lclangFormat -lclangFrontend -lclangFrontendTool -lclangHandleCXX -lclangHandleLLVM -lclangIndex -lclangLex -lclangParse -lclangRewrite -lclangRewriteFrontend -lclangSema -lclangSerialization -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangStaticAnalyzerFrontend -lclangTooling -lclangToolingASTDiff -lclangToolingCore -lclangToolingInclusions $(END_GROUP) -lstdc++
|
||||
# Libraries that should be linked in for the statically linked Clang.
|
||||
CLANG_LIB_NAMES = clangAnalysis clangARCMigrate clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangStaticAnalyzerCheckers clangStaticAnalyzerCore clangStaticAnalyzerFrontend clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||
|
||||
LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML $(END_GROUP)
|
||||
# Libraries that should be linked in for the statically linked LLD.
|
||||
LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO lldMinGW lldReaderWriter lldWasm lldYAML
|
||||
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
|
||||
|
||||
# Other libraries that are needed to link TinyGo.
|
||||
EXTRA_LIB_NAMES = LLVMInterpreter
|
||||
|
||||
# These build targets appear to be the only ones necessary to build all TinyGo
|
||||
# dependencies. Only building a subset significantly speeds up rebuilding LLVM.
|
||||
# The Makefile rules convert a name like lldELF to lib/liblldELF.a to match the
|
||||
# library path (for ninja).
|
||||
# This list also includes a few tools that are necessary as part of the full
|
||||
# TinyGo build.
|
||||
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIBCLANG_NAME) $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)))
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++14
|
||||
CGO_LDFLAGS+=$(LIBCLANG_PATH) -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
CGO_LDFLAGS+=$(abspath $(LLVM_BUILDDIR))/lib/lib$(LIBCLANG_NAME).a -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
endif
|
||||
|
||||
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
|
||||
fmt:
|
||||
@gofmt -l -w $(FMT_PATHS)
|
||||
fmt-check:
|
||||
@@ -130,7 +144,7 @@ gen-device-kendryte: build/gen-device-svd
|
||||
GO111MODULE=off $(GO) fmt ./src/device/kendryte
|
||||
|
||||
gen-device-stm32: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro lib/cmsis-svd/data/STMicro/ src/device/stm32/
|
||||
./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd lib/stm32-svd/svd src/device/stm32/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||
|
||||
|
||||
@@ -142,11 +156,11 @@ llvm-source: $(LLVM_PROJECTDIR)/README.md
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
$(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_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=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 $(LLVM_OPTION)
|
||||
|
||||
# Build LLVM.
|
||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
cd $(LLVM_BUILDDIR); ninja
|
||||
cd $(LLVM_BUILDDIR); ninja $(NINJA_BUILD_TARGETS)
|
||||
|
||||
|
||||
# Build wasi-libc sysroot
|
||||
@@ -163,7 +177,7 @@ tinygo:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||
|
||||
test: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./interp ./transform .
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: do this in one command, parallelize, and only show failing tests (no
|
||||
@@ -209,6 +223,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
||||
@@ -295,6 +311,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=lgt92 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=x9pro examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
|
||||
@@ -323,6 +341,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
|
||||
@@ -43,11 +43,11 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 44 microcontroller boards are currently supported:
|
||||
The following 55 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
* [Adafruit CLUE Alpha](https://www.adafruit.com/product/4500)
|
||||
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
|
||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
||||
@@ -55,22 +55,29 @@ The following 44 microcontroller boards are currently supported:
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
|
||||
* [Adafruit Matrix Portal M4](https://www.adafruit.com/product/4745)
|
||||
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
||||
* [Adafruit PyBadge](https://www.adafruit.com/product/4200)
|
||||
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
|
||||
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
||||
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
|
||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
||||
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
|
||||
* [BBC micro:bit](https://microbit.org/)
|
||||
* [BBC micro:bit v2](https://microbit.org/new-microbit/)
|
||||
* [Digispark](http://digistump.com/products/1)
|
||||
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
|
||||
* [ESP32](https://www.espressif.com/en/products/socs/esp32)
|
||||
* [ESP8266](https://www.espressif.com/en/products/socs/esp8266)
|
||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
|
||||
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
|
||||
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||
@@ -81,12 +88,16 @@ The following 44 microcontroller boards are currently supported:
|
||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
* [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" L552ZE](https://www.st.com/en/evaluation-tools/nucleo-l552ze-q.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
|
||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
|
||||
|
||||
|
||||
+4
-8
@@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
inputs:
|
||||
version: '1.15'
|
||||
version: '1.16'
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
- task: Cache@2
|
||||
@@ -24,15 +24,11 @@ jobs:
|
||||
displayName: Download LLVM source
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
make llvm-source
|
||||
# Workaround for bad symlinks:
|
||||
# https://github.com/microsoft/azure-pipelines-tasks/issues/13418
|
||||
rm -f llvm-project/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink
|
||||
script: make llvm-source
|
||||
- task: CacheBeta@0
|
||||
displayName: Cache LLVM build
|
||||
inputs:
|
||||
key: llvm-build-11-windows-v3
|
||||
key: llvm-build-11-windows-v4
|
||||
path: llvm-build
|
||||
- task: Bash@3
|
||||
displayName: Build LLVM
|
||||
@@ -56,7 +52,7 @@ jobs:
|
||||
- task: CacheBeta@0
|
||||
displayName: Cache wasi-libc sysroot
|
||||
inputs:
|
||||
key: wasi-libc-sysroot-v3
|
||||
key: wasi-libc-sysroot-v4
|
||||
path: lib/wasi-libc/sysroot
|
||||
- task: Bash@3
|
||||
displayName: Build wasi-libc
|
||||
|
||||
+344
-193
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
"github.com/tinygo-org/tinygo/compiler"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/interp"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"github.com/tinygo-org/tinygo/stacksize"
|
||||
"github.com/tinygo-org/tinygo/transform"
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -42,32 +44,359 @@ type BuildResult struct {
|
||||
//
|
||||
// The error value may be of type *MultiError. Callers will likely want to check
|
||||
// for this case and print such errors individually.
|
||||
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
||||
// Compile Go code to IR.
|
||||
machine, err := compiler.NewTargetMachine(config)
|
||||
func Build(pkgName, outpath string, config *compileopts.Config, preAction func() error, action func(BuildResult) error) error {
|
||||
compilerConfig := &compiler.Config{
|
||||
Triple: config.Triple(),
|
||||
CPU: config.CPU(),
|
||||
Features: config.Features(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
|
||||
Scheduler: config.Scheduler(),
|
||||
FuncImplementation: config.FuncImplementation(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
DefaultStackSize: config.Target.DefaultStackSize,
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
Debug: config.Debug(),
|
||||
}
|
||||
|
||||
// Load the target machine, which is the LLVM object that contains all
|
||||
// details of a target (alignment restrictions, pointer size, default
|
||||
// address spaces, etc).
|
||||
machine, err := compiler.NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buildOutput, errs := compiler.Compile(pkgName, machine, config)
|
||||
if errs != nil {
|
||||
return newMultiError(errs)
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
||||
Sizes: compiler.Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The slice of jobs that orchestrates most of the build.
|
||||
// This is somewhat like an in-memory Makefile with each job being a
|
||||
// Makefile target.
|
||||
var jobs []*compileJob
|
||||
|
||||
if preAction != nil {
|
||||
// Add job to preAction.
|
||||
jobs = append(jobs, &compileJob{
|
||||
description: "preAction",
|
||||
run: preAction,
|
||||
})
|
||||
}
|
||||
|
||||
// Add job to compile and optimize all Go files at once.
|
||||
// TODO: parallelize this.
|
||||
var mod llvm.Module
|
||||
var stackSizeLoads []string
|
||||
programJob := &compileJob{
|
||||
description: "compile Go files",
|
||||
run: func() (err error) {
|
||||
mod, err = compileWholeProgram(pkgName, config, compilerConfig, lprogram, machine)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Make sure stack sizes are loaded from a separate section so they can be
|
||||
// modified after linking.
|
||||
if config.AutomaticStackSize() {
|
||||
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, programJob)
|
||||
|
||||
// Check whether we only need to create an object file.
|
||||
// If so, we don't need to link anything and will be finished quickly.
|
||||
outext := filepath.Ext(outpath)
|
||||
if outext == ".o" || outext == ".bc" || outext == ".ll" {
|
||||
// Run jobs to produce the LLVM module.
|
||||
err := runJobs(jobs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Generate output.
|
||||
switch outext {
|
||||
case ".o":
|
||||
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
||||
case ".bc":
|
||||
data := llvm.WriteBitcodeToMemoryBuffer(mod).Bytes()
|
||||
return ioutil.WriteFile(outpath, data, 0666)
|
||||
case ".ll":
|
||||
data := []byte(mod.String())
|
||||
return ioutil.WriteFile(outpath, data, 0666)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// Act as a compiler driver, as we need to produce a complete executable.
|
||||
// First add all jobs necessary to build this object file, then afterwards
|
||||
// run all jobs in parallel as far as possible.
|
||||
|
||||
// Create a temporary directory for intermediary files.
|
||||
dir, err := ioutil.TempDir("", "tinygo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Add job to write the output object file.
|
||||
objfile := filepath.Join(dir, "main.o")
|
||||
outputObjectFileJob := &compileJob{
|
||||
description: "generate output file",
|
||||
dependencies: []*compileJob{programJob},
|
||||
run: func() error {
|
||||
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, outputObjectFileJob)
|
||||
|
||||
// Prepare link command.
|
||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||
executable := filepath.Join(dir, "main")
|
||||
tmppath := executable // final file
|
||||
ldflags := append(config.LDFlags(), "-o", executable, objfile)
|
||||
|
||||
// Add compiler-rt dependency if needed. Usually this is a simple load from
|
||||
// a cache.
|
||||
if config.Target.RTLib == "compiler-rt" {
|
||||
path, job, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if job != nil {
|
||||
// The library was not loaded from cache so needs to be compiled
|
||||
// (and then stored in the cache).
|
||||
jobs = append(jobs, job.dependencies...)
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
ldflags = append(ldflags, path)
|
||||
}
|
||||
|
||||
// Add libc dependency if needed.
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
switch config.Target.Libc {
|
||||
case "picolibc":
|
||||
path, job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if job != nil {
|
||||
// The library needs to be compiled (cache miss).
|
||||
jobs = append(jobs, job.dependencies...)
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
ldflags = append(ldflags, path)
|
||||
case "wasi-libc":
|
||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||
}
|
||||
ldflags = append(ldflags, path)
|
||||
case "":
|
||||
// no library specified, so nothing to do
|
||||
default:
|
||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||
}
|
||||
|
||||
// Add jobs to compile extra files. These files are in C or assembly and
|
||||
// contain things like the interrupt vector table and low level operations
|
||||
// such as stack switching.
|
||||
for i, path := range config.ExtraFiles() {
|
||||
abspath := filepath.Join(root, path)
|
||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
||||
job := &compileJob{
|
||||
description: "compile extra file " + path,
|
||||
run: func() error {
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", path, err}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
ldflags = append(ldflags, outpath)
|
||||
}
|
||||
|
||||
// Add jobs to compile C files in all packages. This is part of CGo.
|
||||
// TODO: do this as part of building the package to be able to link the
|
||||
// bitcode files together.
|
||||
for i, pkg := range lprogram.Sorted() {
|
||||
for j, filename := range pkg.CFiles {
|
||||
file := filepath.Join(pkg.Dir, filename)
|
||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"."+strconv.Itoa(j)+"-"+filepath.Base(file)+".o")
|
||||
job := &compileJob{
|
||||
description: "compile CGo file " + file,
|
||||
run: func() error {
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", file, err}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
ldflags = append(ldflags, outpath)
|
||||
}
|
||||
}
|
||||
|
||||
// Linker flags from CGo lines:
|
||||
// #cgo LDFLAGS: foo
|
||||
if len(lprogram.LDFlags) > 0 {
|
||||
ldflags = append(ldflags, lprogram.LDFlags...)
|
||||
}
|
||||
|
||||
// Create a linker job, which links all object files together and does some
|
||||
// extra stuff that can only be done after linking.
|
||||
jobs = append(jobs, &compileJob{
|
||||
description: "link",
|
||||
dependencies: linkerDependencies,
|
||||
run: func() error {
|
||||
err = link(config.Target.Linker, ldflags...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to link", executable, err}
|
||||
}
|
||||
|
||||
var calculatedStacks []string
|
||||
var stackSizes map[string]functionStackSize
|
||||
if config.Options.PrintStacks || config.AutomaticStackSize() {
|
||||
// Try to determine stack sizes at compile time.
|
||||
// Don't do this by default as it usually doesn't work on
|
||||
// unsupported architectures.
|
||||
calculatedStacks, stackSizes, err = determineStackSizes(mod, executable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if config.AutomaticStackSize() {
|
||||
// Modify the .tinygo_stacksizes section that contains a stack size
|
||||
// for each goroutine.
|
||||
err = modifyStackSizes(executable, stackSizeLoads, stackSizes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not modify stack sizes: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
||||
sizes, err := loadProgramSize(executable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Options.PrintSizes == "short" {
|
||||
fmt.Printf(" code data bss | flash ram\n")
|
||||
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
|
||||
} else {
|
||||
fmt.Printf(" code rodata data bss | flash ram | package\n")
|
||||
for _, name := range sizes.sortedPackageNames() {
|
||||
pkgSize := sizes.Packages[name]
|
||||
fmt.Printf("%7d %7d %7d %7d | %7d %7d | %s\n", pkgSize.Code, pkgSize.ROData, pkgSize.Data, pkgSize.BSS, pkgSize.Flash(), pkgSize.RAM(), name)
|
||||
}
|
||||
fmt.Printf("%7d %7d %7d %7d | %7d %7d | (sum)\n", sizes.Sum.Code, sizes.Sum.ROData, sizes.Sum.Data, sizes.Sum.BSS, sizes.Sum.Flash(), sizes.Sum.RAM())
|
||||
fmt.Printf("%7d - %7d %7d | %7d %7d | (all)\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
|
||||
}
|
||||
}
|
||||
|
||||
// Print goroutine stack sizes, as far as possible.
|
||||
if config.Options.PrintStacks {
|
||||
printStacks(calculatedStacks, stackSizes)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
// Run all jobs to compile and link the program.
|
||||
// Do this now (instead of after elf-to-hex and similar conversions) as it
|
||||
// is simpler and cannot be parallelized.
|
||||
err = runJobs(jobs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get an Intel .hex file or .bin file from the .elf file.
|
||||
outputBinaryFormat := config.BinaryFormat(outext)
|
||||
switch outputBinaryFormat {
|
||||
case "elf":
|
||||
// do nothing, file is already in ELF format
|
||||
case "hex", "bin":
|
||||
// Extract raw binary, either encoding it as a hex file or as a raw
|
||||
// firmware file.
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err := objcopy(executable, tmppath, outputBinaryFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "uf2":
|
||||
// Get UF2 from the .elf file.
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "esp32", "esp8266":
|
||||
// Special format for the ESP family of chips (parsed by the ROM
|
||||
// bootloader).
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
||||
}
|
||||
return action(BuildResult{
|
||||
Binary: tmppath,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
})
|
||||
}
|
||||
|
||||
// compileWholeProgram compiles the entire *loader.Program to a LLVM module and
|
||||
// applies most necessary optimizations and transformations.
|
||||
func compileWholeProgram(pkgName string, config *compileopts.Config, compilerConfig *compiler.Config, lprogram *loader.Program, machine llvm.TargetMachine) (llvm.Module, error) {
|
||||
// Compile AST to IR.
|
||||
mod, errs := compiler.CompileProgram(lprogram, machine, compilerConfig, config.DumpSSA())
|
||||
if errs != nil {
|
||||
return mod, newMultiError(errs)
|
||||
}
|
||||
mod := buildOutput.Mod
|
||||
|
||||
if config.Options.PrintIR {
|
||||
fmt.Println("; Generated LLVM IR:")
|
||||
fmt.Println(mod.String())
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return errors.New("verification error after IR construction")
|
||||
return mod, errors.New("verification error after IR construction")
|
||||
}
|
||||
|
||||
err = interp.Run(mod, config.DumpSSA())
|
||||
err := interp.Run(mod, config.DumpSSA())
|
||||
if err != nil {
|
||||
return err
|
||||
return mod, err
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return errors.New("verification error after interpreting runtime.initAll")
|
||||
return mod, errors.New("verification error after interpreting runtime.initAll")
|
||||
}
|
||||
|
||||
if config.GOOS() != "darwin" {
|
||||
@@ -82,7 +411,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if config.WasmAbi() == "js" {
|
||||
err := transform.ExternalInt64AsPtr(mod)
|
||||
if err != nil {
|
||||
return err
|
||||
return mod, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +440,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return newMultiError(errs)
|
||||
return mod, newMultiError(errs)
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return errors.New("verification failure after LLVM optimization passes")
|
||||
return mod, errors.New("verification failure after LLVM optimization passes")
|
||||
}
|
||||
|
||||
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
||||
@@ -128,185 +457,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
transform.DisableTailCalls(mod)
|
||||
}
|
||||
|
||||
// Make sure stack sizes are loaded from a separate section so they can be
|
||||
// modified after linking.
|
||||
var stackSizeLoads []string
|
||||
if config.AutomaticStackSize() {
|
||||
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
|
||||
}
|
||||
|
||||
// Generate output.
|
||||
outext := filepath.Ext(outpath)
|
||||
switch outext {
|
||||
case ".o":
|
||||
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
||||
case ".bc":
|
||||
data := llvm.WriteBitcodeToMemoryBuffer(mod).Bytes()
|
||||
return ioutil.WriteFile(outpath, data, 0666)
|
||||
case ".ll":
|
||||
data := []byte(mod.String())
|
||||
return ioutil.WriteFile(outpath, data, 0666)
|
||||
default:
|
||||
// Act as a compiler driver.
|
||||
|
||||
// Create a temporary directory for intermediary files.
|
||||
dir, err := ioutil.TempDir("", "tinygo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Write the object file.
|
||||
objfile := filepath.Join(dir, "main.o")
|
||||
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare link command.
|
||||
executable := filepath.Join(dir, "main")
|
||||
tmppath := executable // final file
|
||||
ldflags := append(config.LDFlags(), "-o", executable, objfile)
|
||||
|
||||
// Load builtins library from the cache, possibly compiling it on the
|
||||
// fly.
|
||||
if config.Target.RTLib == "compiler-rt" {
|
||||
librt, err := CompilerRT.Load(config.Triple())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ldflags = append(ldflags, librt)
|
||||
}
|
||||
|
||||
// Add libc.
|
||||
if config.Target.Libc == "picolibc" {
|
||||
libc, err := Picolibc.Load(config.Triple())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ldflags = append(ldflags, libc)
|
||||
}
|
||||
|
||||
// Compile extra files.
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
for i, path := range config.ExtraFiles() {
|
||||
abspath := filepath.Join(root, path)
|
||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", path, err}
|
||||
}
|
||||
ldflags = append(ldflags, outpath)
|
||||
}
|
||||
|
||||
// Compile C files in packages.
|
||||
for i, file := range buildOutput.ExtraFiles {
|
||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", file, err}
|
||||
}
|
||||
ldflags = append(ldflags, outpath)
|
||||
}
|
||||
|
||||
if len(buildOutput.ExtraLDFlags) > 0 {
|
||||
ldflags = append(ldflags, buildOutput.ExtraLDFlags...)
|
||||
}
|
||||
|
||||
// Link the object files together.
|
||||
err = link(config.Target.Linker, ldflags...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to link", executable, err}
|
||||
}
|
||||
|
||||
var calculatedStacks []string
|
||||
var stackSizes map[string]functionStackSize
|
||||
if config.Options.PrintStacks || config.AutomaticStackSize() {
|
||||
// Try to determine stack sizes at compile time.
|
||||
// Don't do this by default as it usually doesn't work on
|
||||
// unsupported architectures.
|
||||
calculatedStacks, stackSizes, err = determineStackSizes(mod, executable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if config.AutomaticStackSize() {
|
||||
// Modify the .tinygo_stacksizes section that contains a stack size
|
||||
// for each goroutine.
|
||||
err = modifyStackSizes(executable, stackSizeLoads, stackSizes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not modify stack sizes: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
||||
sizes, err := loadProgramSize(executable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Options.PrintSizes == "short" {
|
||||
fmt.Printf(" code data bss | flash ram\n")
|
||||
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
|
||||
} else {
|
||||
fmt.Printf(" code rodata data bss | flash ram | package\n")
|
||||
for _, name := range sizes.sortedPackageNames() {
|
||||
pkgSize := sizes.Packages[name]
|
||||
fmt.Printf("%7d %7d %7d %7d | %7d %7d | %s\n", pkgSize.Code, pkgSize.ROData, pkgSize.Data, pkgSize.BSS, pkgSize.Flash(), pkgSize.RAM(), name)
|
||||
}
|
||||
fmt.Printf("%7d %7d %7d %7d | %7d %7d | (sum)\n", sizes.Sum.Code, sizes.Sum.ROData, sizes.Sum.Data, sizes.Sum.BSS, sizes.Sum.Flash(), sizes.Sum.RAM())
|
||||
fmt.Printf("%7d - %7d %7d | %7d %7d | (all)\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
|
||||
}
|
||||
}
|
||||
|
||||
// Print goroutine stack sizes, as far as possible.
|
||||
if config.Options.PrintStacks {
|
||||
printStacks(calculatedStacks, stackSizes)
|
||||
}
|
||||
|
||||
// Get an Intel .hex file or .bin file from the .elf file.
|
||||
outputBinaryFormat := config.BinaryFormat(outext)
|
||||
switch outputBinaryFormat {
|
||||
case "elf":
|
||||
// do nothing, file is already in ELF format
|
||||
case "hex", "bin":
|
||||
// Extract raw binary, either encoding it as a hex file or as a raw
|
||||
// firmware file.
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err := objcopy(executable, tmppath, outputBinaryFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "uf2":
|
||||
// Get UF2 from the .elf file.
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "esp32", "esp8266":
|
||||
// Special format for the ESP family of chips (parsed by the ROM
|
||||
// bootloader).
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
||||
}
|
||||
return action(BuildResult{
|
||||
Binary: tmppath,
|
||||
MainDir: buildOutput.MainDir,
|
||||
})
|
||||
}
|
||||
return mod, nil
|
||||
}
|
||||
|
||||
// functionStackSizes keeps stack size information about a single function
|
||||
|
||||
+6
-19
@@ -30,10 +30,7 @@ func cacheTimestamp(paths []string) (time.Time, error) {
|
||||
// Try to load a given file from the cache. Return "", nil if no cached file can
|
||||
// be found (or the file is stale), return the absolute path if there is a cache
|
||||
// and return an error on I/O errors.
|
||||
//
|
||||
// TODO: the configKey is currently ignored. It is supposed to be used as extra
|
||||
// data for the cache key, like the compiler version and arguments.
|
||||
func cacheLoad(name, configKey string, sourceFiles []string) (string, error) {
|
||||
func cacheLoad(name string, sourceFiles []string) (string, error) {
|
||||
cachepath := filepath.Join(goenv.Get("GOCACHE"), name)
|
||||
cacheStat, err := os.Stat(cachepath)
|
||||
if os.IsNotExist(err) {
|
||||
@@ -58,9 +55,7 @@ func cacheLoad(name, configKey string, sourceFiles []string) (string, error) {
|
||||
|
||||
// Store the file located at tmppath in the cache with the given name. The
|
||||
// tmppath may or may not be gone afterwards.
|
||||
//
|
||||
// Note: the configKey is ignored, see cacheLoad.
|
||||
func cacheStore(tmppath, name, configKey string, sourceFiles []string) (string, error) {
|
||||
func cacheStore(tmppath, name string, sourceFiles []string) (string, error) {
|
||||
// get the last modified time
|
||||
if len(sourceFiles) == 0 {
|
||||
panic("cache: no source files")
|
||||
@@ -74,24 +69,16 @@ func cacheStore(tmppath, name, configKey string, sourceFiles []string) (string,
|
||||
return "", err
|
||||
}
|
||||
cachepath := filepath.Join(dir, name)
|
||||
err = moveFile(tmppath, cachepath)
|
||||
err = copyFile(tmppath, cachepath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cachepath, nil
|
||||
}
|
||||
|
||||
// moveFile renames the file from src to dst. If renaming doesn't work (for
|
||||
// example, the rename crosses a filesystem boundary), the file is copied and
|
||||
// the old file is removed.
|
||||
func moveFile(src, dst string) error {
|
||||
err := os.Rename(src, dst)
|
||||
if err == nil {
|
||||
// Success!
|
||||
return nil
|
||||
}
|
||||
// Failed to move, probably a different filesystem.
|
||||
// Do a copy + remove.
|
||||
// copyFile copies the given file from src to dst. It can copy over
|
||||
// a possibly already existing file at the destination.
|
||||
func copyFile(src, dst string) error {
|
||||
inf, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+2
-2
@@ -25,8 +25,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
if major != 1 || minor < 11 || minor > 15 {
|
||||
return nil, fmt.Errorf("requires go version 1.11 through 1.15, got go%d.%d", major, minor)
|
||||
if major != 1 || minor < 13 || minor > 16 {
|
||||
return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor)
|
||||
}
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
return &compileopts.Config{
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package builder
|
||||
|
||||
// This file implements a job runner for the compiler, which runs jobs in
|
||||
// parallel while taking care of dependencies.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Set to true to enable logging in the job runner. This may help to debug
|
||||
// concurrency or performance issues.
|
||||
const jobRunnerDebug = false
|
||||
|
||||
type jobState uint8
|
||||
|
||||
const (
|
||||
jobStateQueued jobState = iota // not yet running
|
||||
jobStateRunning // running
|
||||
jobStateFinished // finished running
|
||||
)
|
||||
|
||||
// compileJob is a single compiler job, comparable to a single Makefile target.
|
||||
// It is used to orchestrate various compiler tasks that can be run in parallel
|
||||
// but that have dependencies and thus have limitations in how they can be run.
|
||||
type compileJob struct {
|
||||
description string // description, only used for logging
|
||||
dependencies []*compileJob
|
||||
run func() error
|
||||
state jobState
|
||||
err error // error if finished
|
||||
duration time.Duration // how long it took to run this job (only set after finishing)
|
||||
}
|
||||
|
||||
// readyToRun returns whether this job is ready to run: it is itself not yet
|
||||
// started and all dependencies are finished.
|
||||
func (job *compileJob) readyToRun() bool {
|
||||
if job.state != jobStateQueued {
|
||||
// Already running or finished, so shouldn't be run again.
|
||||
return false
|
||||
}
|
||||
|
||||
// Check dependencies.
|
||||
for _, dep := range job.dependencies {
|
||||
if dep.state != jobStateFinished {
|
||||
// A dependency is not finished, so this job has to wait until it
|
||||
// is.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// All conditions are satisfied.
|
||||
return true
|
||||
}
|
||||
|
||||
// runJobs runs all the jobs indicated in the jobs slice and returns the error
|
||||
// of the first job that fails to run.
|
||||
// It runs all jobs in the order of the slice, as long as all dependencies have
|
||||
// already run. Therefore, if some jobs are preferred to run before others, they
|
||||
// should be ordered as such in this slice.
|
||||
func runJobs(jobs []*compileJob) error {
|
||||
// Create channels to communicate with the workers.
|
||||
doneChan := make(chan *compileJob)
|
||||
workerChan := make(chan *compileJob)
|
||||
defer close(workerChan)
|
||||
|
||||
// Start a number of workers.
|
||||
for i := 0; i < runtime.NumCPU(); i++ {
|
||||
if jobRunnerDebug {
|
||||
fmt.Println("## starting worker", i)
|
||||
}
|
||||
go jobWorker(workerChan, doneChan)
|
||||
}
|
||||
|
||||
// Send each job in the jobs slice to a worker, taking care of job
|
||||
// dependencies.
|
||||
numRunningJobs := 0
|
||||
var totalTime time.Duration
|
||||
start := time.Now()
|
||||
for {
|
||||
// If there are free workers, try starting a new job (if one is
|
||||
// available). If it succeeds, try again to fill the entire worker pool.
|
||||
if numRunningJobs < runtime.NumCPU() {
|
||||
jobToRun := nextJob(jobs)
|
||||
if jobToRun != nil {
|
||||
// Start job.
|
||||
if jobRunnerDebug {
|
||||
fmt.Println("## start: ", jobToRun.description)
|
||||
}
|
||||
jobToRun.state = jobStateRunning
|
||||
workerChan <- jobToRun
|
||||
numRunningJobs++
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// When there are no jobs running, all jobs in the jobs slice must have
|
||||
// been finished. Therefore, the work is done.
|
||||
if numRunningJobs == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Wait until a job is finished.
|
||||
job := <-doneChan
|
||||
job.state = jobStateFinished
|
||||
numRunningJobs--
|
||||
totalTime += job.duration
|
||||
if jobRunnerDebug {
|
||||
fmt.Println("## finished:", job.description, "(time "+job.duration.String()+")")
|
||||
}
|
||||
if job.err != nil {
|
||||
// Wait for running jobs to finish.
|
||||
for numRunningJobs != 0 {
|
||||
<-doneChan
|
||||
numRunningJobs--
|
||||
}
|
||||
// Return error of first failing job.
|
||||
return job.err
|
||||
}
|
||||
}
|
||||
|
||||
// Some statistics, if debugging.
|
||||
if jobRunnerDebug {
|
||||
// Total duration of running all jobs.
|
||||
duration := time.Since(start)
|
||||
fmt.Println("## total: ", duration)
|
||||
|
||||
// The individual time of each job combined. On a multicore system, this
|
||||
// should be lower than the total above.
|
||||
fmt.Println("## job sum: ", totalTime)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// nextJob returns the first ready-to-run job.
|
||||
// This is an implementation detail of runJobs.
|
||||
func nextJob(jobs []*compileJob) *compileJob {
|
||||
for _, job := range jobs {
|
||||
if job.readyToRun() {
|
||||
return job
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// jobWorker is the goroutine that runs received jobs.
|
||||
// This is an implementation detail of runJobs.
|
||||
func jobWorker(workerChan, doneChan chan *compileJob) {
|
||||
for job := range workerChan {
|
||||
start := time.Now()
|
||||
err := job.run()
|
||||
if err != nil {
|
||||
job.err = err
|
||||
}
|
||||
job.duration = time.Since(start)
|
||||
doneChan <- job
|
||||
}
|
||||
}
|
||||
+74
-29
@@ -1,7 +1,6 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -40,34 +39,64 @@ func (l *Library) sourcePaths(target string) []string {
|
||||
}
|
||||
|
||||
// Load the library archive, possibly generating and caching it if needed.
|
||||
func (l *Library) Load(target string) (path string, err error) {
|
||||
// The resulting file is stored in the provided tmpdir, which is expected to be
|
||||
// removed after the Load call.
|
||||
func (l *Library) Load(target, tmpdir string) (path string, err error) {
|
||||
path, job, err := l.load(target, "", tmpdir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if job != nil {
|
||||
jobs := append([]*compileJob{job}, job.dependencies...)
|
||||
err = runJobs(jobs)
|
||||
}
|
||||
return path, err
|
||||
}
|
||||
|
||||
// load returns a path to the library file for the given target, loading it from
|
||||
// cache if possible. It will return a non-zero compiler job if the library
|
||||
// wasn't cached, this job (and its dependencies) must be run before the library
|
||||
// path is valid.
|
||||
// The provided tmpdir will be used to store intermediary files and possibly the
|
||||
// output archive file, it is expected to be removed after use.
|
||||
func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob, err error) {
|
||||
// Try to load a precompiled library.
|
||||
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
|
||||
if _, err := os.Stat(precompiledPath); err == nil {
|
||||
// Found a precompiled library for this OS/architecture. Return the path
|
||||
// directly.
|
||||
return precompiledPath, nil
|
||||
return precompiledPath, nil, nil
|
||||
}
|
||||
|
||||
outfile := l.name + "-" + target + ".a"
|
||||
var outfile string
|
||||
if cpu != "" {
|
||||
outfile = l.name + "-" + target + "-" + cpu + ".a"
|
||||
} else {
|
||||
outfile = l.name + "-" + target + ".a"
|
||||
}
|
||||
|
||||
// Try to fetch this library from the cache.
|
||||
if path, err := cacheLoad(outfile, commands["clang"][0], l.sourcePaths(target)); path != "" || err != nil {
|
||||
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil {
|
||||
// Cache hit.
|
||||
return path, err
|
||||
return path, nil, err
|
||||
}
|
||||
// Cache miss, build it now.
|
||||
|
||||
dirPrefix := "tinygo-" + l.name
|
||||
remapDir := filepath.Join(os.TempDir(), dirPrefix)
|
||||
dir, err := ioutil.TempDir(os.TempDir(), dirPrefix)
|
||||
remapDir := filepath.Join(os.TempDir(), "tinygo-"+l.name)
|
||||
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
|
||||
err = os.Mkdir(dir, 0777)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", nil, err
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Precalculate the flags to the compiler invocation.
|
||||
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||
// reproducible. Otherwise the temporary directory is stored in the archive
|
||||
// itself, which varies each run.
|
||||
args := append(l.cflags(), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
if cpu != "" {
|
||||
args = append(args, "-mcpu="+cpu)
|
||||
}
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft")
|
||||
}
|
||||
@@ -78,28 +107,44 @@ func (l *Library) Load(target string) (path string, err error) {
|
||||
args = append(args, "-march=rv64gc", "-mabi=lp64")
|
||||
}
|
||||
|
||||
// Compile all sources.
|
||||
// Create job to put all the object files in a single archive. This archive
|
||||
// file is the (static) library file.
|
||||
var objs []string
|
||||
arpath := filepath.Join(dir, l.name+".a")
|
||||
job = &compileJob{
|
||||
description: "ar " + l.name + ".a",
|
||||
run: func() error {
|
||||
// Create an archive of all object files.
|
||||
err := makeArchive(arpath, objs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Store this archive in the cache.
|
||||
_, err = cacheStore(arpath, outfile, l.sourcePaths(target))
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
// Create jobs to compile all sources. These jobs are depended upon by the
|
||||
// archive job above, so must be run first.
|
||||
for _, srcpath := range l.sourcePaths(target) {
|
||||
srcpath := srcpath // avoid concurrency issues by redefining inside the loop
|
||||
objpath := filepath.Join(dir, filepath.Base(srcpath)+".o")
|
||||
objs = append(objs, objpath)
|
||||
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||
// reproducible. Otherwise the temporary directory is stored in the
|
||||
// archive itself, which varies each run.
|
||||
err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...)
|
||||
if err != nil {
|
||||
return "", &commandError{"failed to build", srcpath, err}
|
||||
}
|
||||
job.dependencies = append(job.dependencies, &compileJob{
|
||||
description: "compile " + srcpath,
|
||||
run: func() error {
|
||||
var compileArgs []string
|
||||
compileArgs = append(compileArgs, args...)
|
||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||
err := runCCompiler("clang", compileArgs...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Put all the object files in a single archive. This archive file will be
|
||||
// used to statically link this library.
|
||||
arpath := filepath.Join(dir, l.name+".a")
|
||||
err = makeArchive(arpath, objs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Store this archive in the cache.
|
||||
return cacheStore(arpath, outfile, commands["clang"][0], l.sourcePaths(target))
|
||||
return arpath, job, nil
|
||||
}
|
||||
|
||||
+14
-3
@@ -54,9 +54,10 @@ type constantInfo struct {
|
||||
// functionInfo stores some information about a CGo function found by libclang
|
||||
// and declared in the AST.
|
||||
type functionInfo struct {
|
||||
args []paramInfo
|
||||
results *ast.FieldList
|
||||
pos token.Pos
|
||||
args []paramInfo
|
||||
results *ast.FieldList
|
||||
pos token.Pos
|
||||
variadic bool
|
||||
}
|
||||
|
||||
// paramInfo is a parameter of a CGo function (see functionInfo).
|
||||
@@ -484,6 +485,16 @@ func (p *cgoPackage) addFuncDecls() {
|
||||
Results: fn.results,
|
||||
},
|
||||
}
|
||||
if fn.variadic {
|
||||
decl.Doc = &ast.CommentGroup{
|
||||
List: []*ast.Comment{
|
||||
&ast.Comment{
|
||||
Slash: fn.pos,
|
||||
Text: "//go:variadic",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
obj.Decl = decl
|
||||
for i, arg := range fn.args {
|
||||
args[i] = &ast.Field{
|
||||
|
||||
+18
-3
@@ -5,6 +5,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
@@ -23,7 +24,7 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
||||
// normalizeResult normalizes Go source code that comes out of tests across
|
||||
// platforms and Go versions.
|
||||
func normalizeResult(result string) string {
|
||||
actual := strings.Replace(result, "\r\n", "\n", -1)
|
||||
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
||||
|
||||
// Make sure all functions are wrapped, even those that would otherwise be
|
||||
// single-line functions. This is necessary because Go 1.14 changed the way
|
||||
@@ -41,6 +42,20 @@ func TestCGo(t *testing.T) {
|
||||
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
|
||||
name := name // avoid a race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Skip tests that require specific Go version.
|
||||
if name == "errors" {
|
||||
ok := false
|
||||
for _, version := range build.Default.ReleaseTags {
|
||||
if version == "go1.16" {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
t.Skip("Results for errors test are only valid for Go 1.16+")
|
||||
}
|
||||
}
|
||||
|
||||
// Read the AST in memory.
|
||||
path := filepath.Join("testdata", name+".go")
|
||||
fset := token.NewFileSet()
|
||||
@@ -98,7 +113,7 @@ func TestCGo(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("could not read expected output: %v", err)
|
||||
}
|
||||
expected := strings.Replace(string(expectedBytes), "\r\n", "\n", -1)
|
||||
expected := strings.ReplaceAll(string(expectedBytes), "\r\n", "\n")
|
||||
|
||||
// Check whether the output is as expected.
|
||||
if expected != actual {
|
||||
@@ -139,7 +154,7 @@ func formatDiagnostic(err error) string {
|
||||
msg := err.Error()
|
||||
if runtime.GOOS == "windows" {
|
||||
// Fix Windows path slashes.
|
||||
msg = strings.Replace(msg, "testdata\\", "testdata/", -1)
|
||||
msg = strings.ReplaceAll(msg, "testdata\\", "testdata/")
|
||||
}
|
||||
return "// " + msg + "\n"
|
||||
}
|
||||
|
||||
+2
-4
@@ -152,12 +152,10 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||
return C.CXChildVisit_Continue // not supported
|
||||
}
|
||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||
fn := &functionInfo{
|
||||
pos: pos,
|
||||
pos: pos,
|
||||
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
|
||||
}
|
||||
p.functions[name] = fn
|
||||
for i := 0; i < numArgs; i++ {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// +build !byollvm
|
||||
// +build !llvm9,!llvm11
|
||||
// +build !llvm10
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-10/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@10/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm10/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-10/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@10/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm10/lib -lclang
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// +build !byollvm
|
||||
// +build llvm10
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-10/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@10/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm10/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-10/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@10/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm10/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -1,14 +0,0 @@
|
||||
// +build !byollvm
|
||||
// +build llvm11
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
@@ -1,14 +0,0 @@
|
||||
// +build !byollvm
|
||||
// +build llvm9
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-9/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@9/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm9/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm9/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
Vendored
+2
-2
@@ -4,10 +4,10 @@
|
||||
// testdata/errors.go:13:23: unexpected token )
|
||||
|
||||
// Type checking errors after CGo processing:
|
||||
// testdata/errors.go:102: 2 << 10 (untyped int constant 2048) overflows uint8
|
||||
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
|
||||
// testdata/errors.go:105: unknown field z in struct literal
|
||||
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
|
||||
// testdata/errors.go:110: C.SOME_CONST_3 (untyped int constant 1234) overflows byte
|
||||
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
|
||||
|
||||
package main
|
||||
|
||||
|
||||
Vendored
+10
@@ -104,6 +104,10 @@ typedef struct {
|
||||
unsigned char e : 3;
|
||||
// Note that C++ allows bitfields bigger than the underlying type.
|
||||
} bitfield_t;
|
||||
|
||||
// Function signatures.
|
||||
void variadic0();
|
||||
void variadic2(int x, int y, ...);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -163,3 +167,9 @@ func accessUnion() {
|
||||
var _ *C.int = union2d.unionfield_i()
|
||||
var _ *[2]float64 = union2d.unionfield_d()
|
||||
}
|
||||
|
||||
// Test function signatures.
|
||||
func accessFunctions() {
|
||||
C.variadic0()
|
||||
C.variadic2(3, 5)
|
||||
}
|
||||
|
||||
Vendored
+5
@@ -4,6 +4,11 @@ import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
func C.variadic0() //go:variadic
|
||||
func C.variadic2(x C.int, y C.int) //go:variadic
|
||||
var C.variadic0$funcaddr unsafe.Pointer
|
||||
var C.variadic2$funcaddr unsafe.Pointer
|
||||
|
||||
const C.option2A = 20
|
||||
const C.optionA = 0
|
||||
const C.optionB = 1
|
||||
|
||||
+21
-38
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
@@ -22,29 +21,6 @@ type Config struct {
|
||||
TestConfig TestConfig
|
||||
}
|
||||
|
||||
// FuncValueImplementation is an enum for the particular implementations of Go
|
||||
// func values.
|
||||
type FuncValueImplementation int
|
||||
|
||||
// These constants describe the various possible implementations of Go func
|
||||
// values.
|
||||
const (
|
||||
FuncValueNone FuncValueImplementation = iota
|
||||
|
||||
// A func value is implemented as a pair of pointers:
|
||||
// {context, function pointer}
|
||||
// where the context may be a pointer to a heap-allocated struct containing
|
||||
// the free variables, or it may be undef if the function being pointed to
|
||||
// doesn't need a context. The function pointer is a regular function
|
||||
// pointer.
|
||||
FuncValueDoubleword
|
||||
|
||||
// As funcValueDoubleword, but with the function pointer replaced by a
|
||||
// unique ID per function signature. Function values are called by using a
|
||||
// switch statement and choosing which function to call.
|
||||
FuncValueSwitch
|
||||
)
|
||||
|
||||
// Triple returns the LLVM target triple, like armv6m-none-eabi.
|
||||
func (c *Config) Triple() string {
|
||||
return c.Target.Triple
|
||||
@@ -144,14 +120,24 @@ func (c *Config) Scheduler() string {
|
||||
|
||||
// FuncImplementation picks an appropriate func value implementation for the
|
||||
// target.
|
||||
func (c *Config) FuncImplementation() FuncValueImplementation {
|
||||
// Always pick the switch implementation, as it allows the use of blocking
|
||||
// inside a function that is used as a func value.
|
||||
func (c *Config) FuncImplementation() string {
|
||||
switch c.Scheduler() {
|
||||
case "none", "coroutines":
|
||||
return FuncValueSwitch
|
||||
case "tasks":
|
||||
return FuncValueDoubleword
|
||||
// A func value is implemented as a pair of pointers:
|
||||
// {context, function pointer}
|
||||
// where the context may be a pointer to a heap-allocated struct
|
||||
// containing the free variables, or it may be undef if the function
|
||||
// being pointed to doesn't need a context. The function pointer is a
|
||||
// regular function pointer.
|
||||
return "doubleword"
|
||||
case "none", "coroutines":
|
||||
// As "doubleword", but with the function pointer replaced by a unique
|
||||
// ID per function signature. Function values are called by using a
|
||||
// switch statement and choosing which function to call.
|
||||
// Pick the switch implementation with the coroutines scheduler, as it
|
||||
// allows the use of blocking inside a function that is used as a func
|
||||
// value.
|
||||
return "switch"
|
||||
default:
|
||||
panic("unknown scheduler type")
|
||||
}
|
||||
@@ -179,7 +165,7 @@ func (c *Config) AutomaticStackSize() bool {
|
||||
func (c *Config) CFlags() []string {
|
||||
cflags := append([]string{}, c.Options.CFlags...)
|
||||
for _, flag := range c.Target.CFlags {
|
||||
cflags = append(cflags, strings.Replace(flag, "{root}", goenv.Get("TINYGOROOT"), -1))
|
||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||
}
|
||||
if c.Target.Libc == "picolibc" {
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
@@ -200,15 +186,9 @@ func (c *Config) LDFlags() []string {
|
||||
// Merge and adjust LDFlags.
|
||||
ldflags := append([]string{}, c.Options.LDFlags...)
|
||||
for _, flag := range c.Target.LDFlags {
|
||||
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
|
||||
ldflags = append(ldflags, strings.ReplaceAll(flag, "{root}", root))
|
||||
}
|
||||
ldflags = append(ldflags, "-L", root)
|
||||
if c.Target.GOARCH == "wasm" {
|
||||
// Round heap size to next multiple of 65536 (the WebAssembly page
|
||||
// size).
|
||||
heapSize := (c.Options.HeapSize + (65536 - 1)) &^ (65536 - 1)
|
||||
ldflags = append(ldflags, "--initial-memory="+strconv.FormatInt(heapSize, 10))
|
||||
}
|
||||
if c.Target.LinkerScript != "" {
|
||||
ldflags = append(ldflags, "-T", c.Target.LinkerScript)
|
||||
}
|
||||
@@ -303,6 +283,9 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
|
||||
return nil, fmt.Errorf("unknown OpenOCD transport: %#v", c.Target.OpenOCDTransport)
|
||||
}
|
||||
args = []string{"-f", "interface/" + openocdInterface + ".cfg"}
|
||||
for _, cmd := range c.Target.OpenOCDCommands {
|
||||
args = append(args, "-c", cmd)
|
||||
}
|
||||
if c.Target.OpenOCDTransport != "" {
|
||||
args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ type Options struct {
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands bool
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintStacks bool
|
||||
@@ -30,7 +31,6 @@ type Options struct {
|
||||
LDFlags []string
|
||||
Tags string
|
||||
WasmAbi string
|
||||
HeapSize int64
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ type TargetSpec struct {
|
||||
OpenOCDInterface string `json:"openocd-interface"`
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
OpenOCDCommands []string `json:"openocd-commands"`
|
||||
JLinkDevice string `json:"jlink-device"`
|
||||
CodeModel string `json:"code-model"`
|
||||
RelocationModel string `json:"relocation-model"`
|
||||
|
||||
+6
-6
@@ -16,7 +16,7 @@ import (
|
||||
// slice. This is required by the Go language spec: an index out of bounds must
|
||||
// cause a panic.
|
||||
func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) {
|
||||
if b.fn.IsNoBounds() {
|
||||
if b.info.nobounds {
|
||||
// The //go:nobounds pragma was added to the function to avoid bounds
|
||||
// checking.
|
||||
return
|
||||
@@ -48,7 +48,7 @@ func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType
|
||||
// biggest possible slice capacity, 'low' means len and 'high' means cap. The
|
||||
// logic is the same in both cases.
|
||||
func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) {
|
||||
if b.fn.IsNoBounds() {
|
||||
if b.info.nobounds {
|
||||
// The //go:nobounds pragma was added to the function to avoid bounds
|
||||
// checking.
|
||||
return
|
||||
@@ -104,7 +104,7 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
|
||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||
// check that the value is not too big for runtime.chanMake.
|
||||
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
|
||||
if b.fn.IsNoBounds() {
|
||||
if b.info.nobounds {
|
||||
// The //go:nobounds pragma was added to the function to avoid bounds
|
||||
// checking.
|
||||
return
|
||||
@@ -189,7 +189,7 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str
|
||||
// createNegativeShiftCheck creates an assertion that panics if the given shift value is negative.
|
||||
// This function assumes that the shift value is signed.
|
||||
func (b *builder) createNegativeShiftCheck(shift llvm.Value) {
|
||||
if b.fn.IsNoBounds() {
|
||||
if b.info.nobounds {
|
||||
// Function disabled bounds checking - skip shift check.
|
||||
return
|
||||
}
|
||||
@@ -212,8 +212,8 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
||||
}
|
||||
}
|
||||
|
||||
faultBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".throw")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".next")
|
||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
|
||||
// Now branch to the out-of-bounds or the regular block.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -35,6 +37,31 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
ptr := b.getValue(call.Args[0])
|
||||
old := b.getValue(call.Args[1])
|
||||
newVal := b.getValue(call.Args[2])
|
||||
if strings.HasSuffix(name, "64") {
|
||||
arch := strings.Split(b.Triple, "-")[0]
|
||||
if strings.HasPrefix(arch, "arm") && strings.HasSuffix(arch, "m") {
|
||||
// Work around a bug in LLVM, at least LLVM 11:
|
||||
// https://reviews.llvm.org/D95891
|
||||
// Check for armv6m, armv7, armv7em, and perhaps others.
|
||||
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
|
||||
if compareAndSwap.IsNil() {
|
||||
// Declare the function if it isn't already declared.
|
||||
i64Type := b.ctx.Int64Type()
|
||||
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
|
||||
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
|
||||
}
|
||||
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
|
||||
// The __sync_val_compare_and_swap_8 function returns the old
|
||||
// value. However, we shouldn't return the old value, we should
|
||||
// return whether the compare/exchange was successful. This is
|
||||
// easily done by comparing the returned (actual) old value with
|
||||
// the expected old value passed to
|
||||
// __sync_val_compare_and_swap_8.
|
||||
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
|
||||
return swapped, true
|
||||
}
|
||||
}
|
||||
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
|
||||
swapped := b.CreateExtractValue(tuple, 1, "")
|
||||
return swapped, true
|
||||
|
||||
+6
-5
@@ -4,6 +4,7 @@ import (
|
||||
"go/types"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -34,14 +35,14 @@ const (
|
||||
|
||||
// createCall creates a new call to runtime.<fnName> with the given arguments.
|
||||
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
|
||||
fullName := "runtime." + fnName
|
||||
fn := b.mod.NamedFunction(fullName)
|
||||
if fn.IsNil() {
|
||||
panic("trying to call non-existent function: " + fullName)
|
||||
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
|
||||
llvmFn := b.getFunction(fn)
|
||||
if llvmFn.IsNil() {
|
||||
panic("trying to call non-existent function: " + fn.RelString(nil))
|
||||
}
|
||||
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
|
||||
args = append(args, llvm.ConstPointerNull(b.i8ptrType)) // coroutine handle
|
||||
return b.createCall(fn, args, name)
|
||||
return b.createCall(llvmFn, args, name)
|
||||
}
|
||||
|
||||
// createCall creates a call to the given function with the arguments possibly
|
||||
|
||||
+1
-2
@@ -79,8 +79,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||
}
|
||||
|
||||
// createChanClose closes the given channel.
|
||||
func (b *builder) createChanClose(param ssa.Value) {
|
||||
ch := b.getValue(param)
|
||||
func (b *builder) createChanClose(ch llvm.Value) {
|
||||
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
||||
}
|
||||
|
||||
|
||||
+385
-382
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,172 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Pass -update to go test to update the output of the test files.
|
||||
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
||||
|
||||
// Basic tests for the compiler. Build some Go files and compare the output with
|
||||
// the expected LLVM IR for regression testing.
|
||||
func TestCompiler(t *testing.T) {
|
||||
target, err := compileopts.LoadTarget("i686--linux")
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target:", err)
|
||||
}
|
||||
config := &compileopts.Config{
|
||||
Options: &compileopts.Options{},
|
||||
Target: target,
|
||||
}
|
||||
compilerConfig := &Config{
|
||||
Triple: config.Triple(),
|
||||
GOOS: config.GOOS(),
|
||||
GOARCH: config.GOARCH(),
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
Scheduler: config.Scheduler(),
|
||||
FuncImplementation: config.FuncImplementation(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
}
|
||||
machine, err := NewTargetMachine(compilerConfig)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
|
||||
tests := []string{
|
||||
"basic.go",
|
||||
"pointer.go",
|
||||
"slice.go",
|
||||
"float.go",
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
t.Run(testCase, func(t *testing.T) {
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{"./testdata/" + testCase}, config.ClangHeaders, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse test case %s: %s", testCase, err)
|
||||
}
|
||||
|
||||
// Compile AST to IR.
|
||||
pkg := lprogram.MainPkg()
|
||||
mod, errs := CompilePackage(testCase, pkg, machine, compilerConfig, false)
|
||||
if errs != nil {
|
||||
for _, err := range errs {
|
||||
t.Log("error:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Optimize IR a little.
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
funcPasses.AddInstructionCombiningPass()
|
||||
funcPasses.InitializeFunc()
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
funcPasses.RunFunc(fn)
|
||||
}
|
||||
funcPasses.FinalizeFunc()
|
||||
|
||||
outfile := "./testdata/" + testCase[:len(testCase)-3] + ".ll"
|
||||
|
||||
// Update test if needed. Do not check the result.
|
||||
if *flagUpdate {
|
||||
err := ioutil.WriteFile(outfile, []byte(mod.String()), 0666)
|
||||
if err != nil {
|
||||
t.Error("failed to write updated output file:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(outfile)
|
||||
if err != nil {
|
||||
t.Fatal("failed to read golden file:", err)
|
||||
}
|
||||
|
||||
if !fuzzyEqualIR(mod.String(), string(expected)) {
|
||||
t.Errorf("output does not match expected output:\n%s", mod.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var alignRegexp = regexp.MustCompile(", align [0-9]+$")
|
||||
|
||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
||||
// equal. That means, only relevant lines are compared (excluding comments
|
||||
// etc.).
|
||||
func fuzzyEqualIR(s1, s2 string) bool {
|
||||
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
||||
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
||||
if len(lines1) != len(lines2) {
|
||||
return false
|
||||
}
|
||||
for i, line1 := range lines1 {
|
||||
line2 := lines2[i]
|
||||
match1 := alignRegexp.MatchString(line1)
|
||||
match2 := alignRegexp.MatchString(line2)
|
||||
if match1 != match2 {
|
||||
// Only one of the lines has the align keyword. Remove it.
|
||||
// This is a change to make the test work in both LLVM 10 and LLVM
|
||||
// 11 (LLVM 11 appears to automatically add alignment everywhere).
|
||||
line1 = alignRegexp.ReplaceAllString(line1, "")
|
||||
line2 = alignRegexp.ReplaceAllString(line2, "")
|
||||
}
|
||||
if line1 != line2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// filterIrrelevantIRLines removes lines from the input slice of strings that
|
||||
// are not relevant in comparing IR. For example, empty lines and comments are
|
||||
// stripped out.
|
||||
func filterIrrelevantIRLines(lines []string) []string {
|
||||
var out []string
|
||||
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 _, line := range lines {
|
||||
line = strings.Split(line, ";")[0] // strip out comments/info
|
||||
line = strings.TrimRight(line, "\r ") // drop '\r' on Windows and remove trailing spaces from comments
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "source_filename = ") {
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 10 && strings.HasPrefix(line, "attributes ") {
|
||||
// Ignore attribute groups. These may change between LLVM versions.
|
||||
// Right now test outputs are for LLVM 10.
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 10 && strings.HasPrefix(line, "target datalayout ") {
|
||||
// Ignore the target layout. This may change between LLVM versions.
|
||||
continue
|
||||
}
|
||||
out = append(out, line)
|
||||
}
|
||||
return out
|
||||
}
|
||||
+38
-38
@@ -14,9 +14,9 @@ package compiler
|
||||
// frames.
|
||||
|
||||
import (
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"github.com/tinygo-org/tinygo/ir"
|
||||
"go/types"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -26,9 +26,9 @@ import (
|
||||
// calls.
|
||||
func (b *builder) deferInitFunc() {
|
||||
// Some setup.
|
||||
b.deferFuncs = make(map[*ir.Function]int)
|
||||
b.deferFuncs = make(map[*ssa.Function]int)
|
||||
b.deferInvokeFuncs = make(map[string]int)
|
||||
b.deferClosureFuncs = make(map[*ir.Function]int)
|
||||
b.deferClosureFuncs = make(map[*ssa.Function]int)
|
||||
b.deferExprFuncs = make(map[ssa.Value]int)
|
||||
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
||||
|
||||
@@ -107,13 +107,11 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
|
||||
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
|
||||
// Regular function call.
|
||||
fn := b.ir.GetFunction(callee)
|
||||
|
||||
if _, ok := b.deferFuncs[fn]; !ok {
|
||||
b.deferFuncs[fn] = len(b.allDeferFuncs)
|
||||
b.allDeferFuncs = append(b.allDeferFuncs, fn)
|
||||
if _, ok := b.deferFuncs[callee]; !ok {
|
||||
b.deferFuncs[callee] = len(b.allDeferFuncs)
|
||||
b.allDeferFuncs = append(b.allDeferFuncs, callee)
|
||||
}
|
||||
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[fn]), false)
|
||||
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[callee]), false)
|
||||
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields).
|
||||
@@ -135,7 +133,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
context := b.CreateExtractValue(closure, 0, "")
|
||||
|
||||
// Get the callback number.
|
||||
fn := b.ir.GetFunction(makeClosure.Fn.(*ssa.Function))
|
||||
fn := makeClosure.Fn.(*ssa.Function)
|
||||
if _, ok := b.deferClosureFuncs[fn]; !ok {
|
||||
b.deferClosureFuncs[fn] = len(b.allDeferFuncs)
|
||||
b.allDeferFuncs = append(b.allDeferFuncs, makeClosure)
|
||||
@@ -155,19 +153,19 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
valueTypes = append(valueTypes, context.Type())
|
||||
|
||||
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||
var funcName string
|
||||
switch builtin.Name() {
|
||||
case "close":
|
||||
funcName = "chanClose"
|
||||
default:
|
||||
b.addError(instr.Pos(), "todo: Implement defer for "+builtin.Name())
|
||||
return
|
||||
var argTypes []types.Type
|
||||
var argValues []llvm.Value
|
||||
for _, arg := range instr.Call.Args {
|
||||
argTypes = append(argTypes, arg.Type())
|
||||
argValues = append(argValues, b.getValue(arg))
|
||||
}
|
||||
|
||||
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
|
||||
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
|
||||
funcName,
|
||||
len(b.allDeferFuncs),
|
||||
callName: builtin.Name(),
|
||||
pos: builtin.Pos(),
|
||||
argTypes: argTypes,
|
||||
callback: len(b.allDeferFuncs),
|
||||
}
|
||||
b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
|
||||
}
|
||||
@@ -176,10 +174,9 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
// Collect all values to be put in the struct (starting with
|
||||
// runtime._defer fields).
|
||||
values = []llvm.Value{callback, next}
|
||||
for _, param := range instr.Call.Args {
|
||||
llvmParam := b.getValue(param)
|
||||
values = append(values, llvmParam)
|
||||
valueTypes = append(valueTypes, llvmParam.Type())
|
||||
for _, param := range argValues {
|
||||
values = append(values, param)
|
||||
valueTypes = append(valueTypes, param.Type())
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -223,7 +220,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
|
||||
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||
}
|
||||
if b.NeedsStackObjects() {
|
||||
if b.NeedsStackObjects {
|
||||
b.trackPointer(alloca)
|
||||
}
|
||||
b.CreateStore(deferFrame, alloca)
|
||||
@@ -251,10 +248,10 @@ func (b *builder) createRunDefers() {
|
||||
// }
|
||||
|
||||
// Create loop.
|
||||
loophead := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loophead")
|
||||
loop := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loop")
|
||||
unreachable := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.default")
|
||||
end := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.end")
|
||||
loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead")
|
||||
loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop")
|
||||
unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default")
|
||||
end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end")
|
||||
b.CreateBr(loophead)
|
||||
|
||||
// Create loop head:
|
||||
@@ -286,7 +283,7 @@ func (b *builder) createRunDefers() {
|
||||
// Create switch case, for example:
|
||||
// case 0:
|
||||
// // run first deferred call
|
||||
block := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.callback")
|
||||
block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback")
|
||||
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
|
||||
b.SetInsertPointAtEnd(block)
|
||||
switch callback := callback.(type) {
|
||||
@@ -350,7 +347,7 @@ func (b *builder) createRunDefers() {
|
||||
|
||||
b.createCall(fnPtr, forwardParams, "")
|
||||
|
||||
case *ir.Function:
|
||||
case *ssa.Function:
|
||||
// Direct call.
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
@@ -372,7 +369,7 @@ func (b *builder) createRunDefers() {
|
||||
|
||||
// Plain TinyGo functions add some extra parameters to implement async functionality and function recievers.
|
||||
// These parameters should not be supplied when calling into an external C/ASM function.
|
||||
if !callback.IsExported() {
|
||||
if !b.getFunctionInfo(callback).exported {
|
||||
// Add the context parameter. We know it is ignored by the receiving
|
||||
// function, but we have to pass one anyway.
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
@@ -382,11 +379,11 @@ func (b *builder) createRunDefers() {
|
||||
}
|
||||
|
||||
// Call real function.
|
||||
b.createCall(callback.LLVMFn, forwardParams, "")
|
||||
b.createCall(b.getFunction(callback), forwardParams, "")
|
||||
|
||||
case *ssa.MakeClosure:
|
||||
// Get the real defer struct type and cast to it.
|
||||
fn := b.ir.GetFunction(callback.Fn.(*ssa.Function))
|
||||
fn := callback.Fn.(*ssa.Function)
|
||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||
params := fn.Signature.Params()
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
@@ -409,7 +406,7 @@ func (b *builder) createRunDefers() {
|
||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||
|
||||
// Call deferred function.
|
||||
b.createCall(fn.LLVMFn, forwardParams, "")
|
||||
b.createCall(b.getFunction(fn), forwardParams, "")
|
||||
case *ssa.Builtin:
|
||||
db := b.deferBuiltinFuncs[callback]
|
||||
|
||||
@@ -426,15 +423,18 @@ func (b *builder) createRunDefers() {
|
||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||
|
||||
// Extract the params from the struct.
|
||||
var forwardParams []llvm.Value
|
||||
var argValues []llvm.Value
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
argValues = append(argValues, forwardParam)
|
||||
}
|
||||
|
||||
b.createRuntimeCall(db.funcName, forwardParams, "")
|
||||
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
|
||||
if err != nil {
|
||||
b.diagnostics = append(b.diagnostics, err)
|
||||
}
|
||||
default:
|
||||
panic("unknown deferred function type")
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import (
|
||||
// makeError makes it easy to create an error from a token.Pos with a message.
|
||||
func (c *compilerContext) makeError(pos token.Pos, msg string) types.Error {
|
||||
return types.Error{
|
||||
Fset: c.ir.Program.Fset,
|
||||
Fset: c.program.Fset,
|
||||
Pos: pos,
|
||||
Msg: msg,
|
||||
}
|
||||
|
||||
+11
-12
@@ -6,7 +6,6 @@ package compiler
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -21,11 +20,11 @@ func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signat
|
||||
// context.
|
||||
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
||||
var funcValueScalar llvm.Value
|
||||
switch c.FuncImplementation() {
|
||||
case compileopts.FuncValueDoubleword:
|
||||
switch c.FuncImplementation {
|
||||
case "doubleword":
|
||||
// Closure is: {context, function pointer}
|
||||
funcValueScalar = funcPtr
|
||||
case compileopts.FuncValueSwitch:
|
||||
case "switch":
|
||||
sigGlobal := c.getTypeCode(sig)
|
||||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||
@@ -67,10 +66,10 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
||||
// value. This may be an expensive operation.
|
||||
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
||||
context = b.CreateExtractValue(funcValue, 0, "")
|
||||
switch b.FuncImplementation() {
|
||||
case compileopts.FuncValueDoubleword:
|
||||
switch b.FuncImplementation {
|
||||
case "doubleword":
|
||||
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
||||
case compileopts.FuncValueSwitch:
|
||||
case "switch":
|
||||
llvmSig := b.getRawFuncType(sig)
|
||||
sigGlobal := b.getTypeCode(sig)
|
||||
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||
@@ -83,11 +82,11 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
|
||||
|
||||
// getFuncType returns the type of a func value given a signature.
|
||||
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
||||
switch c.FuncImplementation() {
|
||||
case compileopts.FuncValueDoubleword:
|
||||
switch c.FuncImplementation {
|
||||
case "doubleword":
|
||||
rawPtr := c.getRawFuncType(typ)
|
||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
||||
case compileopts.FuncValueSwitch:
|
||||
case "switch":
|
||||
return c.getLLVMRuntimeType("funcValue")
|
||||
default:
|
||||
panic("unimplemented func value variant")
|
||||
@@ -149,7 +148,7 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
||||
if len(expr.Bindings) == 0 {
|
||||
panic("unexpected: MakeClosure without bound variables")
|
||||
}
|
||||
f := b.ir.GetFunction(expr.Fn.(*ssa.Function))
|
||||
f := expr.Fn.(*ssa.Function)
|
||||
|
||||
// Collect all bound variables.
|
||||
boundVars := make([]llvm.Value, len(expr.Bindings))
|
||||
@@ -164,5 +163,5 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
||||
context := b.emitPointerPack(boundVars)
|
||||
|
||||
// Create the closure.
|
||||
return b.createFuncValue(f.LLVMFn, context, f.Signature), nil
|
||||
return b.createFuncValue(b.getFunction(f), context, f.Signature), nil
|
||||
}
|
||||
|
||||
+12
-9
@@ -7,6 +7,7 @@ import (
|
||||
"go/token"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -20,19 +21,20 @@ import (
|
||||
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
|
||||
paramBundle := b.emitPointerPack(params)
|
||||
var callee, stackSize llvm.Value
|
||||
switch b.Scheduler() {
|
||||
switch b.Scheduler {
|
||||
case "none", "tasks":
|
||||
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
|
||||
if b.AutomaticStackSize() {
|
||||
if b.AutomaticStackSize {
|
||||
// The stack size is not known until after linking. Call a dummy
|
||||
// function that will be replaced with a load from a special ELF
|
||||
// section that contains the stack size (and is modified after
|
||||
// linking).
|
||||
stackSize = b.createCall(b.mod.NamedFunction("internal/task.getGoroutineStackSize"), []llvm.Value{callee, llvm.Undef(b.i8ptrType), llvm.Undef(b.i8ptrType)}, "stacksize")
|
||||
stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
|
||||
stackSize = b.createCall(stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType), llvm.Undef(b.i8ptrType)}, "stacksize")
|
||||
} else {
|
||||
// The stack size is fixed at compile time. By emitting it here as a
|
||||
// constant, it can be optimized.
|
||||
stackSize = llvm.ConstInt(b.uintptrType, b.Target.DefaultStackSize, false)
|
||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||
}
|
||||
case "coroutines":
|
||||
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
|
||||
@@ -42,7 +44,8 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
b.createCall(b.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
|
||||
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function))
|
||||
b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
|
||||
return llvm.Undef(funcPtr.Type().ElementType().ReturnType())
|
||||
}
|
||||
|
||||
@@ -87,8 +90,8 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||
builder.SetInsertPointAtEnd(entry)
|
||||
|
||||
if c.Debug() {
|
||||
pos := c.ir.Program.Fset.Position(pos)
|
||||
if c.Debug {
|
||||
pos := c.program.Fset.Position(pos)
|
||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||
File: c.getDIFile(pos.Filename),
|
||||
Parameters: nil, // do not show parameters in debugger
|
||||
@@ -144,8 +147,8 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||
builder.SetInsertPointAtEnd(entry)
|
||||
|
||||
if c.Debug() {
|
||||
pos := c.ir.Program.Fset.Position(pos)
|
||||
if c.Debug {
|
||||
pos := c.program.Fset.Position(pos)
|
||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||
File: c.getDIFile(pos.Filename),
|
||||
Parameters: nil, // do not show parameters in debugger
|
||||
|
||||
+68
-21
@@ -11,7 +11,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/ir"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -236,7 +235,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
}
|
||||
|
||||
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
||||
ms := c.program.MethodSets.MethodSet(typ)
|
||||
if ms.Len() == 0 {
|
||||
// no methods, so can leave that one out
|
||||
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
|
||||
@@ -247,15 +246,16 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
method := ms.At(i)
|
||||
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
||||
f := c.ir.GetFunction(c.ir.Program.MethodValue(method))
|
||||
if f.LLVMFn.IsNil() {
|
||||
fn := c.program.MethodValue(method)
|
||||
llvmFn := c.getFunction(fn)
|
||||
if llvmFn.IsNil() {
|
||||
// compiler error, so panic
|
||||
panic("cannot find function: " + f.LinkName())
|
||||
panic("cannot find function: " + c.getFunctionInfo(fn).linkName)
|
||||
}
|
||||
fn := c.getInterfaceInvokeWrapper(f)
|
||||
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFn)
|
||||
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
|
||||
signatureGlobal,
|
||||
llvm.ConstPtrToInt(fn, c.uintptrType),
|
||||
llvm.ConstPtrToInt(wrapper, c.uintptrType),
|
||||
})
|
||||
methods[i] = methodInfo
|
||||
}
|
||||
@@ -303,7 +303,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
||||
// external *i8 indicating the indicating the signature of this method. It is
|
||||
// used during the interface lowering pass.
|
||||
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
||||
signature := ir.MethodSignature(method)
|
||||
signature := methodSignature(method)
|
||||
signatureGlobal := c.mod.NamedGlobal("func " + signature)
|
||||
if signatureGlobal.IsNil() {
|
||||
signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature)
|
||||
@@ -357,8 +357,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
// value.
|
||||
|
||||
prevBlock := b.GetInsertBlock()
|
||||
okBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.ok")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.next")
|
||||
okBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.ok")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||
|
||||
@@ -436,8 +436,8 @@ func (b *builder) getInvokeCall(instr *ssa.CallCommon) (llvm.Value, []llvm.Value
|
||||
// value, dereferences or unpacks it if necessary, and calls the real method.
|
||||
// If the method to wrap has a pointer receiver, no wrapping is necessary and
|
||||
// the function is returned directly.
|
||||
func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
wrapperName := f.LinkName() + "$invoke"
|
||||
func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llvm.Value) llvm.Value {
|
||||
wrapperName := llvmFn.Name() + "$invoke"
|
||||
wrapper := c.mod.NamedFunction(wrapperName)
|
||||
if !wrapper.IsNil() {
|
||||
// Wrapper already created. Return it directly.
|
||||
@@ -445,7 +445,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
}
|
||||
|
||||
// Get the expanded receiver type.
|
||||
receiverType := c.getLLVMType(f.Params[0].Type())
|
||||
receiverType := c.getLLVMType(fn.Params[0].Type())
|
||||
var expandedReceiverType []llvm.Type
|
||||
for _, info := range expandFormalParamType(receiverType, "", nil) {
|
||||
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
||||
@@ -457,11 +457,11 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
// Casting a function signature to a different signature and calling it
|
||||
// with a receiver pointer bitcasted to *i8 (as done in calls on an
|
||||
// interface) is hopefully a safe (defined) operation.
|
||||
return f.LLVMFn
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
// create wrapper function
|
||||
fnType := f.LLVMFn.Type().ElementType()
|
||||
fnType := llvmFn.Type().ElementType()
|
||||
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
|
||||
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
|
||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
||||
@@ -478,9 +478,9 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
defer b.Builder.Dispose()
|
||||
|
||||
// add debug info if needed
|
||||
if c.Debug() {
|
||||
pos := c.ir.Program.Fset.Position(f.Pos())
|
||||
difunc := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line)
|
||||
if c.Debug {
|
||||
pos := c.program.Fset.Position(fn.Pos())
|
||||
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line)
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
@@ -490,13 +490,60 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
|
||||
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
|
||||
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
|
||||
if f.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
|
||||
b.CreateCall(f.LLVMFn, params, "")
|
||||
if llvmFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
b.CreateRetVoid()
|
||||
} else {
|
||||
ret := b.CreateCall(f.LLVMFn, params, "ret")
|
||||
ret := b.CreateCall(llvmFn, params, "ret")
|
||||
b.CreateRet(ret)
|
||||
}
|
||||
|
||||
return wrapper
|
||||
}
|
||||
|
||||
// methodSignature creates a readable version of a method signature (including
|
||||
// the function name, excluding the receiver name). This string is used
|
||||
// internally to match interfaces and to call the correct method on an
|
||||
// interface. Examples:
|
||||
//
|
||||
// String() string
|
||||
// Read([]byte) (int, error)
|
||||
func methodSignature(method *types.Func) string {
|
||||
return method.Name() + signature(method.Type().(*types.Signature))
|
||||
}
|
||||
|
||||
// Make a readable version of a function (pointer) signature.
|
||||
// Examples:
|
||||
//
|
||||
// () string
|
||||
// (string, int) (int, error)
|
||||
func signature(sig *types.Signature) string {
|
||||
s := ""
|
||||
if sig.Params().Len() == 0 {
|
||||
s += "()"
|
||||
} else {
|
||||
s += "("
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += sig.Params().At(i).Type().String()
|
||||
}
|
||||
s += ")"
|
||||
}
|
||||
if sig.Results().Len() == 0 {
|
||||
// keep as-is
|
||||
} else if sig.Results().Len() == 1 {
|
||||
s += " " + sig.Results().At(0).Type().String()
|
||||
} else {
|
||||
s += " ("
|
||||
for i := 0; i < sig.Results().Len(); i++ {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += sig.Results().At(i).Type().String()
|
||||
}
|
||||
s += ")"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
|
||||
// Create a new global of type runtime/interrupt.handle. Globals of this
|
||||
// type are lowered in the interrupt lowering pass.
|
||||
globalType := b.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
||||
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
||||
globalLLVMType := b.getLLVMType(globalType)
|
||||
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||
if global := b.mod.NamedGlobal(globalName); !global.IsNil() {
|
||||
@@ -55,8 +55,8 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
global.SetInitializer(initializer)
|
||||
|
||||
// Add debug info to the interrupt global.
|
||||
if b.Debug() {
|
||||
pos := b.ir.Program.Fset.Position(instr.Pos())
|
||||
if b.Debug {
|
||||
pos := b.program.Fset.Position(instr.Pos())
|
||||
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
|
||||
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
||||
LinkageName: globalName,
|
||||
@@ -79,7 +79,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
// PLIC where each interrupt must be enabled using the interrupt number, and
|
||||
// thus keeps the Interrupt object alive.
|
||||
// This call is removed during interrupt lowering.
|
||||
if strings.HasPrefix(b.Triple(), "avr") {
|
||||
if strings.HasPrefix(b.Triple, "avr") {
|
||||
useFn := b.mod.NamedFunction("runtime/interrupt.use")
|
||||
if useFn.IsNil() {
|
||||
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||
// pointer value directly. It returns the pointer with the packed data.
|
||||
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.Config, values)
|
||||
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.NeedsStackObjects, values)
|
||||
}
|
||||
|
||||
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
||||
|
||||
@@ -5,7 +5,6 @@ package llvmutil
|
||||
// itself if possible and legal.
|
||||
|
||||
import (
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
// bitcasts, or else allocates a value on the heap if it cannot be packed in the
|
||||
// pointer value directly. It returns the pointer with the packed data.
|
||||
// If the values are all constants, they are be stored in a constant global and deduplicated.
|
||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.Config, values []llvm.Value) llvm.Value {
|
||||
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bool, values []llvm.Value) llvm.Value {
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
@@ -101,7 +100,7 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
|
||||
llvm.Undef(i8ptrType), // unused context parameter
|
||||
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
||||
}, "")
|
||||
if config.NeedsStackObjects() {
|
||||
if needsStackObjects {
|
||||
trackPointer := mod.NamedFunction("runtime.trackPointer")
|
||||
builder.CreateCall(trackPointer, []llvm.Value{
|
||||
packedHeapAlloc,
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
package compiler
|
||||
|
||||
// This file implements a simple reachability analysis, to reduce compile time.
|
||||
// This DCE pass used to be necessary for improving other passes but now it
|
||||
// isn't necessary anymore.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/types"
|
||||
"sort"
|
||||
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
type dceState struct {
|
||||
*compilerContext
|
||||
functions []*dceFunction
|
||||
functionMap map[*ssa.Function]*dceFunction
|
||||
}
|
||||
|
||||
type dceFunction struct {
|
||||
*ssa.Function
|
||||
functionInfo
|
||||
flag bool // used by dead code elimination
|
||||
}
|
||||
|
||||
func (p *dceState) addFunction(ssaFn *ssa.Function) {
|
||||
if _, ok := p.functionMap[ssaFn]; ok {
|
||||
return
|
||||
}
|
||||
f := &dceFunction{Function: ssaFn}
|
||||
f.functionInfo = p.getFunctionInfo(ssaFn)
|
||||
p.functions = append(p.functions, f)
|
||||
p.functionMap[ssaFn] = f
|
||||
|
||||
for _, anon := range ssaFn.AnonFuncs {
|
||||
p.addFunction(anon)
|
||||
}
|
||||
}
|
||||
|
||||
// simpleDCE returns a list of alive functions in the program. Compiling only
|
||||
// these functions makes the compiler faster.
|
||||
//
|
||||
// This functionality will likely be replaced in the future with build caching.
|
||||
func (c *compilerContext) simpleDCE(lprogram *loader.Program) ([]*ssa.Function, error) {
|
||||
mainPkg := c.program.Package(lprogram.MainPkg().Pkg)
|
||||
if mainPkg == nil {
|
||||
panic("could not find main package")
|
||||
}
|
||||
p := &dceState{
|
||||
compilerContext: c,
|
||||
functionMap: make(map[*ssa.Function]*dceFunction),
|
||||
}
|
||||
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := c.program.Package(pkg.Pkg)
|
||||
memberNames := make([]string, 0)
|
||||
for name := range pkg.Members {
|
||||
memberNames = append(memberNames, name)
|
||||
}
|
||||
sort.Strings(memberNames)
|
||||
|
||||
for _, name := range memberNames {
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
p.addFunction(member)
|
||||
case *ssa.Type:
|
||||
methods := getAllMethods(pkg.Prog, member.Type())
|
||||
if !types.IsInterface(member.Type()) {
|
||||
// named type
|
||||
for _, method := range methods {
|
||||
p.addFunction(pkg.Prog.MethodValue(method))
|
||||
}
|
||||
}
|
||||
case *ssa.Global:
|
||||
// Ignore. Globals are not handled here.
|
||||
case *ssa.NamedConst:
|
||||
// Ignore: these are already resolved.
|
||||
default:
|
||||
panic("unknown member type: " + member.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial set of live functions. Include main.main, *.init and runtime.*
|
||||
// functions.
|
||||
main, ok := mainPkg.Members["main"].(*ssa.Function)
|
||||
if !ok {
|
||||
if mainPkg.Members["main"] == nil {
|
||||
return nil, errors.New("function main is undeclared in the main package")
|
||||
} else {
|
||||
return nil, errors.New("cannot declare main - must be func")
|
||||
}
|
||||
}
|
||||
runtimePkg := c.program.ImportedPackage("runtime")
|
||||
mathPkg := c.program.ImportedPackage("math")
|
||||
taskPkg := c.program.ImportedPackage("internal/task")
|
||||
p.functionMap[main].flag = true
|
||||
worklist := []*ssa.Function{main}
|
||||
for _, f := range p.functions {
|
||||
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || f.Pkg == taskPkg || (f.Pkg == mathPkg && f.Pkg != nil) {
|
||||
if f.flag {
|
||||
continue
|
||||
}
|
||||
f.flag = true
|
||||
worklist = append(worklist, f.Function)
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all called functions recursively.
|
||||
for len(worklist) != 0 {
|
||||
f := worklist[len(worklist)-1]
|
||||
worklist = worklist[:len(worklist)-1]
|
||||
for _, block := range f.Blocks {
|
||||
for _, instr := range block.Instrs {
|
||||
if instr, ok := instr.(*ssa.MakeInterface); ok {
|
||||
for _, sel := range getAllMethods(c.program, instr.X.Type()) {
|
||||
fn := c.program.MethodValue(sel)
|
||||
callee := p.functionMap[fn]
|
||||
if callee == nil {
|
||||
// TODO: why is this necessary?
|
||||
p.addFunction(fn)
|
||||
callee = p.functionMap[fn]
|
||||
}
|
||||
if !callee.flag {
|
||||
callee.flag = true
|
||||
worklist = append(worklist, callee.Function)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, operand := range instr.Operands(nil) {
|
||||
if operand == nil || *operand == nil {
|
||||
continue
|
||||
}
|
||||
switch operand := (*operand).(type) {
|
||||
case *ssa.Function:
|
||||
f := p.functionMap[operand]
|
||||
if f == nil {
|
||||
// FIXME HACK: this function should have been
|
||||
// discovered already. It is not for bound methods.
|
||||
p.addFunction(operand)
|
||||
f = p.functionMap[operand]
|
||||
}
|
||||
if !f.flag {
|
||||
f.flag = true
|
||||
worklist = append(worklist, operand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return all live functions.
|
||||
liveFunctions := []*ssa.Function{}
|
||||
for _, f := range p.functions {
|
||||
if f.flag {
|
||||
liveFunctions = append(liveFunctions, f.Function)
|
||||
}
|
||||
}
|
||||
return liveFunctions, nil
|
||||
}
|
||||
+285
-2
@@ -15,6 +15,269 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// functionInfo contains some information about a function or method. In
|
||||
// particular, it contains information obtained from pragmas.
|
||||
//
|
||||
// The linkName value contains a valid link name, even if //go:linkname is not
|
||||
// present.
|
||||
type functionInfo struct {
|
||||
module string // go:wasm-module
|
||||
importName string // go:linkname, go:export - The name the developer assigns
|
||||
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
|
||||
exported bool // go:export, CGo
|
||||
nobounds bool // go:nobounds
|
||||
variadic bool // go:variadic (CGo only)
|
||||
inline inlineType // go:inline
|
||||
}
|
||||
|
||||
type inlineType int
|
||||
|
||||
// How much to inline.
|
||||
const (
|
||||
// Default behavior. The compiler decides for itself whether any given
|
||||
// function will be inlined. Whether any function is inlined depends on the
|
||||
// optimization level.
|
||||
inlineDefault inlineType = iota
|
||||
|
||||
// Inline hint, just like the C inline keyword (signalled using
|
||||
// //go:inline). The compiler will be more likely to inline this function,
|
||||
// but it is not a guarantee.
|
||||
inlineHint
|
||||
|
||||
// Don't inline, just like the GCC noinline attribute. Signalled using
|
||||
// //go:noinline.
|
||||
inlineNone
|
||||
)
|
||||
|
||||
// getFunction returns the LLVM function for the given *ssa.Function, creating
|
||||
// it if needed. It can later be filled with compilerContext.createFunction().
|
||||
func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
info := c.getFunctionInfo(fn)
|
||||
llvmFn := c.mod.NamedFunction(info.linkName)
|
||||
if !llvmFn.IsNil() {
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
var retType llvm.Type
|
||||
if fn.Signature.Results() == nil {
|
||||
retType = c.ctx.VoidType()
|
||||
} else if fn.Signature.Results().Len() == 1 {
|
||||
retType = c.getLLVMType(fn.Signature.Results().At(0).Type())
|
||||
} else {
|
||||
results := make([]llvm.Type, 0, fn.Signature.Results().Len())
|
||||
for i := 0; i < fn.Signature.Results().Len(); i++ {
|
||||
results = append(results, c.getLLVMType(fn.Signature.Results().At(i).Type()))
|
||||
}
|
||||
retType = c.ctx.StructType(results, false)
|
||||
}
|
||||
|
||||
var paramInfos []paramInfo
|
||||
for _, param := range fn.Params {
|
||||
paramType := c.getLLVMType(param.Type())
|
||||
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
|
||||
paramInfos = append(paramInfos, paramFragmentInfos...)
|
||||
}
|
||||
|
||||
// Add an extra parameter as the function context. This context is used in
|
||||
// closures and bound methods, but should be optimized away when not used.
|
||||
if !info.exported {
|
||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
|
||||
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "parentHandle", flags: 0})
|
||||
}
|
||||
|
||||
var paramTypes []llvm.Type
|
||||
for _, info := range paramInfos {
|
||||
paramTypes = append(paramTypes, info.llvmType)
|
||||
}
|
||||
|
||||
fnType := llvm.FunctionType(retType, paramTypes, info.variadic)
|
||||
llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType)
|
||||
if strings.HasPrefix(c.Triple, "wasm") {
|
||||
// C functions without prototypes like this:
|
||||
// void foo();
|
||||
// are actually variadic functions. However, it appears that it has been
|
||||
// decided in WebAssembly that such prototype-less functions are not
|
||||
// allowed in WebAssembly.
|
||||
// In C, this can only happen when there are zero parameters, hence this
|
||||
// check here. For more information:
|
||||
// https://reviews.llvm.org/D48443
|
||||
// https://github.com/WebAssembly/tool-conventions/issues/16
|
||||
if info.variadic && len(fn.Params) == 0 {
|
||||
attr := c.ctx.CreateStringAttribute("no-prototype", "")
|
||||
llvmFn.AddFunctionAttr(attr)
|
||||
}
|
||||
}
|
||||
|
||||
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
||||
for i, info := range paramInfos {
|
||||
if info.flags¶mIsDeferenceableOrNull == 0 {
|
||||
continue
|
||||
}
|
||||
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||
el := info.llvmType.ElementType()
|
||||
size := c.targetData.TypeAllocSize(el)
|
||||
if size == 0 {
|
||||
// dereferenceable_or_null(0) appears to be illegal in LLVM.
|
||||
continue
|
||||
}
|
||||
dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, size)
|
||||
llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull)
|
||||
}
|
||||
}
|
||||
|
||||
// Set a number of function or parameter attributes, depending on the
|
||||
// function. These functions are runtime functions that are known to have
|
||||
// certain attributes that might not be inferred by the compiler.
|
||||
switch info.linkName {
|
||||
case "abort":
|
||||
// On *nix systems, the "abort" functuion in libc is used to handle fatal panics.
|
||||
// Mark it as noreturn so LLVM can optimize away code.
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
|
||||
case "runtime.alloc":
|
||||
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
|
||||
// returns values that are never null and never alias to an existing value.
|
||||
for _, attrName := range []string{"noalias", "nonnull"} {
|
||||
llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0))
|
||||
}
|
||||
case "runtime.trackPointer":
|
||||
// This function is necessary for tracking pointers on the stack in a
|
||||
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
||||
// that the only thing we'll do is read the pointer.
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
}
|
||||
|
||||
// External/exported functions may not retain pointer values.
|
||||
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
|
||||
if info.exported {
|
||||
// Set the wasm-import-module attribute if the function's module is set.
|
||||
if info.module != "" {
|
||||
|
||||
// We need to add the wasm-import-module and the wasm-import-name
|
||||
wasmImportModuleAttr := c.ctx.CreateStringAttribute("wasm-import-module", info.module)
|
||||
llvmFn.AddFunctionAttr(wasmImportModuleAttr)
|
||||
|
||||
// Add the Wasm Import Name, if we are a named wasm import
|
||||
if info.importName != "" {
|
||||
wasmImportNameAttr := c.ctx.CreateStringAttribute("wasm-import-name", info.importName)
|
||||
llvmFn.AddFunctionAttr(wasmImportNameAttr)
|
||||
}
|
||||
}
|
||||
nocaptureKind := llvm.AttributeKindID("nocapture")
|
||||
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
|
||||
for i, typ := range paramTypes {
|
||||
if typ.TypeKind() == llvm.PointerTypeKind {
|
||||
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
// getFunctionInfo returns information about a function that is not directly
|
||||
// present in *ssa.Function, such as the link name and whether it should be
|
||||
// exported.
|
||||
func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
||||
info := functionInfo{}
|
||||
if strings.HasPrefix(f.Name(), "C.") {
|
||||
// Created by CGo: such a name cannot be created by regular C code.
|
||||
info.linkName = f.Name()[2:]
|
||||
info.exported = true
|
||||
} else {
|
||||
// Pick the default linkName.
|
||||
info.linkName = f.RelString(nil)
|
||||
}
|
||||
// Check for //go: pragmas, which may change the link name (among others).
|
||||
info.parsePragmas(f)
|
||||
return info
|
||||
}
|
||||
|
||||
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
|
||||
// //export or //go:noinline.
|
||||
func (info *functionInfo) parsePragmas(f *ssa.Function) {
|
||||
if f.Syntax() == nil {
|
||||
return
|
||||
}
|
||||
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
||||
|
||||
// Our importName for a wasm module (if we are compiling to wasm), or llvm link name
|
||||
var importName string
|
||||
|
||||
for _, comment := range decl.Doc.List {
|
||||
text := comment.Text
|
||||
if strings.HasPrefix(text, "//export ") {
|
||||
// Rewrite '//export' to '//go:export' for compatibility with
|
||||
// gc.
|
||||
text = "//go:" + text[2:]
|
||||
}
|
||||
if !strings.HasPrefix(text, "//go:") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(text)
|
||||
switch parts[0] {
|
||||
case "//go:export":
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
importName = parts[1]
|
||||
info.exported = true
|
||||
case "//go:wasm-module":
|
||||
// Alternative comment for setting the import module.
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
info.module = parts[1]
|
||||
case "//go:inline":
|
||||
info.inline = inlineHint
|
||||
case "//go:noinline":
|
||||
info.inline = inlineNone
|
||||
case "//go:linkname":
|
||||
if len(parts) != 3 || parts[1] != f.Name() {
|
||||
continue
|
||||
}
|
||||
// Only enable go:linkname when the package imports "unsafe".
|
||||
// This is a slightly looser requirement than what gc uses: gc
|
||||
// requires the file to import "unsafe", not the package as a
|
||||
// whole.
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.linkName = parts[2]
|
||||
}
|
||||
case "//go:nobounds":
|
||||
// Skip bounds checking in this function. Useful for some
|
||||
// runtime functions.
|
||||
// This is somewhat dangerous and thus only imported in packages
|
||||
// that import unsafe.
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.nobounds = true
|
||||
}
|
||||
case "//go:variadic":
|
||||
// The //go:variadic pragma is emitted by the CGo preprocessing
|
||||
// pass for C variadic functions. This includes both explicit
|
||||
// (with ...) and implicit (no parameters in signature)
|
||||
// functions.
|
||||
if strings.HasPrefix(f.Name(), "C.") {
|
||||
// This prefix cannot naturally be created, it must have
|
||||
// been created as a result of CGo preprocessing.
|
||||
info.variadic = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the importName for our exported function if we have one
|
||||
if importName != "" {
|
||||
if info.module == "" {
|
||||
info.linkName = importName
|
||||
} else {
|
||||
// WebAssembly import
|
||||
info.importName = importName
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// globalInfo contains some information about a specific global. By default,
|
||||
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||
// some symbols this is different (due to //go:extern for example).
|
||||
@@ -82,11 +345,11 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||
}
|
||||
}
|
||||
|
||||
if c.Debug() && !info.extern {
|
||||
if c.Debug && !info.extern {
|
||||
// Add debug info.
|
||||
// TODO: this should be done for every global in the program, not just
|
||||
// the ones that are referenced from some code.
|
||||
pos := c.ir.Program.Fset.Position(g.Pos())
|
||||
pos := c.program.Fset.Position(g.Pos())
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||
Name: g.RelString(nil),
|
||||
LinkageName: info.linkName,
|
||||
@@ -145,3 +408,23 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all methods of a type.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
ms := prog.MethodSets.MethodSet(typ)
|
||||
methods := make([]*types.Selection, ms.Len())
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
methods[i] = ms.At(i)
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
// Return true if this package imports "unsafe", false otherwise.
|
||||
func hasUnsafeImport(pkg *types.Package) bool {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp == types.Unsafe {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+8
-8
@@ -16,8 +16,8 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
num := b.getValue(call.Args[0])
|
||||
var syscallResult llvm.Value
|
||||
switch {
|
||||
case b.GOARCH() == "amd64":
|
||||
if b.GOOS() == "darwin" {
|
||||
case b.GOARCH == "amd64":
|
||||
if b.GOOS == "darwin" {
|
||||
// Darwin adds this magic number to system call numbers:
|
||||
//
|
||||
// > Syscall classes for 64-bit system call entry.
|
||||
@@ -58,7 +58,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
case b.GOARCH() == "386" && b.GOOS() == "linux":
|
||||
case b.GOARCH == "386" && b.GOOS == "linux":
|
||||
// Sources:
|
||||
// syscall(2) man page
|
||||
// https://stackoverflow.com/a/2538212
|
||||
@@ -84,7 +84,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
case b.GOARCH() == "arm" && b.GOOS() == "linux":
|
||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||
// Implement the EABI system call convention for Linux.
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
@@ -116,7 +116,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
case b.GOARCH() == "arm64" && b.GOOS() == "linux":
|
||||
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
@@ -149,9 +149,9 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH())
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||
}
|
||||
switch b.GOOS() {
|
||||
switch b.GOOS {
|
||||
case "linux", "freebsd":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
@@ -187,6 +187,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH())
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
// Basic tests that don't need to be split into a separate file.
|
||||
|
||||
func addInt(x, y int) int {
|
||||
return x + y
|
||||
}
|
||||
|
||||
func equalInt(x, y int) bool {
|
||||
return x == y
|
||||
}
|
||||
|
||||
func floatEQ(x, y float32) bool {
|
||||
return x == y
|
||||
}
|
||||
|
||||
func floatNE(x, y float32) bool {
|
||||
return x != y
|
||||
}
|
||||
|
||||
func floatLower(x, y float32) bool {
|
||||
return x < y
|
||||
}
|
||||
|
||||
func floatLowerEqual(x, y float32) bool {
|
||||
return x <= y
|
||||
}
|
||||
|
||||
func floatGreater(x, y float32) bool {
|
||||
return x > y
|
||||
}
|
||||
|
||||
func floatGreaterEqual(x, y float32) bool {
|
||||
return x >= y
|
||||
}
|
||||
|
||||
func complexReal(x complex64) float32 {
|
||||
return real(x)
|
||||
}
|
||||
|
||||
func complexImag(x complex64) float32 {
|
||||
return imag(x)
|
||||
}
|
||||
|
||||
func complexAdd(x, y complex64) complex64 {
|
||||
return x + y
|
||||
}
|
||||
|
||||
func complexSub(x, y complex64) complex64 {
|
||||
return x - y
|
||||
}
|
||||
|
||||
func complexMul(x, y complex64) complex64 {
|
||||
return x * y
|
||||
}
|
||||
|
||||
// TODO: complexDiv (requires runtime call)
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
; ModuleID = 'basic.go'
|
||||
source_filename = "basic.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = add i32 %x, %y
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp eq i32 %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp oeq float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp une float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp olt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp ole float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp ogt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp oge float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret float %x.r
|
||||
}
|
||||
|
||||
define internal float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret float %x.i
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fadd float %x.r, %y.r
|
||||
%1 = fadd float %x.i, %y.i
|
||||
%2 = insertvalue { float, float } undef, float %0, 0
|
||||
%3 = insertvalue { float, float } %2, float %1, 1
|
||||
ret { float, float } %3
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fsub float %x.r, %y.r
|
||||
%1 = fsub float %x.i, %y.i
|
||||
%2 = insertvalue { float, float } undef, float %0, 0
|
||||
%3 = insertvalue { float, float } %2, float %1, 1
|
||||
ret { float, float } %3
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fmul float %x.r, %y.r
|
||||
%1 = fmul float %x.i, %y.i
|
||||
%2 = fsub float %0, %1
|
||||
%3 = fmul float %x.r, %y.i
|
||||
%4 = fmul float %x.i, %y.r
|
||||
%5 = fadd float %3, %4
|
||||
%6 = insertvalue { float, float } undef, float %2, 0
|
||||
%7 = insertvalue { float, float } %6, float %5, 1
|
||||
ret { float, float } %7
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
// Test converting floats to ints.
|
||||
|
||||
func f32tou32(v float32) uint32 {
|
||||
return uint32(v)
|
||||
}
|
||||
|
||||
func maxu32f() float32 {
|
||||
return float32(^uint32(0))
|
||||
}
|
||||
|
||||
func maxu32tof32() uint32 {
|
||||
f := float32(^uint32(0))
|
||||
return uint32(f)
|
||||
}
|
||||
|
||||
func inftoi32() (uint32, uint32, int32, int32) {
|
||||
inf := 1.0
|
||||
inf /= 0.0
|
||||
|
||||
return uint32(inf), uint32(-inf), int32(inf), int32(-inf)
|
||||
}
|
||||
|
||||
func u32tof32tou32(v uint32) uint32 {
|
||||
return uint32(float32(v))
|
||||
}
|
||||
|
||||
func f32tou32tof32(v float32) float32 {
|
||||
return float32(uint32(v))
|
||||
}
|
||||
|
||||
func f32tou8(v float32) uint8 {
|
||||
return uint8(v)
|
||||
}
|
||||
|
||||
func f32toi8(v float32) int8 {
|
||||
return int8(v)
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
; ModuleID = 'float.go'
|
||||
source_filename = "float.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
%inbounds = and i1 %positive, %withinmax
|
||||
%saturated = sext i1 %positive to i32
|
||||
%normal = fptoui float %v to i32
|
||||
%0 = select i1 %inbounds, i32 %normal, i32 %saturated
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
define internal float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret float 0x41F0000000000000
|
||||
}
|
||||
|
||||
define internal i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 -1
|
||||
}
|
||||
|
||||
define internal { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||
}
|
||||
|
||||
define internal i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = uitofp i32 %v to float
|
||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||
%normal = fptoui float %0 to i32
|
||||
%1 = select i1 %withinmax, i32 %normal, i32 -1
|
||||
ret i32 %1
|
||||
}
|
||||
|
||||
define internal float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
%inbounds = and i1 %positive, %withinmax
|
||||
%saturated = sext i1 %positive to i32
|
||||
%normal = fptoui float %v to i32
|
||||
%0 = select i1 %inbounds, i32 %normal, i32 %saturated
|
||||
%1 = uitofp i32 %0 to float
|
||||
ret float %1
|
||||
}
|
||||
|
||||
define internal i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||
%inbounds = and i1 %positive, %withinmax
|
||||
%saturated = sext i1 %positive to i8
|
||||
%normal = fptoui float %v to i8
|
||||
%0 = select i1 %inbounds, i8 %normal, i8 %saturated
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
define internal i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||
%inbounds = and i1 %abovemin, %belowmax
|
||||
%saturated = select i1 %abovemin, i8 127, i8 -128
|
||||
%isnan = fcmp uno float %v, 0.000000e+00
|
||||
%remapped = select i1 %isnan, i8 0, i8 %saturated
|
||||
%normal = fptosi float %v to i8
|
||||
%0 = select i1 %inbounds, i8 %normal, i8 %remapped
|
||||
ret i8 %0
|
||||
}
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
// This file tests various operations on pointers, such as pointer arithmetic
|
||||
// and dereferencing pointers.
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Dereference pointers.
|
||||
|
||||
func pointerDerefZero(x *[0]int) [0]int {
|
||||
return *x // This is a no-op, there is nothing to load.
|
||||
}
|
||||
|
||||
// Unsafe pointer casts, they are sometimes a no-op.
|
||||
|
||||
func pointerCastFromUnsafe(x unsafe.Pointer) *int {
|
||||
return (*int)(x)
|
||||
}
|
||||
|
||||
func pointerCastToUnsafe(x *int) unsafe.Pointer {
|
||||
return unsafe.Pointer(x)
|
||||
}
|
||||
|
||||
func pointerCastToUnsafeNoop(x *byte) unsafe.Pointer {
|
||||
return unsafe.Pointer(x)
|
||||
}
|
||||
|
||||
// The compiler has support for a few special cast+add patterns that are
|
||||
// transformed into a single GEP.
|
||||
|
||||
func pointerUnsafeGEPFixedOffset(ptr *byte) *byte {
|
||||
return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + 10))
|
||||
}
|
||||
|
||||
func pointerUnsafeGEPByteOffset(ptr *byte, offset uintptr) *byte {
|
||||
return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + offset))
|
||||
}
|
||||
|
||||
func pointerUnsafeGEPIntOffset(ptr *int32, offset uintptr) *int32 {
|
||||
return (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + offset*4))
|
||||
}
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
; ModuleID = 'pointer.go'
|
||||
source_filename = "pointer.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret [0 x i32] zeroinitializer
|
||||
}
|
||||
|
||||
define internal i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = bitcast i8* %x to i32*
|
||||
ret i32* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = bitcast i32* %x to i8*
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i8* %x
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
||||
ret i32* %0
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func sliceLen(ints []int) int {
|
||||
return len(ints)
|
||||
}
|
||||
|
||||
func sliceCap(ints []int) int {
|
||||
return cap(ints)
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
; ModuleID = 'slice.go'
|
||||
source_filename = "slice.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 %ints.len
|
||||
}
|
||||
|
||||
define internal i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 %ints.cap
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
module github.com/tinygo-org/tinygo
|
||||
|
||||
go 1.11
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b
|
||||
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e
|
||||
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693
|
||||
github.com/chromedp/chromedp v0.6.4
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||
go.bug.st/serial v1.0.0
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
go.bug.st/serial v1.1.2
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9
|
||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f
|
||||
)
|
||||
|
||||
@@ -1,33 +1,39 @@
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
|
||||
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b h1:LF+GRwyzxrO3MUzPvejv+yBup0lNG+/QdIRrkxOPseA=
|
||||
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b/go.mod h1:E6LPWRdIJc11h/di5p0rwvRmUYbhGpBEH7ZbPfzDIOE=
|
||||
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e h1:Hv0JVyHhbIXb9NiYQe4NsrfgrSofAp0q2FnhhJOXgi8=
|
||||
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e/go.mod h1:vmQMRHFZrY3T+Jv51T0n87OK/i6bK+5P9a+Fg5jPwgQ=
|
||||
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 h1:11eq/RkpaotwdF6b1TRMcdgQUPNmyFEJOB7zLvh0O/Y=
|
||||
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693/go.mod h1:55pim6Ht4LJKdVLlyFJV/g++HsEA1hQxPbB5JyNdZC0=
|
||||
github.com/chromedp/chromedp v0.6.4 h1:Gx7ZkRyrSVmbbDDja/ieNgNGJIvElroPOyeqYQGVDSY=
|
||||
github.com/chromedp/chromedp v0.6.4/go.mod h1:vodUdJf5dF/b8n0UBJv6NeM/QK28RjP3j+eM7fq4+84=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
|
||||
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g=
|
||||
github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
|
||||
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs=
|
||||
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
|
||||
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 h1:6J+qramlHVLmiBOgRiBOnQkno8uprqG6YFFQTt6uYIw=
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
|
||||
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
|
||||
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
|
||||
go.bug.st/serial v1.1.2/go.mod h1:VmYBeyJWp5BnJ0tw2NUJHZdJTGl2ecBGABHlzRK1knY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||
@@ -39,8 +45,11 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
||||
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-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -48,5 +57,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9 h1:l2kTQOhqEoeDTK3ckUnwReOQwMPUmURMIdjJbeAuDT4=
|
||||
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f h1:FP5Do5omlQ/dLQ3Hfy7oyJo69VS5Hn46rZw004r0lGU=
|
||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
|
||||
+21
-2
@@ -142,10 +142,11 @@ func getHomeDir() string {
|
||||
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
|
||||
// found, it returns an empty string.
|
||||
func getGoroot() string {
|
||||
// An explicitly set GOROOT always has preference.
|
||||
goroot := os.Getenv("GOROOT")
|
||||
if goroot != "" {
|
||||
// An explicitly set GOROOT always has preference.
|
||||
return goroot
|
||||
// Convert to the standard GOROOT being referenced, if it's a TinyGo cache.
|
||||
return getStandardGoroot(goroot)
|
||||
}
|
||||
|
||||
// Check for the location of the 'go' binary and base GOROOT on that.
|
||||
@@ -195,3 +196,21 @@ func isGoroot(goroot string) bool {
|
||||
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// getStandardGoroot returns the physical path to a real, standard Go GOROOT
|
||||
// implied by the given path.
|
||||
// If the given path appears to be a TinyGo cached GOROOT, it returns the path
|
||||
// referenced by symlinks contained in the cache. Otherwise, it returns the
|
||||
// given path as-is.
|
||||
func getStandardGoroot(path string) string {
|
||||
// Check if the "bin" subdirectory of our given GOROOT is a symlink, and then
|
||||
// return the _parent_ directory of its destination.
|
||||
if dest, err := os.Readlink(filepath.Join(path, "bin")); nil == err {
|
||||
// Clean the destination to remove any trailing slashes, so that
|
||||
// filepath.Dir will always return the parent.
|
||||
// (because both "/foo" and "/foo/" are valid symlink destinations,
|
||||
// but filepath.Dir would return "/" and "/foo", respectively)
|
||||
return filepath.Dir(filepath.Clean(dest))
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.17.0-dev"
|
||||
const Version = "0.18.0-dev"
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
|
||||
+2
-4
@@ -11,19 +11,17 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
var errLiteralToPointer = errors.New("interp: trying to convert literal value to pointer")
|
||||
|
||||
// These errors are expected during normal execution and can be recovered from
|
||||
// by running the affected function at runtime instead of compile time.
|
||||
var (
|
||||
errExpectedPointer = errors.New("interp: trying to use an integer as a pointer (memory-mapped I/O?)")
|
||||
errIntegerAsPointer = errors.New("interp: trying to use an integer as a pointer (memory-mapped I/O?)")
|
||||
errUnsupportedInst = errors.New("interp: unsupported instruction")
|
||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||
)
|
||||
|
||||
func isRecoverableError(err error) bool {
|
||||
return err == errExpectedPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
||||
}
|
||||
|
||||
// ErrorLine is one line in a traceback. The position may be missing.
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ func Run(mod llvm.Module, debug bool) error {
|
||||
if callErr != nil {
|
||||
if isRecoverableError(callErr.Err) {
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, "not interpretring", r.pkgName, "because of error:", callErr.Err)
|
||||
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
||||
}
|
||||
mem.revert()
|
||||
continue
|
||||
|
||||
+16
-1
@@ -550,7 +550,22 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
}
|
||||
ptr, err := operands[0].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
if err != errIntegerAsPointer {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
// GEP on fixed pointer value (for example, memory-mapped I/O).
|
||||
ptrValue := operands[0].Uint() + offset
|
||||
switch operands[0].len(r) {
|
||||
case 8:
|
||||
locals[inst.localIndex] = literalValue{uint64(ptrValue)}
|
||||
case 4:
|
||||
locals[inst.localIndex] = literalValue{uint32(ptrValue)}
|
||||
case 2:
|
||||
locals[inst.localIndex] = literalValue{uint16(ptrValue)}
|
||||
default:
|
||||
panic("pointer operand is not of a known pointer size")
|
||||
}
|
||||
continue
|
||||
}
|
||||
ptr = ptr.addOffset(uint32(offset))
|
||||
locals[inst.localIndex] = ptr
|
||||
|
||||
+2
-2
@@ -348,7 +348,7 @@ func (v literalValue) clone() value {
|
||||
}
|
||||
|
||||
func (v literalValue) asPointer(r *runner) (pointerValue, error) {
|
||||
return pointerValue{}, errLiteralToPointer
|
||||
return pointerValue{}, errIntegerAsPointer
|
||||
}
|
||||
|
||||
func (v literalValue) asRawValue(r *runner) rawValue {
|
||||
@@ -949,7 +949,7 @@ func (v rawValue) clone() value {
|
||||
func (v rawValue) asPointer(r *runner) (pointerValue, error) {
|
||||
if v.buf[0] <= 255 {
|
||||
// Probably a null pointer or memory-mapped I/O.
|
||||
return pointerValue{}, errExpectedPointer
|
||||
return pointerValue{}, errIntegerAsPointer
|
||||
}
|
||||
return pointerValue{v.buf[0]}, nil
|
||||
}
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
package ir
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// This file provides a wrapper around go/ssa values and adds extra
|
||||
// functionality to them.
|
||||
|
||||
// View on all functions, types, and globals in a program, with analysis
|
||||
// results.
|
||||
type Program struct {
|
||||
Program *ssa.Program
|
||||
LoaderProgram *loader.Program
|
||||
mainPkg *ssa.Package
|
||||
Functions []*Function
|
||||
functionMap map[*ssa.Function]*Function
|
||||
}
|
||||
|
||||
// Function or method.
|
||||
type Function struct {
|
||||
*ssa.Function
|
||||
LLVMFn llvm.Value
|
||||
module string // go:wasm-module
|
||||
linkName string // go:linkname, go:export
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
inline InlineType // go:inline
|
||||
}
|
||||
|
||||
// Interface type that is at some point used in a type assert (to check whether
|
||||
// it implements another interface).
|
||||
type Interface struct {
|
||||
Num int
|
||||
Type *types.Interface
|
||||
}
|
||||
|
||||
type InlineType int
|
||||
|
||||
// How much to inline.
|
||||
const (
|
||||
// Default behavior. The compiler decides for itself whether any given
|
||||
// function will be inlined. Whether any function is inlined depends on the
|
||||
// optimization level.
|
||||
InlineDefault InlineType = iota
|
||||
|
||||
// Inline hint, just like the C inline keyword (signalled using
|
||||
// //go:inline). The compiler will be more likely to inline this function,
|
||||
// but it is not a guarantee.
|
||||
InlineHint
|
||||
|
||||
// Don't inline, just like the GCC noinline attribute. Signalled using
|
||||
// //go:noinline.
|
||||
InlineNone
|
||||
)
|
||||
|
||||
// Create and initialize a new *Program from a *ssa.Program.
|
||||
func NewProgram(lprogram *loader.Program) *Program {
|
||||
program := lprogram.LoadSSA()
|
||||
program.Build()
|
||||
|
||||
mainPkg := program.ImportedPackage(lprogram.MainPkg().ImportPath)
|
||||
if mainPkg == nil {
|
||||
panic("could not find main package")
|
||||
}
|
||||
p := &Program{
|
||||
Program: program,
|
||||
LoaderProgram: lprogram,
|
||||
mainPkg: mainPkg,
|
||||
functionMap: make(map[*ssa.Function]*Function),
|
||||
}
|
||||
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
p.AddPackage(program.ImportedPackage(pkg.ImportPath))
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// Add a package to this Program. All packages need to be added first before any
|
||||
// analysis is done for correct results.
|
||||
func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||
memberNames := make([]string, 0)
|
||||
for name := range pkg.Members {
|
||||
memberNames = append(memberNames, name)
|
||||
}
|
||||
sort.Strings(memberNames)
|
||||
|
||||
for _, name := range memberNames {
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
p.addFunction(member)
|
||||
case *ssa.Type:
|
||||
methods := getAllMethods(pkg.Prog, member.Type())
|
||||
if !types.IsInterface(member.Type()) {
|
||||
// named type
|
||||
for _, method := range methods {
|
||||
p.addFunction(pkg.Prog.MethodValue(method))
|
||||
}
|
||||
}
|
||||
case *ssa.Global:
|
||||
// Ignore. Globals are not handled here.
|
||||
case *ssa.NamedConst:
|
||||
// Ignore: these are already resolved.
|
||||
default:
|
||||
panic("unknown member type: " + member.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) addFunction(ssaFn *ssa.Function) {
|
||||
if _, ok := p.functionMap[ssaFn]; ok {
|
||||
return
|
||||
}
|
||||
f := &Function{Function: ssaFn}
|
||||
f.parsePragmas()
|
||||
p.Functions = append(p.Functions, f)
|
||||
p.functionMap[ssaFn] = f
|
||||
|
||||
for _, anon := range ssaFn.AnonFuncs {
|
||||
p.addFunction(anon)
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if this package imports "unsafe", false otherwise.
|
||||
func hasUnsafeImport(pkg *types.Package) bool {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp == types.Unsafe {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Program) GetFunction(ssaFn *ssa.Function) *Function {
|
||||
return p.functionMap[ssaFn]
|
||||
}
|
||||
|
||||
func (p *Program) MainPkg() *ssa.Package {
|
||||
return p.mainPkg
|
||||
}
|
||||
|
||||
// Parse compiler directives in the preceding comments.
|
||||
func (f *Function) parsePragmas() {
|
||||
if f.Syntax() == nil {
|
||||
return
|
||||
}
|
||||
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
||||
for _, comment := range decl.Doc.List {
|
||||
text := comment.Text
|
||||
if strings.HasPrefix(text, "//export ") {
|
||||
// Rewrite '//export' to '//go:export' for compatibility with
|
||||
// gc.
|
||||
text = "//go:" + text[2:]
|
||||
}
|
||||
if !strings.HasPrefix(text, "//go:") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(text)
|
||||
switch parts[0] {
|
||||
case "//go:export":
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
f.linkName = parts[1]
|
||||
f.exported = true
|
||||
case "//go:wasm-module":
|
||||
// Alternative comment for setting the import module.
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
f.module = parts[1]
|
||||
case "//go:inline":
|
||||
f.inline = InlineHint
|
||||
case "//go:noinline":
|
||||
f.inline = InlineNone
|
||||
case "//go:linkname":
|
||||
if len(parts) != 3 || parts[1] != f.Name() {
|
||||
continue
|
||||
}
|
||||
// Only enable go:linkname when the package imports "unsafe".
|
||||
// This is a slightly looser requirement than what gc uses: gc
|
||||
// requires the file to import "unsafe", not the package as a
|
||||
// whole.
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
f.linkName = parts[2]
|
||||
}
|
||||
case "//go:nobounds":
|
||||
// Skip bounds checking in this function. Useful for some
|
||||
// runtime functions.
|
||||
// This is somewhat dangerous and thus only imported in packages
|
||||
// that import unsafe.
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
f.nobounds = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Function) IsNoBounds() bool {
|
||||
return f.nobounds
|
||||
}
|
||||
|
||||
// Return true iff this function is externally visible.
|
||||
func (f *Function) IsExported() bool {
|
||||
return f.exported || f.CName() != ""
|
||||
}
|
||||
|
||||
// Return the inline directive of this function.
|
||||
func (f *Function) Inline() InlineType {
|
||||
return f.inline
|
||||
}
|
||||
|
||||
// Return the module name if not the default.
|
||||
func (f *Function) Module() string {
|
||||
return f.module
|
||||
}
|
||||
|
||||
// Return the link name for this function.
|
||||
func (f *Function) LinkName() string {
|
||||
if f.linkName != "" {
|
||||
return f.linkName
|
||||
}
|
||||
if f.Signature.Recv() != nil {
|
||||
// Method on a defined type (which may be a pointer).
|
||||
return f.RelString(nil)
|
||||
} else {
|
||||
// Bare function.
|
||||
if name := f.CName(); name != "" {
|
||||
// Name CGo functions directly.
|
||||
return name
|
||||
} else {
|
||||
return f.RelString(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the name of the C function if this is a CGo wrapper. Otherwise, return
|
||||
// a zero-length string.
|
||||
func (f *Function) CName() string {
|
||||
name := f.Name()
|
||||
if strings.HasPrefix(name, "_Cfunc_") {
|
||||
// emitted by `go tool cgo`
|
||||
return name[len("_Cfunc_"):]
|
||||
}
|
||||
if strings.HasPrefix(name, "C.") {
|
||||
// created by ../loader/cgo.go
|
||||
return name[2:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Get all methods of a type.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
ms := prog.MethodSets.MethodSet(typ)
|
||||
methods := make([]*types.Selection, ms.Len())
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
methods[i] = ms.At(i)
|
||||
}
|
||||
return methods
|
||||
}
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
package ir
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
// This file implements several optimization passes (analysis + transform) to
|
||||
// optimize code in SSA form before it is compiled to LLVM IR. It is based on
|
||||
// the IR defined in ir.go.
|
||||
|
||||
// Make a readable version of a method signature (including the function name,
|
||||
// excluding the receiver name). This string is used internally to match
|
||||
// interfaces and to call the correct method on an interface. Examples:
|
||||
//
|
||||
// String() string
|
||||
// Read([]byte) (int, error)
|
||||
func MethodSignature(method *types.Func) string {
|
||||
return method.Name() + signature(method.Type().(*types.Signature))
|
||||
}
|
||||
|
||||
// Make a readable version of a function (pointer) signature.
|
||||
// Examples:
|
||||
//
|
||||
// () string
|
||||
// (string, int) (int, error)
|
||||
func signature(sig *types.Signature) string {
|
||||
s := ""
|
||||
if sig.Params().Len() == 0 {
|
||||
s += "()"
|
||||
} else {
|
||||
s += "("
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += sig.Params().At(i).Type().String()
|
||||
}
|
||||
s += ")"
|
||||
}
|
||||
if sig.Results().Len() == 0 {
|
||||
// keep as-is
|
||||
} else if sig.Results().Len() == 1 {
|
||||
s += " " + sig.Results().At(0).Type().String()
|
||||
} else {
|
||||
s += " ("
|
||||
for i := 0; i < sig.Results().Len(); i++ {
|
||||
if i > 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += sig.Results().At(i).Type().String()
|
||||
}
|
||||
s += ")"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Simple pass that removes dead code. This pass makes later analysis passes
|
||||
// more useful.
|
||||
func (p *Program) SimpleDCE() error {
|
||||
// Unmark all functions.
|
||||
for _, f := range p.Functions {
|
||||
f.flag = false
|
||||
}
|
||||
|
||||
// Initial set of live functions. Include main.main, *.init and runtime.*
|
||||
// functions.
|
||||
main, ok := p.mainPkg.Members["main"].(*ssa.Function)
|
||||
if !ok {
|
||||
if p.mainPkg.Members["main"] == nil {
|
||||
return errors.New("function main is undeclared in the main package")
|
||||
} else {
|
||||
return errors.New("cannot declare main - must be func")
|
||||
}
|
||||
}
|
||||
runtimePkg := p.Program.ImportedPackage("runtime")
|
||||
mathPkg := p.Program.ImportedPackage("math")
|
||||
taskPkg := p.Program.ImportedPackage("internal/task")
|
||||
p.GetFunction(main).flag = true
|
||||
worklist := []*ssa.Function{main}
|
||||
for _, f := range p.Functions {
|
||||
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || f.Pkg == taskPkg || (f.Pkg == mathPkg && f.Pkg != nil) {
|
||||
if f.flag {
|
||||
continue
|
||||
}
|
||||
f.flag = true
|
||||
worklist = append(worklist, f.Function)
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all called functions recursively.
|
||||
for len(worklist) != 0 {
|
||||
f := worklist[len(worklist)-1]
|
||||
worklist = worklist[:len(worklist)-1]
|
||||
for _, block := range f.Blocks {
|
||||
for _, instr := range block.Instrs {
|
||||
if instr, ok := instr.(*ssa.MakeInterface); ok {
|
||||
for _, sel := range getAllMethods(p.Program, instr.X.Type()) {
|
||||
fn := p.Program.MethodValue(sel)
|
||||
callee := p.GetFunction(fn)
|
||||
if callee == nil {
|
||||
// TODO: why is this necessary?
|
||||
p.addFunction(fn)
|
||||
callee = p.GetFunction(fn)
|
||||
}
|
||||
if !callee.flag {
|
||||
callee.flag = true
|
||||
worklist = append(worklist, callee.Function)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, operand := range instr.Operands(nil) {
|
||||
if operand == nil || *operand == nil {
|
||||
continue
|
||||
}
|
||||
switch operand := (*operand).(type) {
|
||||
case *ssa.Function:
|
||||
f := p.GetFunction(operand)
|
||||
if f == nil {
|
||||
// FIXME HACK: this function should have been
|
||||
// discovered already. It is not for bound methods.
|
||||
p.addFunction(operand)
|
||||
f = p.GetFunction(operand)
|
||||
}
|
||||
if !f.flag {
|
||||
f.flag = true
|
||||
worklist = append(worklist, operand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unmarked functions.
|
||||
livefunctions := []*Function{}
|
||||
for _, f := range p.Functions {
|
||||
if f.flag {
|
||||
livefunctions = append(livefunctions, f)
|
||||
} else {
|
||||
delete(p.functionMap, f.Function)
|
||||
}
|
||||
}
|
||||
p.Functions = livefunctions
|
||||
|
||||
return nil
|
||||
}
|
||||
+1
-1
Submodule lib/nrfx updated: 3ab39a9d45...d779b49fc5
Submodule
+1
Submodule lib/stm32-svd added at c6b5be976f
+17
-7
@@ -16,7 +16,6 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/tinygo-org/tinygo/cgo"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -43,6 +42,7 @@ type Program struct {
|
||||
type PackageJSON struct {
|
||||
Dir string
|
||||
ImportPath string
|
||||
Name string
|
||||
ForTest string
|
||||
|
||||
// Source files
|
||||
@@ -51,7 +51,8 @@ type PackageJSON struct {
|
||||
CFiles []string
|
||||
|
||||
// Dependency information
|
||||
Imports []string
|
||||
Imports []string
|
||||
ImportMap map[string]string
|
||||
|
||||
// Error information
|
||||
Error *struct {
|
||||
@@ -108,10 +109,7 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||
os.Exit(status.ExitStatus())
|
||||
}
|
||||
os.Exit(1)
|
||||
os.Exit(exitErr.ExitCode())
|
||||
}
|
||||
return nil, fmt.Errorf("failed to run `go list`: %s", err)
|
||||
}
|
||||
@@ -335,7 +333,16 @@ func (p *Package) Check() error {
|
||||
// Do typechecking of the package.
|
||||
checker.Importer = p
|
||||
|
||||
typesPkg, err := checker.Check(p.ImportPath, p.program.fset, p.Files, &p.info)
|
||||
packageName := p.ImportPath
|
||||
if p.Name == "main" {
|
||||
// The main package normally has a different import path, such as
|
||||
// "command-line-arguments" or "./testdata/cgo". Therefore, use the name
|
||||
// "main" in such a case: this package isn't imported from anywhere.
|
||||
// This is safe as it isn't possible to import a package with the name
|
||||
// "main".
|
||||
packageName = "main"
|
||||
}
|
||||
typesPkg, err := checker.Check(packageName, p.program.fset, p.Files, &p.info)
|
||||
if err != nil {
|
||||
if err, ok := err.(Errors); ok {
|
||||
return err
|
||||
@@ -401,6 +408,9 @@ func (p *Package) Import(to string) (*types.Package, error) {
|
||||
if to == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
if newTo, ok := p.ImportMap[to]; ok && !strings.HasSuffix(newTo, ".test]") {
|
||||
to = newTo
|
||||
}
|
||||
if imported, ok := p.program.Packages[to]; ok {
|
||||
return imported.Pkg, nil
|
||||
} else {
|
||||
|
||||
@@ -16,3 +16,11 @@ func (p *Program) LoadSSA() *ssa.Program {
|
||||
|
||||
return prog
|
||||
}
|
||||
|
||||
// LoadSSA constructs the SSA form of this package.
|
||||
//
|
||||
// The program must already be parsed and type-checked with the .Parse() method.
|
||||
func (p *Package) LoadSSA() *ssa.Package {
|
||||
prog := ssa.NewProgram(p.program.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
||||
return prog.CreatePackage(p.Pkg, p.Files, &p.info, true)
|
||||
}
|
||||
|
||||
@@ -14,16 +14,17 @@ import (
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/interp"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"github.com/tinygo-org/tinygo/transform"
|
||||
"tinygo.org/x/go-llvm"
|
||||
|
||||
"go.bug.st/serial"
|
||||
@@ -89,6 +90,14 @@ func copyFile(src, dst string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// executeCommand is a simple wrapper to exec.Cmd
|
||||
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
|
||||
if options.PrintCommands {
|
||||
fmt.Printf("%s %s\n ", name, strings.Join(arg, " "))
|
||||
}
|
||||
return exec.Command(name, arg...)
|
||||
}
|
||||
|
||||
// Build compiles and links the given package and writes it to outpath.
|
||||
func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
@@ -96,7 +105,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
return builder.Build(pkgName, outpath, config, nil, func(result builder.BuildResult) error {
|
||||
if err := os.Rename(result.Binary, outpath); err != nil {
|
||||
// Moving failed. Do a file copy.
|
||||
inf, err := os.Open(result.Binary)
|
||||
@@ -132,7 +141,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
||||
return err
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
return builder.Build(pkgName, outpath, config, nil, func(result builder.BuildResult) error {
|
||||
if testCompileOnly || outpath != "" {
|
||||
// Write test binary to the specified file name.
|
||||
if outpath == "" {
|
||||
@@ -148,7 +157,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
||||
}
|
||||
if len(config.Target.Emulator) == 0 {
|
||||
// Run directly.
|
||||
cmd := exec.Command(result.Binary)
|
||||
cmd := executeCommand(config.Options, result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = result.MainDir
|
||||
@@ -156,10 +165,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
||||
if err != nil {
|
||||
// Propagate the exit code
|
||||
if err, ok := err.(*exec.ExitError); ok {
|
||||
if status, ok := err.Sys().(syscall.WaitStatus); ok {
|
||||
os.Exit(status.ExitStatus())
|
||||
}
|
||||
os.Exit(1)
|
||||
os.Exit(err.ExitCode())
|
||||
}
|
||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
@@ -167,7 +173,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary)
|
||||
cmd := exec.Command(config.Target.Emulator[0], args...)
|
||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
buf := &bytes.Buffer{}
|
||||
w := io.MultiWriter(os.Stdout, buf)
|
||||
cmd.Stdout = w
|
||||
@@ -231,7 +237,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return errors.New("unknown flash method: " + flashMethod)
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||
return builder.Build(pkgName, fileExt, config, func() error {
|
||||
// do we need port reset to put MCU into bootloader mode?
|
||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||
if port == "" {
|
||||
@@ -244,19 +250,21 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
|
||||
err := touchSerialPortAt1200bps(port)
|
||||
if err != nil {
|
||||
return &commandError{"failed to reset port", result.Binary, err}
|
||||
return &commandError{"failed to reset port", "", err}
|
||||
}
|
||||
// give the target MCU a chance to restart into bootloader
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
return nil
|
||||
|
||||
}, func(result builder.BuildResult) error {
|
||||
// this flashing method copies the binary data to a Mass Storage Device (msd)
|
||||
switch flashMethod {
|
||||
case "", "command":
|
||||
// Create the command.
|
||||
flashCmd := config.Target.FlashCommand
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.Replace(flashCmd, fileToken, result.Binary, -1)
|
||||
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
|
||||
|
||||
if port == "" && strings.Contains(flashCmd, "{port}") {
|
||||
var err error
|
||||
@@ -266,7 +274,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
}
|
||||
}
|
||||
|
||||
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||
flashCmd = strings.ReplaceAll(flashCmd, "{port}", port)
|
||||
|
||||
// Execute the command.
|
||||
var cmd *exec.Cmd
|
||||
@@ -276,9 +284,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
if len(command) < 2 {
|
||||
return errors.New("invalid flash command")
|
||||
}
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
cmd = executeCommand(config.Options, command[0], command[1:]...)
|
||||
default:
|
||||
cmd = exec.Command("/bin/sh", "-c", flashCmd)
|
||||
cmd = executeCommand(config.Options, "/bin/sh", "-c", flashCmd)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
@@ -292,13 +300,13 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
case "msd":
|
||||
switch fileExt {
|
||||
case ".uf2":
|
||||
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary)
|
||||
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
case ".hex":
|
||||
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary)
|
||||
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
@@ -312,7 +320,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return err
|
||||
}
|
||||
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
|
||||
cmd := exec.Command("openocd", args...)
|
||||
cmd := executeCommand(config.Options, "openocd", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
@@ -342,7 +350,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
return errors.New("gdb not configured in the target specification")
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
|
||||
return builder.Build(pkgName, "", config, nil, func(result builder.BuildResult) error {
|
||||
// Find a good way to run GDB.
|
||||
gdbInterface, openocdInterface := config.Programmer()
|
||||
switch gdbInterface {
|
||||
@@ -381,11 +389,11 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
daemon = exec.Command("openocd", args...)
|
||||
daemon = executeCommand(config.Options, "openocd", args...)
|
||||
if ocdOutput {
|
||||
// Make it clear which output is from the daemon.
|
||||
w := &ColorWriter{
|
||||
Out: os.Stderr,
|
||||
Out: colorable.NewColorableStderr(),
|
||||
Prefix: "openocd: ",
|
||||
Color: TermColorYellow,
|
||||
}
|
||||
@@ -396,11 +404,11 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt")
|
||||
|
||||
// We need a separate debugging daemon for on-chip debugging.
|
||||
daemon = exec.Command("JLinkGDBServer", "-device", config.Target.JLinkDevice)
|
||||
daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice)
|
||||
if ocdOutput {
|
||||
// Make it clear which output is from the daemon.
|
||||
w := &ColorWriter{
|
||||
Out: os.Stderr,
|
||||
Out: colorable.NewColorableStderr(),
|
||||
Prefix: "jlink: ",
|
||||
Color: TermColorYellow,
|
||||
}
|
||||
@@ -412,7 +420,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
|
||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "qemu-user":
|
||||
@@ -420,7 +428,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary)
|
||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "mgba":
|
||||
@@ -428,7 +436,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary, "-g")
|
||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "simavr":
|
||||
@@ -436,7 +444,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], "-g", result.Binary)
|
||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "msd":
|
||||
@@ -457,8 +465,15 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
}
|
||||
defer func() {
|
||||
daemon.Process.Signal(os.Interrupt)
|
||||
// Maybe we should send a .Kill() after x seconds?
|
||||
var stopped uint32
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
if atomic.LoadUint32(&stopped) == 0 {
|
||||
daemon.Process.Kill()
|
||||
}
|
||||
}()
|
||||
daemon.Wait()
|
||||
atomic.StoreUint32(&stopped, 1)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -477,7 +492,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
for _, cmd := range gdbCommands {
|
||||
params = append(params, "-ex", cmd)
|
||||
}
|
||||
cmd := exec.Command(config.Target.GDB, params...)
|
||||
cmd := executeCommand(config.Options, config.Target.GDB, params...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -499,10 +514,10 @@ func Run(pkgName string, options *compileopts.Options) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||
return builder.Build(pkgName, ".elf", config, nil, func(result builder.BuildResult) error {
|
||||
if len(config.Target.Emulator) == 0 {
|
||||
// Run directly.
|
||||
cmd := exec.Command(result.Binary)
|
||||
cmd := executeCommand(config.Options, result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
@@ -517,7 +532,7 @@ func Run(pkgName string, options *compileopts.Options) error {
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary)
|
||||
cmd := exec.Command(config.Target.Emulator[0], args...)
|
||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
@@ -560,7 +575,7 @@ func touchSerialPortAt1200bps(port string) (err error) {
|
||||
|
||||
const maxMSDRetries = 10
|
||||
|
||||
func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) error {
|
||||
// find standard UF2 info path
|
||||
var infoPath string
|
||||
switch runtime.GOOS {
|
||||
@@ -569,7 +584,7 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
case "darwin":
|
||||
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume)
|
||||
path, err := windowsFindUSBDrive(volume, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -584,7 +599,7 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
|
||||
}
|
||||
|
||||
func flashHexUsingMSD(volume, tmppath string) error {
|
||||
func flashHexUsingMSD(volume, tmppath string, options *compileopts.Options) error {
|
||||
// find expected volume path
|
||||
var destPath string
|
||||
switch runtime.GOOS {
|
||||
@@ -593,7 +608,7 @@ func flashHexUsingMSD(volume, tmppath string) error {
|
||||
case "darwin":
|
||||
destPath = "/Volumes/" + volume
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume)
|
||||
path, err := windowsFindUSBDrive(volume, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -627,8 +642,8 @@ func locateDevice(volume, path string) (string, error) {
|
||||
return d[0], nil
|
||||
}
|
||||
|
||||
func windowsFindUSBDrive(volume string) (string, error) {
|
||||
cmd := exec.Command("wmic",
|
||||
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) {
|
||||
cmd := executeCommand(options, "wmic",
|
||||
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
|
||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||
|
||||
@@ -650,30 +665,6 @@ func windowsFindUSBDrive(volume string) (string, error) {
|
||||
return "", errors.New("unable to locate a USB device to be flashed")
|
||||
}
|
||||
|
||||
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
|
||||
// number.
|
||||
func parseSize(s string) (int64, error) {
|
||||
s = strings.ToLower(strings.TrimSpace(s))
|
||||
if len(s) == 0 {
|
||||
return 0, errors.New("no size provided")
|
||||
}
|
||||
multiply := int64(1)
|
||||
switch s[len(s)-1] {
|
||||
case 'k':
|
||||
multiply = 1 << 10
|
||||
case 'm':
|
||||
multiply = 1 << 20
|
||||
case 'g':
|
||||
multiply = 1 << 30
|
||||
}
|
||||
if multiply != 1 {
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
n, err := strconv.ParseInt(s, 0, 64)
|
||||
n *= multiply
|
||||
return n, err
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
func getDefaultPort() (port string, err error) {
|
||||
var portPath string
|
||||
@@ -784,6 +775,15 @@ func printCompilerError(logln func(...interface{}), err error) {
|
||||
logln()
|
||||
}
|
||||
}
|
||||
case transform.CoroutinesError:
|
||||
logln(err.Pos.String() + ": " + err.Msg)
|
||||
logln("\ntraceback:")
|
||||
for _, line := range err.Traceback {
|
||||
logln(line.Name)
|
||||
if line.Position.IsValid() {
|
||||
logln("\t" + line.Position.String())
|
||||
}
|
||||
}
|
||||
case loader.Errors:
|
||||
logln("#", err.Pkg.ImportPath)
|
||||
for _, err := range err.Errs {
|
||||
@@ -832,6 +832,7 @@ func main() {
|
||||
target := flag.String("target", "", "LLVM target | .json file with TargetSpec")
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||
printCommands := flag.Bool("x", false, "Print commands")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
port := flag.String("port", "", "flash port")
|
||||
@@ -839,7 +840,6 @@ func main() {
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||
heapSize := flag.String("heap-size", "1M", "default heap size in bytes (only supported by WebAssembly)")
|
||||
|
||||
var flagJSON, flagDeps *bool
|
||||
if command == "help" || command == "list" {
|
||||
@@ -880,6 +880,7 @@ func main() {
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintCommands: *printCommands,
|
||||
Tags: *tags,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
@@ -893,16 +894,9 @@ func main() {
|
||||
options.LDFlags = strings.Split(*ldFlags, " ")
|
||||
}
|
||||
|
||||
var err error
|
||||
if options.HeapSize, err = parseSize(*heapSize); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Could not read heap size:", *heapSize)
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Setenv("CC", "clang -target="+*target)
|
||||
|
||||
err = options.Verify()
|
||||
err := options.Verify()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
usage()
|
||||
@@ -955,9 +949,17 @@ func main() {
|
||||
fmt.Fprintf(os.Stderr, "Unknown library: %s\n", name)
|
||||
os.Exit(1)
|
||||
}
|
||||
path, err := lib.Load(*target)
|
||||
tmpdir, err := ioutil.TempDir("", "tinygo*")
|
||||
if err != nil {
|
||||
handleCompilerError(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
path, err := lib.Load(*target, tmpdir)
|
||||
handleCompilerError(err)
|
||||
copyFile(path, outpath)
|
||||
err = copyFile(path, outpath)
|
||||
if err != nil {
|
||||
handleCompilerError(err)
|
||||
}
|
||||
case "flash", "gdb":
|
||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||
if command == "flash" {
|
||||
@@ -1076,10 +1078,7 @@ func main() {
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||
os.Exit(status.ExitStatus())
|
||||
}
|
||||
os.Exit(1)
|
||||
os.Exit(exitErr.ExitCode())
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
||||
os.Exit(1)
|
||||
|
||||
+14
-2
@@ -58,6 +58,9 @@ func TestCompiler(t *testing.T) {
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Run("Host", func(t *testing.T) {
|
||||
runPlatTests("", matches, t)
|
||||
if runtime.GOOS == "darwin" {
|
||||
runTest("testdata/libc/env.go", "", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,6 +107,7 @@ func TestCompiler(t *testing.T) {
|
||||
|
||||
t.Run("WASI", func(t *testing.T) {
|
||||
runPlatTests("wasi", matches, t)
|
||||
runTest("testdata/libc/env.go", "wasi", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -113,7 +117,6 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
|
||||
for _, path := range matches {
|
||||
path := path // redefine to avoid race condition
|
||||
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(path, target, t)
|
||||
@@ -133,7 +136,7 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
||||
return Build(src, out, opts)
|
||||
}
|
||||
|
||||
func runTest(path, target string, t *testing.T) {
|
||||
func runTest(path, target string, t *testing.T, environmentVars ...string) {
|
||||
// Get the expected output for this test.
|
||||
txtpath := path[:len(path)-3] + ".txt"
|
||||
if path[len(path)-1] == os.PathSeparator {
|
||||
@@ -182,6 +185,7 @@ func runTest(path, target string, t *testing.T) {
|
||||
ranTooLong := false
|
||||
if target == "" {
|
||||
cmd = exec.Command(binary)
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
} else {
|
||||
spec, err := compileopts.LoadTarget(target)
|
||||
if err != nil {
|
||||
@@ -193,6 +197,14 @@ func runTest(path, target string, t *testing.T) {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
|
||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||
for _, v := range environmentVars {
|
||||
cmd.Args = append(cmd.Args, "--env", v)
|
||||
}
|
||||
} else {
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
}
|
||||
}
|
||||
stdout := &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// STM32FXXX (except stm32f1xx) bitfield definitions that are not
|
||||
// auto-generated by gen-device-svd.go
|
||||
|
||||
// These apply to the stm32 families that use the MODER, OTYPE amd PUPDR
|
||||
// registers for managing GPIO functionality.
|
||||
|
||||
// Add in other families that use the same settings, e.g. stm32f0xx, etc
|
||||
|
||||
// +build stm32,!stm32f103xx
|
||||
|
||||
package stm32
|
||||
|
||||
// AltFunc represents the alternate function peripherals that can be mapped to
|
||||
// the GPIO ports. Since these differ by what is supported on the stm32 family
|
||||
// they are defined in the more specific files
|
||||
type AltFunc uint8
|
||||
|
||||
// Family-wide common post-reset AltFunc. This represents
|
||||
// normal GPIO operation of the pins
|
||||
const AF0_SYSTEM AltFunc = 0
|
||||
|
||||
const (
|
||||
// Register values for the chip
|
||||
// GPIOx_MODER
|
||||
GPIOModeInput = 0
|
||||
GPIOModeOutputGeneral = 1
|
||||
GPIOModeOutputAltFunc = 2
|
||||
GPIOModeAnalog = 3
|
||||
|
||||
// GPIOx_OTYPER
|
||||
GPIOOutputTypePushPull = 0
|
||||
GPIOOutputTypeOpenDrain = 1
|
||||
|
||||
// GPIOx_OSPEEDR
|
||||
GPIOSpeedLow = 0
|
||||
GPIOSpeedMid = 1
|
||||
GPIOSpeedHigh = 2 // Note: this is also low speed on stm32f0, see RM0091
|
||||
GPIOSpeedVeryHigh = 3
|
||||
|
||||
// GPIOx_PUPDR
|
||||
GPIOPUPDRFloating = 0
|
||||
GPIOPUPDRPullUp = 1
|
||||
GPIOPUPDRPullDown = 2
|
||||
)
|
||||
|
||||
// SPI prescaler values fPCLK / X
|
||||
const (
|
||||
SPI_PCLK_2 = 0
|
||||
SPI_PCLK_4 = 1
|
||||
SPI_PCLK_8 = 2
|
||||
SPI_PCLK_16 = 3
|
||||
SPI_PCLK_32 = 4
|
||||
SPI_PCLK_64 = 5
|
||||
SPI_PCLK_128 = 6
|
||||
SPI_PCLK_256 = 7
|
||||
)
|
||||
@@ -1,88 +0,0 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||
|
||||
// +build stm32,stm32f103xx
|
||||
|
||||
package stm32
|
||||
|
||||
const (
|
||||
// Flash Access Control Register flag values.
|
||||
FLASH_ACR_LATENCY_0 = 0x00000001
|
||||
FLASH_ACR_LATENCY_1 = 0x00000002
|
||||
FLASH_ACR_LATENCY_2 = 0x00000004
|
||||
|
||||
// Reset and Clock Control Control Register flag values.
|
||||
|
||||
// System Clock source
|
||||
RCC_CFGR_SW_HSI = 0
|
||||
RCC_CFGR_SW_HSE = 1
|
||||
RCC_CFGR_SW_PLL = 2
|
||||
|
||||
// Flags for when System Clock source is set.
|
||||
RCC_CFGR_SWS_HSI = 0x00000000
|
||||
RCC_CFGR_SWS_HSE = 0x00000004
|
||||
RCC_CFGR_SWS_PLL = 0x00000008
|
||||
|
||||
// Sets PCLK1
|
||||
RCC_CFGR_PPRE1_DIV_NONE = 0x00000000
|
||||
RCC_CFGR_PPRE1_DIV_2 = 0x00000400
|
||||
RCC_CFGR_PPRE1_DIV_4 = 0x00000500
|
||||
RCC_CFGR_PPRE1_DIV_8 = 0x00000600
|
||||
RCC_CFGR_PPRE1_DIV_16 = 0x00000700
|
||||
|
||||
// Sets PCLK2
|
||||
RCC_CFGR_PPRE2_DIV_NONE = 0x00000000
|
||||
RCC_CFGR_PPRE2_DIV_2 = 0x00002000
|
||||
RCC_CFGR_PPRE2_DIV_4 = 0x00002800
|
||||
RCC_CFGR_PPRE2_DIV_8 = 0x00003000
|
||||
RCC_CFGR_PPRE2_DIV_16 = 0x00003800
|
||||
|
||||
// Sets PLL multiplier
|
||||
RCC_CFGR_PLLMUL_2 = 0x00000000
|
||||
RCC_CFGR_PLLMUL_3 = 0x00040000
|
||||
RCC_CFGR_PLLMUL_4 = 0x00080000
|
||||
RCC_CFGR_PLLMUL_5 = 0x000C0000
|
||||
RCC_CFGR_PLLMUL_6 = 0x00100000
|
||||
RCC_CFGR_PLLMUL_7 = 0x00140000
|
||||
RCC_CFGR_PLLMUL_8 = 0x00180000
|
||||
RCC_CFGR_PLLMUL_9 = 0x001C0000
|
||||
RCC_CFGR_PLLMUL_10 = 0x00200000
|
||||
RCC_CFGR_PLLMUL_11 = 0x00240000
|
||||
RCC_CFGR_PLLMUL_12 = 0x00280000
|
||||
RCC_CFGR_PLLMUL_13 = 0x002C0000
|
||||
RCC_CFGR_PLLMUL_14 = 0x00300000
|
||||
RCC_CFGR_PLLMUL_15 = 0x00340000
|
||||
RCC_CFGR_PLLMUL_16 = 0x00380000
|
||||
|
||||
// RTC clock source
|
||||
RCC_RTCCLKSource_LSE = 0x00000100
|
||||
RCC_RTCCLKSource_LSI = 0x00000200
|
||||
RCC_RTCCLKSource_HSE_Div128 = 0x00000300
|
||||
|
||||
// SPI settings
|
||||
SPI_FirstBit_MSB = 0x0000
|
||||
SPI_FirstBit_LSB = 0x0080
|
||||
|
||||
SPI_BaudRatePrescaler_2 = 0x0000
|
||||
SPI_BaudRatePrescaler_4 = 0x0008
|
||||
SPI_BaudRatePrescaler_8 = 0x0010
|
||||
SPI_BaudRatePrescaler_16 = 0x0018
|
||||
SPI_BaudRatePrescaler_32 = 0x0020
|
||||
SPI_BaudRatePrescaler_64 = 0x0028
|
||||
SPI_BaudRatePrescaler_128 = 0x0030
|
||||
SPI_BaudRatePrescaler_256 = 0x0038
|
||||
|
||||
SPI_Direction_2Lines_FullDuplex = 0x0000
|
||||
SPI_Direction_2Lines_RxOnly = 0x0400
|
||||
SPI_Direction_1Line_Rx = 0x8000
|
||||
SPI_Direction_1Line_Tx = 0xC000
|
||||
|
||||
SPI_Mode_Master = 0x0104
|
||||
SPI_Mode_Slave = 0x0000
|
||||
|
||||
SPI_NSS_Soft = 0x0200
|
||||
SPI_NSS_Hard = 0x0000
|
||||
|
||||
SPI_NSSInternalSoft_Set = 0x0100
|
||||
SPI_NSSInternalSoft_Reset = 0xFEFF
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// STM32FXXX (except stm32f1xx) bitfield definitions that are not
|
||||
// auto-generated by gen-device-svd.go
|
||||
// +build stm32f4
|
||||
|
||||
// Alternate function settings on the stm32f4 series
|
||||
|
||||
package stm32
|
||||
|
||||
const (
|
||||
// Alternative peripheral pin functions
|
||||
// AF0_SYSTEM is defined im the common bitfields package
|
||||
AF1_TIM1_2 AltFunc = 1
|
||||
AF2_TIM3_4_5 = 2
|
||||
AF3_TIM8_9_10_11 = 3
|
||||
AF4_I2C1_2_3 = 4
|
||||
AF5_SPI1_SPI2 = 5
|
||||
AF6_SPI3 = 6
|
||||
AF7_USART1_2_3 = 7
|
||||
AF8_USART4_5_6 = 8
|
||||
AF9_CAN1_CAN2_TIM12_13_14 = 9
|
||||
AF10_OTG_FS_OTG_HS = 10
|
||||
AF11_ETH = 11
|
||||
AF12_FSMC_SDIO_OTG_HS_1 = 12
|
||||
AF13_DCMI = 13
|
||||
AF14 = 14
|
||||
AF15_EVENTOUT = 15
|
||||
)
|
||||
@@ -15,7 +15,7 @@ func main() {
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
sensor := machine.ADC{machine.ADC2}
|
||||
sensor.Configure()
|
||||
sensor.Configure(machine.ADCConfig{})
|
||||
|
||||
for {
|
||||
val := sensor.Get()
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package machine
|
||||
|
||||
// Hardware abstraction layer for the analog-to-digital conversion (ADC)
|
||||
// peripheral.
|
||||
|
||||
// ADCConfig holds ADC configuration parameters. If left unspecified, the zero
|
||||
// value of each parameter will use the peripheral's default settings.
|
||||
type ADCConfig struct {
|
||||
Reference uint32 // analog reference voltage (AREF) in millivolts
|
||||
Resolution uint32 // number of bits for a single conversion (e.g., 8, 10, 12)
|
||||
Samples uint32 // number of samples for a single conversion (e.g., 4, 8, 16, 32)
|
||||
}
|
||||
@@ -58,7 +58,7 @@ const (
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
UART0 = &USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
@@ -66,8 +66,8 @@ const (
|
||||
SDA_PIN = P0_05 // I2C0 external
|
||||
SCL_PIN = P0_04 // I2C0 external
|
||||
|
||||
SDA1_PIN = P0_00 // I2C1 internal
|
||||
SCL1_PIN = P0_01 // I2C1 internal
|
||||
SDA1_PIN = P1_10 // I2C1 internal
|
||||
SCL1_PIN = P1_12 // I2C1 internal
|
||||
)
|
||||
|
||||
// SPI pins (internal flash)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
const HasLowFrequencyCrystal = false
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
@@ -105,7 +105,7 @@ const (
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
UART0 = &USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -77,7 +77,7 @@ const (
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
UART0 = &USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -122,17 +122,17 @@ var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART3,
|
||||
AltFuncSelector: stm32.AF7_USART1_2_3,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART6,
|
||||
AltFuncSelector: stm32.AF8_USART4_5_6,
|
||||
AltFuncSelector: AF8_USART4_5_6,
|
||||
}
|
||||
UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
AltFuncSelector: stm32.AF7_USART1_2_3,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART0 = UART1
|
||||
)
|
||||
@@ -181,15 +181,15 @@ const (
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: stm32.SPI2,
|
||||
AltFuncSelector: stm32.AF5_SPI1_SPI2,
|
||||
AltFuncSelector: AF5_SPI1_SPI2,
|
||||
}
|
||||
SPI2 = SPI{
|
||||
Bus: stm32.SPI3,
|
||||
AltFuncSelector: stm32.AF6_SPI3,
|
||||
AltFuncSelector: AF6_SPI3,
|
||||
}
|
||||
SPI3 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: stm32.AF5_SPI1_SPI2,
|
||||
AltFuncSelector: AF5_SPI1_SPI2,
|
||||
}
|
||||
SPI0 = SPI1
|
||||
)
|
||||
@@ -229,15 +229,15 @@ const (
|
||||
var (
|
||||
I2C1 = I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: stm32.AF4_I2C1_2_3,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
I2C2 = I2C{
|
||||
Bus: stm32.I2C2,
|
||||
AltFuncSelector: stm32.AF4_I2C1_2_3,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
I2C3 = I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: stm32.AF4_I2C1_2_3,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
// +build grandcentral_m4
|
||||
|
||||
package machine
|
||||
|
||||
// Digital pins
|
||||
const (
|
||||
// = Pin Alt. Function SERCOM PWM Timer Interrupt
|
||||
// ------ -------------------- -------- ----------- -----------
|
||||
D0 = PB25 // UART1 RX 0[1] EXTI9
|
||||
D1 = PB24 // UART1 TX 0[0] EXTI8
|
||||
D2 = PC18 // TCC0[2] EXTI2
|
||||
D3 = PC19 // TCC0[3] EXTI3
|
||||
D4 = PC20 // TCC0[4] EXTI4
|
||||
D5 = PC21 // TCC0[5] EXTI5
|
||||
D6 = PD20 // TCC1[0] EXTI10
|
||||
D7 = PD21 // TCC1[1] EXTI11
|
||||
D8 = PB18 // TCC1[0] EXTI2
|
||||
D9 = PB02 // TC6[0] EXTI3
|
||||
D10 = PB22 // TC7[0] EXTI6
|
||||
D11 = PB23 // EXTI7
|
||||
D12 = PB00 // TC7[0] EXTI0
|
||||
D13 = PB01 // On-board LED TC7[1] EXTI1
|
||||
D14 = PB16 // UART4 TX, I2S0 SCK 5[0] TC6[0] EXTI0
|
||||
D15 = PB17 // UART4 RX, I2S0 MCK 5[1] EXTI1
|
||||
D16 = PC22 // UART3 TX 1[0] EXTI6
|
||||
D17 = PC23 // UART3 RX 1[1] EXTI6
|
||||
D18 = PB12 // UART2 TX 4[0] TCC3[0] EXTI12
|
||||
D19 = PB13 // UART2 RX 4[1] TCC3[1] EXTI13
|
||||
D20 = PB20 // I2C0 SDA 3[0] EXTI4
|
||||
D21 = PB21 // I2C0 SCL 3[1] EXTI5
|
||||
D22 = PD12 // EXTI7
|
||||
D23 = PA15 // TCC2[1] EXTI15
|
||||
D24 = PC17 // I2C1 SCL 6[1] TCC0[1] EXTI1
|
||||
D25 = PC16 // I2C1 SDA 6[0] TCC0[0] EXTI0
|
||||
D26 = PA12 // PCC DEN1 TC2[0] EXTI12
|
||||
D27 = PA13 // PCC DEN2 TC2[1] EXTI13
|
||||
D28 = PA14 // PCC CLK TCC2[0] EXTI14
|
||||
D29 = PB19 // PCC XCLK EXTI3
|
||||
D30 = PA23 // PCC D7 TC4[1] EXTI7
|
||||
D31 = PA22 // PCC D6, I2S0 SDI TC4[0] EXTI6
|
||||
D32 = PA21 // PCC D5, I2S0 SDO EXTI5
|
||||
D33 = PA20 // PCC D4, I2S0 FS EXTI4
|
||||
D34 = PA19 // PCC D3 TC3[1] EXTI3
|
||||
D35 = PA18 // PCC D2 TC3[0] EXTI2
|
||||
D36 = PA17 // PCC D1 EXTI1
|
||||
D37 = PA16 // PCC D0 EXTI0
|
||||
D38 = PB15 // PCC D9 TCC4[1] EXTI15
|
||||
D39 = PB14 // PCC D8 TCC4[0] EXTI14
|
||||
D40 = PC13 // PCC D11 EXTI13
|
||||
D41 = PC12 // PCC D10 EXTI12
|
||||
D42 = PC15 // PCC D13 EXTI15
|
||||
D43 = PC14 // PCC D12 EXTI14
|
||||
D44 = PC11 // EXTI11
|
||||
D45 = PC10 // EXTI10
|
||||
D46 = PC06 // EXTI6
|
||||
D47 = PC07 // EXTI5
|
||||
D48 = PC04 // EXTI4
|
||||
D49 = PC05 // EXTI5
|
||||
D50 = PD11 // SPI0 SDI 7[3] EXTI11
|
||||
D51 = PD08 // SPI0 SDO 7[0] EXTI8
|
||||
D52 = PD09 // SPI0 SCK 7[1] EXTI9
|
||||
D53 = PD10 // SPI0 CS EXTI10
|
||||
D54 = PB05 // ADC1 (A8) EXTI5
|
||||
D55 = PB06 // ADC1 (A9) EXTI6
|
||||
D56 = PB07 // ADC1 (A10) EXTI7
|
||||
D57 = PB08 // ADC1 (A11) EXTI8
|
||||
D58 = PB09 // ADC1 (A12) EXTI9
|
||||
D59 = PA04 // ADC0 (A13) TC0[0] EXTI4
|
||||
D60 = PA06 // ADC0 (A14) TC1[0] EXTI6
|
||||
D61 = PA07 // ADC0 (A15) TC1[1] EXTI7
|
||||
D62 = PB20 // I2C0 SDA 3[0] TCC1[2] EXTI4
|
||||
D63 = PB21 // I2C0 SCL 3[1] TCC1[3] EXTI5
|
||||
D64 = PD11 // SPI0 SDI 7[3] EXTI6
|
||||
D65 = PD08 // SPI0 SDO 7[0] EXTI3
|
||||
D66 = PD09 // SPI0 SCK 7[1] EXTI4
|
||||
D67 = PA02 // ADC0 (A0), DAC0 EXTI2
|
||||
D68 = PA05 // ADC0 (A1), DAC1 EXTI5
|
||||
D69 = PB03 // ADC0 (A2) TC6[1] EXTI3
|
||||
D70 = PC00 // ADC1 (A3) EXTI0
|
||||
D71 = PC01 // ADC1 (A4) EXTI1
|
||||
D72 = PC02 // ADC1 (A5) EXTI2
|
||||
D73 = PC03 // ADC1 (A6) EXTI3
|
||||
D74 = PB04 // ADC1 (A7) EXTI4
|
||||
D75 = PC31 // UART RX LED
|
||||
D76 = PC30 // UART TX LED
|
||||
D77 = PA27 // USB HOST EN
|
||||
D78 = PA24 // USB DM EXTI8
|
||||
D79 = PA25 // USB DP EXTI9
|
||||
D80 = PB29 // SD/SPI1 SDI 2[3]
|
||||
D81 = PB27 // SD/SPI1 SCK 2[1]
|
||||
D82 = PB26 // SD/SPI1 SDO 2[0]
|
||||
D83 = PB28 // SD/SPI1 CS
|
||||
D84 = PA03 // AREF EXTI3
|
||||
D85 = PA02 // DAC0 EXTI2
|
||||
D86 = PA05 // DAC1 EXTI5
|
||||
D87 = PB01 // On-board LED (D13) TC7[1] EXTI1
|
||||
D88 = PC24 // On-board NeoPixel
|
||||
D89 = PB10 // QSPI SCK EXTI10
|
||||
D90 = PB11 // QSPI CS EXTI11
|
||||
D91 = PA08 // QSPI ID0 EXTI(NMI)
|
||||
D92 = PA09 // QSPI ID1 EXTI9
|
||||
D93 = PA10 // QSPI ID2 EXTI10
|
||||
D94 = PA11 // QSPI ID3 EXTI11
|
||||
D95 = PB31 // SD Detect EXTI15
|
||||
D96 = PB30 // SWO EXTI14
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = D67 // (PA02) ADC0 ch. 0,
|
||||
A1 = D68 // (PA05) ADC0 ch. 5,
|
||||
A2 = D69 // (PB03) ADC0 ch. 15
|
||||
A3 = D70 // (PC00) ADC1 ch. 10
|
||||
A4 = D71 // (PC01) ADC1 ch. 11
|
||||
A5 = D72 // (PC02) ADC1 ch. 4
|
||||
A6 = D73 // (PC03) ADC1 ch. 5
|
||||
A7 = D74 // (PB04) ADC1 ch. 6
|
||||
A8 = D54 // (PB05) ADC1 ch. 7
|
||||
A9 = D55 // (PB06) ADC1 ch. 8
|
||||
A10 = D56 // (PB07) ADC1 ch. 9
|
||||
A11 = D57 // (PB08) ADC1 ch. 0
|
||||
A12 = D58 // (PB09) ADC1 ch. 1
|
||||
A13 = D59 // (PA04) ADC0 ch. 4
|
||||
A14 = D60 // (PA06) ADC0 ch. 6
|
||||
A15 = D61 // (PA07) ADC0 ch. 7
|
||||
|
||||
AREF = D84 // (PA03)
|
||||
)
|
||||
|
||||
// LED pins
|
||||
const (
|
||||
LED_PIN = D13 // (PB01), also on D87
|
||||
UART_RX_LED_PIN = D75 // (PC31)
|
||||
UART_TX_LED_PIN = D76 // (PC30)
|
||||
NEOPIXEL_PIN = D88 // (PC24)
|
||||
|
||||
// aliases used by examples and drivers
|
||||
LED = LED_PIN
|
||||
LED_RX = UART_RX_LED_PIN
|
||||
LED_TX = UART_TX_LED_PIN
|
||||
NEOPIXEL = NEOPIXEL_PIN
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART1_RX_PIN = D0 // (PB25)
|
||||
UART1_TX_PIN = D1 // (PB24)
|
||||
|
||||
UART2_RX_PIN = D19 // (PB13)
|
||||
UART2_TX_PIN = D18 // (PB12)
|
||||
|
||||
UART3_RX_PIN = D17 // (PC23)
|
||||
UART3_TX_PIN = D16 // (PC22)
|
||||
|
||||
UART4_RX_PIN = D15 // (PB17)
|
||||
UART4_TX_PIN = D14 // (PB16)
|
||||
|
||||
UART_RX_PIN = UART1_RX_PIN // default pins
|
||||
UART_TX_PIN = UART1_TX_PIN //
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D66 // (PD09), also on D52
|
||||
SPI0_SDO_PIN = D65 // (PD08), also on D51
|
||||
SPI0_SDI_PIN = D64 // (PD11), also on D50
|
||||
SPI0_CS_PIN = D53 // (PD10)
|
||||
|
||||
SPI1_SCK_PIN = D81 // (PB27)
|
||||
SPI1_SDO_PIN = D82 // (PB26)
|
||||
SPI1_SDI_PIN = D80 // (PB29)
|
||||
|
||||
SPI_SCK_PIN = SPI0_SCK_PIN // default pins
|
||||
SPI_SDO_PIN = SPI0_SDO_PIN //
|
||||
SPI_SDI_PIN = SPI0_SDI_PIN //
|
||||
SPI_CS_PIN = SPI0_CS_PIN //
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
I2C0_SDA_PIN = D62 // (PB20), also on D20
|
||||
I2C0_SCL_PIN = D63 // (PB21), also on D21
|
||||
|
||||
I2C1_SDA_PIN = D25 // (PC16)
|
||||
I2C1_SCL_PIN = D24 // (PC17)
|
||||
|
||||
I2C_SDA_PIN = I2C0_SDA_PIN // default pins
|
||||
I2C_SCL_PIN = I2C0_SCL_PIN //
|
||||
|
||||
SDA_PIN = I2C_SDA_PIN // unconventional pin names
|
||||
SCL_PIN = I2C_SCL_PIN // (required by machine_atsamd51.go)
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S0_SCK_PIN = D14 // (PB16)
|
||||
I2S0_MCK_PIN = D15 // (PB17)
|
||||
I2S0_FS_PIN = D33 // (PA20)
|
||||
I2S0_SDO_PIN = D32 // (PA21)
|
||||
I2S0_SDI_PIN = D31 // (PA22)
|
||||
|
||||
I2S_SCK_PIN = I2S0_SCK_PIN // default pins
|
||||
I2S_WS_PIN = I2S0_FS_PIN //
|
||||
I2S_SD_PIN = I2S0_SDO_PIN //
|
||||
)
|
||||
|
||||
// SD card pins
|
||||
const (
|
||||
SD0_SCK_PIN = D81 // (PB27)
|
||||
SD0_SDO_PIN = D82 // (PB26)
|
||||
SD0_SDI_PIN = D80 // (PB29)
|
||||
SD0_CS_PIN = D83 // (PB28)
|
||||
SD0_DET_PIN = D95 // (PB31)
|
||||
|
||||
SDCARD_SCK_PIN = SD0_SCK_PIN // default pins
|
||||
SDCARD_SDO_PIN = SD0_SDO_PIN //
|
||||
SDCARD_SDI_PIN = SD0_SDI_PIN //
|
||||
SDCARD_CS_PIN = SD0_CS_PIN //
|
||||
SDCARD_DET_PIN = SD0_DET_PIN //
|
||||
)
|
||||
|
||||
// Other peripheral constants
|
||||
const (
|
||||
RESET_MAGIC_VALUE = 0xF01669EF // Used to reset into bootloader
|
||||
)
|
||||
|
||||
// USB CDC pins
|
||||
const (
|
||||
USBCDC_HOSTEN_PIN = D77 // (PA27) host enable
|
||||
USBCDC_DM_PIN = D78 // (PA24) D-
|
||||
USBCDC_DP_PIN = D79 // (PA25) D+
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit Grand Central M4"
|
||||
usb_STRING_MANUFACTURER = "Adafruit"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x8031
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
// +build grandcentral_m4
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_2, UART2.handleInterrupt)
|
||||
UART3.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART3.handleInterrupt)
|
||||
UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART4.handleInterrupt)
|
||||
}
|
||||
|
||||
// UART on the Grand Central M4
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART_INT,
|
||||
SERCOM: 4,
|
||||
}
|
||||
UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM1_USART_INT,
|
||||
SERCOM: 1,
|
||||
}
|
||||
UART4 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART_INT,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// I2C on the Grand Central M4
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM3_I2CM,
|
||||
SERCOM: 3,
|
||||
}
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM6_I2CM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Grand Central M4
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM7_SPIM,
|
||||
SERCOM: 7,
|
||||
}
|
||||
SPI1 = SPI{ // SD card
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
@@ -72,7 +72,7 @@ const (
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
UART0 = &USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// +build lgt92
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
LED1 = PA12
|
||||
LED2 = PA8
|
||||
LED3 = PA11
|
||||
|
||||
LED_RED = LED1
|
||||
LED_BLUE = LED2
|
||||
LED_GREEN = LED3
|
||||
|
||||
// Default led
|
||||
LED = LED1
|
||||
|
||||
BUTTON = PB14
|
||||
|
||||
// LG GPS module
|
||||
GPS_STANDBY_PIN = PB3
|
||||
GPS_RESET_PIN = PB4
|
||||
GPS_POWER_PIN = PB5
|
||||
|
||||
MEMS_ACCEL_CS = PE3
|
||||
MEMS_ACCEL_INT1 = PE0
|
||||
MEMS_ACCEL_INT2 = PE1
|
||||
|
||||
// SPI
|
||||
SPI1_SCK_PIN = PA5
|
||||
SPI1_SDI_PIN = PA6
|
||||
SPI1_SDO_PIN = PA7
|
||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
||||
|
||||
// LORA RFM95 Radio
|
||||
RFM95_DIO0_PIN = PC13
|
||||
|
||||
//TinyGo UART is MCU LPUSART1
|
||||
UART_RX_PIN = PA13
|
||||
UART_TX_PIN = PA14
|
||||
|
||||
//TinyGo UART1 is MCU USART1
|
||||
UART1_RX_PIN = PB6
|
||||
UART1_TX_PIN = PB7
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// Console UART (LPUSART1)
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.LPUART1,
|
||||
AltFuncSelector: 6,
|
||||
}
|
||||
|
||||
// Gps UART
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
AltFuncSelector: 0,
|
||||
}
|
||||
|
||||
// SPI
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
}
|
||||
SPI1 = &SPI0
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Enable UARTs Interrupts
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_AES_RNG_LPUART1, UART0.handleInterrupt)
|
||||
UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, UART1.handleInterrupt)
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// +build microbit_v2
|
||||
|
||||
package machine
|
||||
|
||||
// The micro:bit does not have a 32kHz crystal on board.
|
||||
const HasLowFrequencyCrystal = false
|
||||
|
||||
const (
|
||||
LED = P13
|
||||
LED1 = LED
|
||||
)
|
||||
|
||||
// Buttons on the micro:bit v2 (A and B)
|
||||
const (
|
||||
BUTTON Pin = BUTTONA
|
||||
BUTTONA Pin = P5
|
||||
BUTTONB Pin = P11
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = P34
|
||||
UART_RX_PIN Pin = P33
|
||||
)
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC0 Pin = P0
|
||||
ADC1 Pin = P1
|
||||
ADC2 Pin = P2
|
||||
)
|
||||
|
||||
// I2C0 (internal) pins
|
||||
const (
|
||||
SDA_PIN Pin = SDA0_PIN
|
||||
SCL_PIN Pin = SCL0_PIN
|
||||
SDA0_PIN Pin = P30
|
||||
SCL0_PIN Pin = P31
|
||||
)
|
||||
|
||||
// I2C1 (external) pins
|
||||
const (
|
||||
SDA1_PIN Pin = P20
|
||||
SCL1_PIN Pin = P19
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = P13
|
||||
SPI0_SDO_PIN Pin = P15
|
||||
SPI0_SDI_PIN Pin = P14
|
||||
)
|
||||
|
||||
// GPIO/Analog pins
|
||||
const (
|
||||
P0 Pin = 2
|
||||
P1 Pin = 3
|
||||
P2 Pin = 4
|
||||
P3 Pin = 31
|
||||
P4 Pin = 28
|
||||
P5 Pin = 14
|
||||
P6 Pin = 37
|
||||
P7 Pin = 11
|
||||
P8 Pin = 10
|
||||
P9 Pin = 9
|
||||
P10 Pin = 30
|
||||
P11 Pin = 23
|
||||
P12 Pin = 12
|
||||
P13 Pin = 17
|
||||
P14 Pin = 1
|
||||
P15 Pin = 13
|
||||
P16 Pin = 34
|
||||
P19 Pin = 26
|
||||
P20 Pin = 32
|
||||
P21 Pin = 21
|
||||
P22 Pin = 22
|
||||
P23 Pin = 15
|
||||
P24 Pin = 24
|
||||
P25 Pin = 19
|
||||
P26 Pin = 36
|
||||
P27 Pin = 0
|
||||
P28 Pin = 20
|
||||
P29 Pin = 5
|
||||
P30 Pin = 16
|
||||
P31 Pin = 8
|
||||
P32 Pin = 25
|
||||
P33 Pin = 40
|
||||
P34 Pin = 6
|
||||
)
|
||||
|
||||
// LED matrix pins
|
||||
const (
|
||||
LED_COL_1 Pin = P0_28
|
||||
LED_COL_2 Pin = P0_11
|
||||
LED_COL_3 Pin = P0_31
|
||||
LED_COL_4 Pin = P1_05
|
||||
LED_COL_5 Pin = P0_30
|
||||
LED_ROW_1 Pin = P0_21
|
||||
LED_ROW_2 Pin = P0_22
|
||||
LED_ROW_3 Pin = P0_15
|
||||
LED_ROW_4 Pin = P0_24
|
||||
LED_ROW_5 Pin = P0_19
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "BBC micro:bit V2"
|
||||
usb_STRING_MANUFACTURER = "BBC"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x0d28
|
||||
usb_PID uint16 = 0x0204
|
||||
)
|
||||
@@ -0,0 +1,84 @@
|
||||
// +build nicenano
|
||||
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D006 = P0_06
|
||||
D008 = P0_08
|
||||
D017 = P0_17
|
||||
D020 = P0_20
|
||||
D022 = P0_22
|
||||
D024 = P0_24
|
||||
D100 = P1_00
|
||||
D011 = P0_11
|
||||
D104 = P1_04
|
||||
D106 = P1_06
|
||||
|
||||
D004 = P0_04 // AIN2; P0.04 (AIN2) is used to read the voltage of the battery via ADC. It can’t be used for any other function.
|
||||
D013 = P0_13 // VCC 3.3V; P0.13 on VCC shuts off the power to VCC when you set it to high; This saves on battery immensely for LEDs of all kinds that eat power even when off
|
||||
D115 = P1_15
|
||||
D113 = P1_13
|
||||
D031 = P0_31 // AIN7
|
||||
D029 = P0_29 // AIN5
|
||||
D002 = P0_02 // AIN0
|
||||
|
||||
D111 = P1_11
|
||||
D010 = P0_10 // NFC2
|
||||
D009 = P0_09 // NFC1
|
||||
|
||||
D026 = P0_26
|
||||
D012 = P0_12
|
||||
D101 = P1_01
|
||||
D102 = P1_02
|
||||
D107 = P1_07
|
||||
)
|
||||
|
||||
// Analog Pins
|
||||
const (
|
||||
AIN2 = P0_04 // Battery
|
||||
AIN7 = P0_31
|
||||
AIN5 = P0_29
|
||||
AIN0 = P0_02
|
||||
)
|
||||
|
||||
const (
|
||||
LED = P0_15
|
||||
)
|
||||
|
||||
// UART0 pins (logical UART1)
|
||||
const (
|
||||
UART_RX_PIN = P0_06
|
||||
UART_TX_PIN = P0_08
|
||||
)
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = P0_17 // I2C0 external
|
||||
SCL_PIN = P0_20 // I2C0 external
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = P0_22 // SCK
|
||||
SPI0_SDO_PIN = P0_24 // SDO
|
||||
SPI0_SDI_PIN = P1_00 // SDI
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "nice!nano"
|
||||
usb_STRING_MANUFACTURER = "Nice Keyboards"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x0029
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
// +build nrf52840_mdk_usb_dongle
|
||||
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
|
||||
// LEDs on the nrf52840-mdk-usb-dongle
|
||||
const (
|
||||
LED Pin = LED_GREEN
|
||||
LED_GREEN Pin = 22
|
||||
LED_RED Pin = 23
|
||||
LED_BLUE Pin = 24
|
||||
)
|
||||
|
||||
// RESET/USR button, depending on value of PSELRESET UICR register
|
||||
const (
|
||||
BUTTON Pin = 18
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = NoPin
|
||||
UART_RX_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
)
|
||||
|
||||
// I2C pins (unused)
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
SCL_PIN = NoPin
|
||||
)
|
||||
|
||||
// SPI pins (unused)
|
||||
const (
|
||||
SPI0_SCK_PIN = NoPin
|
||||
SPI0_SDO_PIN = NoPin
|
||||
SPI0_SDI_PIN = NoPin
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK USB Dongle"
|
||||
usb_STRING_MANUFACTURER = "Makerdiary"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x1915
|
||||
usb_PID uint16 = 0xCAFE
|
||||
)
|
||||
@@ -38,7 +38,7 @@ const (
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK USB Dongle"
|
||||
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK"
|
||||
usb_STRING_MANUFACTURER = "Makerdiary"
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// +build nucleol552ze
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
LED = LED_BUILTIN
|
||||
LED_BUILTIN = LED_GREEN
|
||||
LED_GREEN = PC7
|
||||
LED_BLUE = PB7
|
||||
LED_RED = PA9
|
||||
)
|
||||
|
||||
const (
|
||||
BUTTON = BUTTON_USER
|
||||
BUTTON_USER = PC13
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
// PG7 and PG8 are connected to the ST-Link Virtual Com Port (VCP)
|
||||
UART_TX_PIN = PG7
|
||||
UART_RX_PIN = PG8
|
||||
UART_ALT_FN = 8 // GPIO_AF8_LPUART1
|
||||
)
|
||||
|
||||
var (
|
||||
// LPUART1 is the hardware serial port connected to the onboard ST-LINK
|
||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||
// Both UART0 and UART1 refer to LPUART1.
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.LPUART1,
|
||||
AltFuncSelector: UART_ALT_FN,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_LPUART1, UART0.handleInterrupt)
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// +build p1am_100
|
||||
|
||||
// This contains the pin mappings for the ProductivityOpen P1AM-100 board.
|
||||
//
|
||||
// For more information, see: https://facts-engineering.github.io/
|
||||
//
|
||||
package machine
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0x07738135
|
||||
|
||||
// Note: On the P1AM-100, pins D8, D9, D10, A3, and A4 are used for
|
||||
// communication with the base controller.
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 Pin = PA22 // PWM available
|
||||
D1 Pin = PA23 // PWM available
|
||||
D2 Pin = PA10 // PWM available
|
||||
D3 Pin = PA11 // PWM available
|
||||
D4 Pin = PB10 // PWM available
|
||||
D5 Pin = PB11 // PWM available
|
||||
D6 Pin = PA20 // PWM available
|
||||
D7 Pin = PA21 // PWM available
|
||||
|
||||
D8 Pin = PA16 // PWM available
|
||||
D9 Pin = PA17
|
||||
D10 Pin = PA19 // PWM available
|
||||
D11 Pin = PA08
|
||||
D12 Pin = PA09
|
||||
D13 Pin = PB23
|
||||
D14 Pin = PB22
|
||||
|
||||
// Remaining pins are shared with analog pins
|
||||
D15 Pin = PA02
|
||||
|
||||
D16 Pin = PB02
|
||||
D17 Pin = PB03
|
||||
D18 Pin = PA04 // PWM available
|
||||
D19 Pin = PA05 // PWM available
|
||||
D20 Pin = PA06
|
||||
D21 Pin = PA07
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 Pin = PA02 // ADC/AIN[0]
|
||||
A1 Pin = PB02 // ADC/AIN[10]
|
||||
A2 Pin = PB03 // ADC/AIN[11]
|
||||
A3 Pin = PA04 // ADC/AIN[4]
|
||||
A4 Pin = PA05 // ADC/AIN[5]
|
||||
A5 Pin = PA06 // ADC/AIN[6]
|
||||
A6 Pin = PA07 // ADC/AIN[7]
|
||||
)
|
||||
|
||||
const (
|
||||
SWITCH Pin = PA28
|
||||
LED Pin = PB08
|
||||
ADC_BATTERY Pin = PB09 // ADC/AIN[3]
|
||||
)
|
||||
|
||||
// P1AM Base Controller
|
||||
const (
|
||||
BASE_SLAVE_SELECT_PIN Pin = A3
|
||||
BASE_SLAVE_ACK_PIN Pin = A4
|
||||
BASE_ENABLE_PIN Pin = PB09
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN Pin = PA24
|
||||
USBCDC_DP_PIN Pin = PA25
|
||||
USBCDC_HOST_ENABLE_PIN Pin = PA18
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_RX_PIN Pin = PB23 // RX: SERCOM5/PAD[3]
|
||||
UART_TX_PIN Pin = PB22 // TX: SERCOM5/PAD[2]
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = PA08 // SDA: SERCOM0/PAD[0]
|
||||
SCL_PIN Pin = PA09 // SCL: SERCOM0/PAD[1]
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = D9 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_SDO_PIN Pin = D8 // SDO: SERCOM1/PAD[0]
|
||||
SPI0_SDI_PIN Pin = D10 // SDI: SERCOM1/PAD[3]
|
||||
)
|
||||
|
||||
// SD card pins
|
||||
const (
|
||||
SDCARD_SDI_PIN Pin = PA15 // SDI: SERCOM2/PAD[3]
|
||||
SDCARD_SDO_PIN Pin = PA12 // SDO: SERCOM2/PAD[0]
|
||||
SDCARD_SCK_PIN Pin = PA13 // SCK: SERCOM2/PAD[1]
|
||||
SDCARD_SS_PIN Pin = PA14 // SS: as GPIO
|
||||
SDCARD_CD_PIN Pin = PA27
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN Pin = D2
|
||||
I2S_SD_PIN Pin = A6
|
||||
I2S_WS_PIN = D3
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "P1AM-100"
|
||||
usb_STRING_MANUFACTURER = "Facts Engineering"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x1354
|
||||
usb_PID uint16 = 0x4000
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
// +build sam,atsamd21,p1am_100
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART1 on the P1AM-100 connects to the normal TX/RX pins.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM3_USART,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the P1AM-100.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM0_I2CM,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the P1AM-100 is used for Base Controller.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPI,
|
||||
SERCOM: 1,
|
||||
}
|
||||
BASE_CONTROLLER_SPI = SPI0
|
||||
)
|
||||
|
||||
// SPI1 is connected to the SD card slot on the P1AM-100
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM2_SPI,
|
||||
SERCOM: 2,
|
||||
}
|
||||
SDCARD_SPI = SPI1
|
||||
)
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = USB
|
||||
Serial = &USB
|
||||
UART0 = NRF_UART0
|
||||
)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = USB
|
||||
Serial = &USB
|
||||
UART0 = NRF_UART0
|
||||
)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = USB
|
||||
Serial = &USB
|
||||
UART0 = NRF_UART0
|
||||
)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
AltFuncSelector: stm32.AF7_USART1_2_3,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
@@ -62,7 +62,19 @@ const (
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: stm32.AF5_SPI1_SPI2,
|
||||
AltFuncSelector: AF5_SPI1_SPI2,
|
||||
}
|
||||
SPI1 = &SPI0
|
||||
)
|
||||
|
||||
const (
|
||||
I2C0_SCL_PIN = PB6
|
||||
I2C0_SDA_PIN = PB9
|
||||
)
|
||||
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -136,9 +136,11 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
UART0 = &UART1 // alias UART0 to UART1
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART6,
|
||||
Bus: nxp.LPUART6,
|
||||
Buffer: NewRingBuffer(),
|
||||
txBuffer: NewRingBuffer(),
|
||||
muxRX: muxSelect{ // D0 (PA3 [AD_B0_03])
|
||||
mux: nxp.IOMUXC_LPUART6_RX_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART6_RX_SELECT_INPUT,
|
||||
@@ -149,8 +151,9 @@ var (
|
||||
},
|
||||
}
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART4,
|
||||
Bus: nxp.LPUART4,
|
||||
Buffer: NewRingBuffer(),
|
||||
txBuffer: NewRingBuffer(),
|
||||
muxRX: muxSelect{ // D7 (PB17 [B1_01])
|
||||
mux: nxp.IOMUXC_LPUART4_RX_SELECT_INPUT_DAISY_GPIO_B1_01_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART4_RX_SELECT_INPUT,
|
||||
@@ -161,8 +164,9 @@ var (
|
||||
},
|
||||
}
|
||||
UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART2,
|
||||
Bus: nxp.LPUART2,
|
||||
Buffer: NewRingBuffer(),
|
||||
txBuffer: NewRingBuffer(),
|
||||
muxRX: muxSelect{ // D15 (PA19 [AD_B1_03])
|
||||
mux: nxp.IOMUXC_LPUART2_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_03_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART2_RX_SELECT_INPUT,
|
||||
@@ -173,8 +177,9 @@ var (
|
||||
},
|
||||
}
|
||||
UART4 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART3,
|
||||
Bus: nxp.LPUART3,
|
||||
Buffer: NewRingBuffer(),
|
||||
txBuffer: NewRingBuffer(),
|
||||
muxRX: muxSelect{ // D16 (PA23 [AD_B1_07])
|
||||
mux: nxp.IOMUXC_LPUART3_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_07_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART3_RX_SELECT_INPUT,
|
||||
@@ -185,8 +190,9 @@ var (
|
||||
},
|
||||
}
|
||||
UART5 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART8,
|
||||
Bus: nxp.LPUART8,
|
||||
Buffer: NewRingBuffer(),
|
||||
txBuffer: NewRingBuffer(),
|
||||
muxRX: muxSelect{ // D21 (PA27 [AD_B1_11])
|
||||
mux: nxp.IOMUXC_LPUART8_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_11_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART8_RX_SELECT_INPUT,
|
||||
@@ -197,15 +203,17 @@ var (
|
||||
},
|
||||
}
|
||||
UART6 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART1,
|
||||
Bus: nxp.LPUART1,
|
||||
Buffer: NewRingBuffer(),
|
||||
txBuffer: NewRingBuffer(),
|
||||
// LPUART1 not connected via IOMUXC
|
||||
// RX: D24 (PA12 [AD_B0_12])
|
||||
// TX: D25 (PA13 [AD_B0_13])
|
||||
}
|
||||
UART7 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART7,
|
||||
Bus: nxp.LPUART7,
|
||||
Buffer: NewRingBuffer(),
|
||||
txBuffer: NewRingBuffer(),
|
||||
muxRX: muxSelect{ // D28 (PC18 [EMC_32])
|
||||
mux: nxp.IOMUXC_LPUART7_RX_SELECT_INPUT_DAISY_GPIO_EMC_32_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART7_RX_SELECT_INPUT,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build avr nrf sam stm32,!stm32f407,!stm32f7x2 fe310 k210
|
||||
// +build avr nrf sam stm32,!stm32f7x2,!stm32l5x2,!stm32l0 fe310 k210
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ package machine
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
@@ -13,7 +15,7 @@ type I2CConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) {
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
@@ -33,6 +35,8 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
|
||||
// Enable twi module.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
@@ -110,6 +114,12 @@ func (i2c I2C) readByte() byte {
|
||||
return byte(avr.TWDR.Get())
|
||||
}
|
||||
|
||||
// UART
|
||||
var (
|
||||
// UART0 is the hardware serial port on the AVR.
|
||||
UART0 = UART{Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
// UART on the AVR.
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
@@ -155,3 +165,108 @@ func (uart UART) WriteByte(c byte) error {
|
||||
avr.UDR0.Set(c) // send char
|
||||
return nil
|
||||
}
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
LSBFirst bool
|
||||
Mode uint8
|
||||
}
|
||||
|
||||
// SPI is for the Serial Peripheral Interface
|
||||
// Data is taken from http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf page 169 and following
|
||||
type SPI struct {
|
||||
// The registers for the SPIx port set by the chip
|
||||
spcr *volatile.Register8
|
||||
spdr *volatile.Register8
|
||||
spsr *volatile.Register8
|
||||
|
||||
// The io pins for the SPIx port set by the chip
|
||||
sck Pin
|
||||
sdi Pin
|
||||
sdo Pin
|
||||
cs Pin
|
||||
}
|
||||
|
||||
// Configure is intended to setup the SPI interface.
|
||||
func (s SPI) Configure(config SPIConfig) error {
|
||||
|
||||
// This is only here to help catch a bug with the configuration
|
||||
// where a machine missed a value.
|
||||
if s.spcr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
|
||||
s.spsr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
|
||||
s.spdr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
|
||||
s.sck == 0 || s.sdi == 0 || s.sdo == 0 || s.cs == 0 {
|
||||
return errSPIInvalidMachineConfig
|
||||
}
|
||||
|
||||
// Make the defaults meaningful
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 4000000
|
||||
}
|
||||
|
||||
// Default all port configuration bits to 0 for simplicity
|
||||
s.spcr.Set(0)
|
||||
s.spsr.Set(0)
|
||||
|
||||
// Setup pins output configuration
|
||||
s.sck.Configure(PinConfig{Mode: PinOutput})
|
||||
s.sdi.Configure(PinConfig{Mode: PinInput})
|
||||
s.sdo.Configure(PinConfig{Mode: PinOutput})
|
||||
|
||||
// Prevent CS glitches if the pin is enabled Low (0, default)
|
||||
s.cs.High()
|
||||
// If the CS pin is not configured as output the SPI port operates in
|
||||
// slave mode.
|
||||
s.cs.Configure(PinConfig{Mode: PinOutput})
|
||||
|
||||
frequencyDivider := CPUFrequency() / config.Frequency
|
||||
|
||||
switch {
|
||||
case frequencyDivider >= 128:
|
||||
s.spcr.SetBits(avr.SPCR_SPR0 | avr.SPCR_SPR1)
|
||||
case frequencyDivider >= 64:
|
||||
s.spcr.SetBits(avr.SPCR_SPR1)
|
||||
case frequencyDivider >= 32:
|
||||
s.spcr.SetBits(avr.SPCR_SPR1)
|
||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
||||
case frequencyDivider >= 16:
|
||||
s.spcr.SetBits(avr.SPCR_SPR0)
|
||||
case frequencyDivider >= 8:
|
||||
s.spcr.SetBits(avr.SPCR_SPR0)
|
||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
||||
case frequencyDivider >= 4:
|
||||
// The clock is already set to all 0's.
|
||||
default: // defaults to fastest which is /2
|
||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
||||
}
|
||||
|
||||
switch config.Mode {
|
||||
case Mode1:
|
||||
s.spcr.SetBits(avr.SPCR_CPHA)
|
||||
case Mode2:
|
||||
s.spcr.SetBits(avr.SPCR_CPOL)
|
||||
case Mode3:
|
||||
s.spcr.SetBits(avr.SPCR_CPHA | avr.SPCR_CPOL)
|
||||
default: // default is mode 0
|
||||
}
|
||||
|
||||
if config.LSBFirst {
|
||||
s.spcr.SetBits(avr.SPCR_DORD)
|
||||
}
|
||||
|
||||
// enable SPI, set controller, set clock rate
|
||||
s.spcr.SetBits(avr.SPCR_SPE | avr.SPCR_MSTR)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes the byte into the register and returns the read content
|
||||
func (s SPI) Transfer(b byte) (byte, error) {
|
||||
s.spdr.Set(uint8(b))
|
||||
|
||||
for !s.spsr.HasBits(avr.SPSR_SPIF) {
|
||||
}
|
||||
|
||||
return byte(s.spdr.Get()), nil
|
||||
}
|
||||
|
||||
@@ -69,3 +69,13 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR,
|
||||
spsr: avr.SPSR,
|
||||
spdr: avr.SPDR,
|
||||
sck: PB7,
|
||||
sdo: PB5,
|
||||
sdi: PB6,
|
||||
cs: PB4}
|
||||
|
||||
@@ -126,3 +126,13 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
return avr.PORTA, 255
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR,
|
||||
spdr: avr.SPDR,
|
||||
spsr: avr.SPSR,
|
||||
sck: PB1,
|
||||
sdo: PB2,
|
||||
sdi: PB3,
|
||||
cs: PB0}
|
||||
|
||||
@@ -88,3 +88,13 @@ func (pwm PWM) Set(value uint16) {
|
||||
panic("Invalid PWM pin")
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR,
|
||||
spdr: avr.SPDR,
|
||||
spsr: avr.SPSR,
|
||||
sck: PB5,
|
||||
sdo: PB3,
|
||||
sdi: PB4,
|
||||
cs: PB2}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// +build avr,atmega328pb
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const irq_USART0_RX = avr.IRQ_USART0_RX
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
switch {
|
||||
case p >= PB0 && p <= PB7: // port B
|
||||
return avr.PORTB, 1 << uint8(p-portB)
|
||||
case p >= PC0 && p <= PC7: // port C
|
||||
return avr.PORTC, 1 << uint8(p-portC)
|
||||
default: // port D
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
}
|
||||
}
|
||||
|
||||
// InitPWM initializes the registers needed for PWM.
|
||||
func InitPWM() {
|
||||
// use waveform generation
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
||||
|
||||
// set timer 0 prescale factor to 64
|
||||
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
||||
|
||||
// set timer 1 prescale factor to 64
|
||||
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
||||
|
||||
// put timer 1 in 8-bit phase correct pwm mode
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
||||
|
||||
// set timer 2 prescale factor to 64
|
||||
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
||||
|
||||
// configure timer 2 for phase correct pwm (8-bit)
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() error {
|
||||
switch pwm.Pin / 8 {
|
||||
case 0: // port B
|
||||
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
|
||||
case 2: // port D
|
||||
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||
// 8-bit value ranging from 0 to 255.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
value8 := uint8(value >> 8)
|
||||
switch pwm.Pin {
|
||||
case PD3:
|
||||
// connect pwm to pin on timer 2, channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
avr.OCR2B.Set(value8) // set pwm duty
|
||||
case PD5:
|
||||
// connect pwm to pin on timer 0, channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
avr.OCR0B.Set(value8) // set pwm duty
|
||||
case PD6:
|
||||
// connect pwm to pin on timer 0, channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
avr.OCR0A.Set(value8) // set pwm duty
|
||||
case PB1:
|
||||
// connect pwm to pin on timer 1, channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1AL.Set(value8) // set pwm duty
|
||||
case PB2:
|
||||
// connect pwm to pin on timer 1, channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1BL.Set(value8) // set pwm duty
|
||||
case PB3:
|
||||
// connect pwm to pin on timer 2, channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
avr.OCR2A.Set(value8) // set pwm duty
|
||||
default:
|
||||
panic("Invalid PWM pin")
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR0,
|
||||
spdr: avr.SPDR0,
|
||||
spsr: avr.SPSR0,
|
||||
sck: PB5,
|
||||
sdo: PB3,
|
||||
sdi: PB4,
|
||||
cs: PB2}
|
||||
|
||||
var SPI1 = SPI{
|
||||
spcr: avr.SPCR1,
|
||||
spdr: avr.SPDR1,
|
||||
spsr: avr.SPSR1,
|
||||
sck: PC1,
|
||||
sdo: PE3,
|
||||
sdi: PC0,
|
||||
cs: PE2}
|
||||
+149
-27
@@ -11,6 +11,7 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -307,13 +308,30 @@ func InitADC() {
|
||||
|
||||
// set calibration
|
||||
sam.ADC.CALIB.Set((bias << 8) | linearity)
|
||||
}
|
||||
|
||||
// Configure configures a ADC pin to be able to be used to read data.
|
||||
func (a ADC) Configure(config ADCConfig) {
|
||||
|
||||
// Wait for synchronization
|
||||
waitADCSync()
|
||||
|
||||
var resolution uint32
|
||||
switch config.Resolution {
|
||||
case 8:
|
||||
resolution = sam.ADC_CTRLB_RESSEL_8BIT
|
||||
case 10:
|
||||
resolution = sam.ADC_CTRLB_RESSEL_10BIT
|
||||
case 12:
|
||||
resolution = sam.ADC_CTRLB_RESSEL_12BIT
|
||||
case 16:
|
||||
resolution = sam.ADC_CTRLB_RESSEL_16BIT
|
||||
default:
|
||||
resolution = sam.ADC_CTRLB_RESSEL_12BIT
|
||||
}
|
||||
// Divide Clock by 32 with 12 bits resolution as default
|
||||
sam.ADC.CTRLB.Set((sam.ADC_CTRLB_PRESCALER_DIV32 << sam.ADC_CTRLB_PRESCALER_Pos) |
|
||||
(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos))
|
||||
uint16(resolution<<sam.ADC_CTRLB_RESSEL_Pos))
|
||||
|
||||
// Sampling Time Length
|
||||
sam.ADC.SAMPCTRL.Set(5)
|
||||
@@ -325,18 +343,44 @@ func InitADC() {
|
||||
sam.ADC.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
|
||||
|
||||
// Averaging (see datasheet table in AVGCTRL register description)
|
||||
sam.ADC.AVGCTRL.Set((sam.ADC_AVGCTRL_SAMPLENUM_1 << sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
|
||||
var samples uint32
|
||||
switch config.Samples {
|
||||
case 1:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_1
|
||||
case 2:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_2
|
||||
case 4:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_4
|
||||
case 8:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_8
|
||||
case 16:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_16
|
||||
case 32:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_32
|
||||
case 64:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_64
|
||||
case 128:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_128
|
||||
case 256:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_256
|
||||
case 512:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_512
|
||||
case 1024:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_1024
|
||||
default:
|
||||
samples = sam.ADC_AVGCTRL_SAMPLENUM_1
|
||||
}
|
||||
sam.ADC.AVGCTRL.Set(uint8(samples<<sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
|
||||
(0x0 << sam.ADC_AVGCTRL_ADJRES_Pos))
|
||||
|
||||
// TODO: use config.Reference to set AREF level
|
||||
|
||||
// Analog Reference is AREF pin (3.3v)
|
||||
sam.ADC.INPUTCTRL.SetBits(sam.ADC_INPUTCTRL_GAIN_DIV2 << sam.ADC_INPUTCTRL_GAIN_Pos)
|
||||
|
||||
// 1/2 VDDANA = 0.5 * 3V3 = 1.65V
|
||||
sam.ADC.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1 << sam.ADC_REFCTRL_REFSEL_Pos)
|
||||
}
|
||||
|
||||
// Configure configures a ADCPin to be able to be used to read data.
|
||||
func (a ADC) Configure() {
|
||||
a.Pin.Configure(PinConfig{Mode: PinAnalog})
|
||||
return
|
||||
}
|
||||
@@ -1421,36 +1465,104 @@ func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
Buffer *RingBuffer
|
||||
TxIdx volatile.Register8
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
}
|
||||
|
||||
const (
|
||||
usbcdcTxSizeMask uint8 = 0x3F
|
||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
||||
usbcdcTxBank1st uint8 = 0x00
|
||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
||||
)
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() error {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
sz := idx & usbcdcTxSizeMask
|
||||
bk := idx & usbcdcTxBankMask
|
||||
if 0 < sz {
|
||||
|
||||
if usbcdc.waitTxc {
|
||||
// waiting for the next flush(), because the transmission is not complete
|
||||
return nil
|
||||
}
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
|
||||
// set the data
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
|
||||
if bk == usbcdcTxBank1st {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
||||
} else {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
||||
}
|
||||
|
||||
// clean multi packet size of bytes already sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set count of bytes to be sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
UART0.sent = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc USBCDC) WriteByte(c byte) error {
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
// Supposedly to handle problem with Windows USB serial ports?
|
||||
if usbLineInfo.lineState > 0 {
|
||||
// set the data
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c
|
||||
ok := false
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN]))))
|
||||
idx := UART0.TxIdx.Get()
|
||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
||||
UART0.TxIdx.Set(idx + 1)
|
||||
ok = true
|
||||
}
|
||||
|
||||
// clean multi packet size of bytes already sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
interrupt.Restore(mask)
|
||||
|
||||
// set count of bytes to be sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((1 & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errUSBCDCWriteByteTimeout
|
||||
if ok {
|
||||
break
|
||||
} else if usbcdcTxMaxRetriesAllowed < UART0.waitTxcRetryCount {
|
||||
mask := interrupt.Disable()
|
||||
UART0.waitTxc = false
|
||||
UART0.waitTxcRetryCount = 0
|
||||
usbcdc.TxIdx.Set(0)
|
||||
usbLineInfo.lineState = 0
|
||||
interrupt.Restore(mask)
|
||||
break
|
||||
} else {
|
||||
mask := interrupt.Disable()
|
||||
if UART0.sent {
|
||||
if UART0.waitTxc {
|
||||
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) != 0 {
|
||||
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
UART0.waitTxc = false
|
||||
UART0.Flush()
|
||||
}
|
||||
} else {
|
||||
UART0.Flush()
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1657,9 +1769,19 @@ func handleUSB(intr interrupt.Interrupt) {
|
||||
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
||||
setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
if i == usb_CDC_ENDPOINT_IN {
|
||||
UART0.waitTxc = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
|
||||
UART0.waitTxcRetryCount++
|
||||
}
|
||||
}
|
||||
|
||||
UART0.Flush()
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user