Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem f50758ca5a interp: support GEP on fixed (MMIO) addresses
GetElementPtr would not work on values that weren't pointers. Because
fixed addresses (often used in memory-mapped I/O) are integers rather
than pointers in interp, it would return an error.

This resulted in the teensy40 target not compiling correctly since the
interp package rewrite. This commit should fix that.
2021-03-05 21:24:28 +01:00
208 changed files with 2369 additions and 6896 deletions
+29 -24
View File
@@ -80,12 +80,12 @@ commands:
steps: steps:
- restore_cache: - restore_cache:
keys: keys:
- wasi-libc-sysroot-v4 - wasi-libc-sysroot-v3
- run: - run:
name: "Build wasi-libc" name: "Build wasi-libc"
command: make wasi-libc command: make wasi-libc
- save_cache: - save_cache:
key: wasi-libc-sysroot-v4 key: wasi-libc-sysroot-v3
paths: paths:
- lib/wasi-libc/sysroot - lib/wasi-libc/sysroot
test-linux: test-linux:
@@ -108,13 +108,13 @@ commands:
- run: go install -tags=llvm<<parameters.llvm>> . - run: go install -tags=llvm<<parameters.llvm>> .
- restore_cache: - restore_cache:
keys: keys:
- wasi-libc-sysroot-systemclang-v3 - wasi-libc-sysroot-systemclang-v2
- run: make wasi-libc - run: make wasi-libc
- save_cache: - save_cache:
key: wasi-libc-sysroot-systemclang-v3 key: wasi-libc-sysroot-systemclang-v2
paths: paths:
- lib/wasi-libc/sysroot - lib/wasi-libc/sysroot
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform . - run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
- run: make gen-device -j4 - run: make gen-device -j4
- run: make smoketest XTENSA=0 - run: make smoketest XTENSA=0
- run: make tinygo-test - run: make tinygo-test
@@ -132,7 +132,6 @@ commands:
- run: - run:
name: "Install apt dependencies" name: "Install apt dependencies"
command: | command: |
sudo apt-get update
sudo apt-get install \ sudo apt-get install \
gcc-arm-linux-gnueabihf \ gcc-arm-linux-gnueabihf \
libc6-dev-armel-cross \ libc6-dev-armel-cross \
@@ -154,7 +153,7 @@ commands:
- llvm-source-linux - llvm-source-linux
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-linux-v2-assert - llvm-build-11-linux-v1-assert
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
@@ -169,7 +168,7 @@ commands:
make ASSERT=1 llvm-build make ASSERT=1 llvm-build
fi fi
- save_cache: - save_cache:
key: llvm-build-11-linux-v2-assert key: llvm-build-11-linux-v1-assert
paths: paths:
llvm-build llvm-build
- run: make ASSERT=1 - run: make ASSERT=1
@@ -191,7 +190,6 @@ commands:
- run: - run:
name: "Install apt dependencies" name: "Install apt dependencies"
command: | command: |
sudo apt-get update
sudo apt-get install \ sudo apt-get install \
gcc-arm-linux-gnueabihf \ gcc-arm-linux-gnueabihf \
libc6-dev-armel-cross \ libc6-dev-armel-cross \
@@ -213,7 +211,7 @@ commands:
- llvm-source-linux - llvm-source-linux
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-linux-v2-noassert - llvm-build-11-linux-v1-noassert
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
@@ -228,7 +226,7 @@ commands:
make llvm-build make llvm-build
fi fi
- save_cache: - save_cache:
key: llvm-build-11-linux-v2-noassert key: llvm-build-11-linux-v1-noassert
paths: paths:
llvm-build llvm-build
- build-wasi-libc - build-wasi-libc
@@ -270,8 +268,8 @@ commands:
- run: - run:
name: "Install dependencies" name: "Install dependencies"
command: | command: |
curl https://dl.google.com/go/go1.16.darwin-amd64.tar.gz -o go1.16.darwin-amd64.tar.gz 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.16.darwin-amd64.tar.gz sudo tar -C /usr/local -xzf go1.15.5.darwin-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go ln -s /usr/local/go/bin/go /usr/local/bin/go
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
- install-xtensa-toolchain: - install-xtensa-toolchain:
@@ -292,7 +290,7 @@ commands:
- llvm-project - llvm-project
- restore_cache: - restore_cache:
keys: keys:
- llvm-build-11-macos-v2 - llvm-build-11-macos-v1
- run: - run:
name: "Build LLVM" name: "Build LLVM"
command: | command: |
@@ -304,17 +302,17 @@ commands:
make llvm-build make llvm-build
fi fi
- save_cache: - save_cache:
key: llvm-build-11-macos-v2 key: llvm-build-11-macos-v1
paths: paths:
llvm-build llvm-build
- restore_cache: - restore_cache:
keys: keys:
- wasi-libc-sysroot-macos-v3 - wasi-libc-sysroot-macos-v2
- run: - run:
name: "Build wasi-libc" name: "Build wasi-libc"
command: make wasi-libc command: make wasi-libc
- save_cache: - save_cache:
key: wasi-libc-sysroot-macos-v3 key: wasi-libc-sysroot-macos-v2
paths: paths:
- lib/wasi-libc/sysroot - lib/wasi-libc/sysroot
- run: - run:
@@ -342,6 +340,18 @@ commands:
- /go/pkg/mod - /go/pkg/mod
jobs: 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: test-llvm10-go113:
docker: docker:
- image: circleci/golang:1.13-buster - image: circleci/golang:1.13-buster
@@ -360,12 +370,6 @@ jobs:
steps: steps:
- test-linux: - test-linux:
llvm: "11" llvm: "11"
test-llvm11-go116:
docker:
- image: circleci/golang:1.16-buster
steps:
- test-linux:
llvm: "11"
assert-test-linux: assert-test-linux:
docker: docker:
- image: circleci/golang:1.14-stretch - image: circleci/golang:1.14-stretch
@@ -387,10 +391,11 @@ jobs:
workflows: workflows:
test-all: test-all:
jobs: jobs:
- test-llvm9-go111
- test-llvm10-go112
- test-llvm10-go113 - test-llvm10-go113
- test-llvm10-go114 - test-llvm10-go114
- test-llvm11-go115 - test-llvm11-go115
- test-llvm11-go116
- build-linux - build-linux
- build-macos - build-macos
- assert-test-linux - assert-test-linux
-3
View File
@@ -20,6 +20,3 @@
[submodule "lib/picolibc"] [submodule "lib/picolibc"]
path = lib/picolibc path = lib/picolibc
url = https://github.com/keith-packard/picolibc.git url = https://github.com/keith-packard/picolibc.git
[submodule "lib/stm32-svd"]
path = lib/stm32-svd
url = https://github.com/tinygo-org/stm32-svd
+6 -1
View File
@@ -23,7 +23,7 @@ different guide:
LLVM, Clang and LLD are quite light on dependencies, requiring only standard LLVM, Clang and LLD are quite light on dependencies, requiring only standard
build tools to be built. Go is of course necessary to build TinyGo itself. build tools to be built. Go is of course necessary to build TinyGo itself.
* Go (1.13+) * Go (1.11+)
* Standard build tools (gcc/clang) * Standard build tools (gcc/clang)
* git * git
* CMake * CMake
@@ -43,6 +43,11 @@ 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 `LLVM_BUILDDIR`, `CLANG_SRC` and `LLD_SRC` make variables, but that is not
covered by this guide. 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 ## Build LLVM, Clang, LLD
Before starting the build, you may want to set the following environment Before starting the build, you may want to set the following environment
-90
View File
@@ -1,93 +1,3 @@
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 0.16.0
--- ---
+6 -6
View File
@@ -1,10 +1,10 @@
# TinyGo base stage installs the most recent Go 1.15.x, LLVM 11 and the TinyGo compiler itself. # TinyGo base stage installs the most recent Go 1.15.x, LLVM 10 and the TinyGo compiler itself.
FROM golang:1.15 AS tinygo-base FROM golang:1.15 AS tinygo-base
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \ RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \ echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
apt-get update && \ apt-get update && \
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git apt-get install -y llvm-10-dev libclang-10-dev lld-10 git
COPY . /tinygo COPY . /tinygo
@@ -29,7 +29,7 @@ COPY --from=tinygo-base /tinygo/targets /tinygo/targets
RUN cd /tinygo/ && \ RUN cd /tinygo/ && \
apt-get update && \ apt-get update && \
apt-get install -y make clang-11 libllvm11 lld-11 && \ apt-get install -y make clang-10 libllvm10 lld-10 && \
make wasi-libc make wasi-libc
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers. # 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/ && \ RUN cd /tinygo/ && \
apt-get update && \ apt-get update && \
apt-get install -y apt-utils make clang-11 && \ apt-get install -y apt-utils make clang-10 && \
make gen-device-nrf && make gen-device-stm32 make gen-device-nrf && make gen-device-stm32
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms. # 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/ && \ RUN cd /tinygo/ && \
apt-get update && \ apt-get update && \
apt-get install -y apt-utils make clang-11 binutils-avr gcc-avr avr-libc && \ apt-get install -y apt-utils make clang-10 binutils-avr gcc-avr avr-libc && \
make gen-device make gen-device
CMD ["tinygo"] CMD ["tinygo"]
+2 -2
View File
@@ -1,7 +1,7 @@
Copyright (c) 2018-2021 TinyGo Authors. All rights reserved. Copyright (c) 2018-2020 TinyGo Authors. All rights reserved.
TinyGo includes portions of the Go standard library. TinyGo includes portions of the Go standard library.
Copyright (c) 2009-2021 The Go Authors. All rights reserved. Copyright (c) 2009-2020 The Go Authors. All rights reserved.
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with 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. LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
+12 -34
View File
@@ -54,53 +54,39 @@ ifeq ($(OS),Windows_NT)
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++ CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
CGO_LDFLAGS_EXTRA += -lversion CGO_LDFLAGS_EXTRA += -lversion
LIBCLANG_NAME = libclang LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/liblibclang.a
else ifeq ($(shell uname -s),Darwin) else ifeq ($(shell uname -s),Darwin)
MD5SUM = md5 MD5SUM = md5
LIBCLANG_NAME = clang LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
else ifeq ($(shell uname -s),FreeBSD) else ifeq ($(shell uname -s),FreeBSD)
MD5SUM = md5 MD5SUM = md5
LIBCLANG_NAME = clang LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
START_GROUP = -Wl,--start-group START_GROUP = -Wl,--start-group
END_GROUP = -Wl,--end-group END_GROUP = -Wl,--end-group
else else
LIBCLANG_NAME = clang LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
START_GROUP = -Wl,--start-group START_GROUP = -Wl,--start-group
END_GROUP = -Wl,--end-group END_GROUP = -Wl,--end-group
endif endif
# Libraries that should be linked in for the statically linked Clang. 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++
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++
# Libraries that should be linked in for the statically linked LLD. LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML $(END_GROUP)
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. # For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","") 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_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CXXFLAGS=-std=c++14 CGO_CXXFLAGS=-std=c++14
CGO_LDFLAGS+=$(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) 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)
endif endif
clean: clean:
@rm -rf build @rm -rf build
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_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: fmt:
@gofmt -l -w $(FMT_PATHS) @gofmt -l -w $(FMT_PATHS)
fmt-check: fmt-check:
@@ -144,7 +130,7 @@ gen-device-kendryte: build/gen-device-svd
GO111MODULE=off $(GO) fmt ./src/device/kendryte GO111MODULE=off $(GO) fmt ./src/device/kendryte
gen-device-stm32: build/gen-device-svd gen-device-stm32: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd lib/stm32-svd/svd src/device/stm32/ ./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro lib/cmsis-svd/data/STMicro/ src/device/stm32/
GO111MODULE=off $(GO) fmt ./src/device/stm32 GO111MODULE=off $(GO) fmt ./src/device/stm32
@@ -156,11 +142,11 @@ llvm-source: $(LLVM_PROJECTDIR)/README.md
# Configure LLVM. # Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd) TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: llvm-source $(LLVM_BUILDDIR)/build.ninja: llvm-source
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(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_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
# Build LLVM. # Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
cd $(LLVM_BUILDDIR); ninja $(NINJA_BUILD_TARGETS) cd $(LLVM_BUILDDIR); ninja
# Build wasi-libc sysroot # Build wasi-libc sysroot
@@ -177,7 +163,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`" . 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 test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform . CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./interp ./transform .
# Test known-working standard library packages. # Test known-working standard library packages.
# TODO: do this in one command, parallelize, and only show failing tests (no # TODO: do this in one command, parallelize, and only show failing tests (no
@@ -223,8 +209,6 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink $(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
@$(MD5SUM) test.hex @$(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 $(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial $(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@@ -311,8 +295,6 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
@$(MD5SUM) test.hex @$(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 $(TINYGO) build -size short -o test.hex -target=x9pro examples/blinky1
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1 $(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
@@ -341,10 +323,6 @@ smoketest:
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1 $(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
@$(MD5SUM) test.hex @$(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 # test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
+4 -15
View File
@@ -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. You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 55 microcontroller boards are currently supported: The following 44 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333) * [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333) * [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
* [Adafruit CLUE](https://www.adafruit.com/product/4500) * [Adafruit CLUE Alpha](https://www.adafruit.com/product/4500)
* [Adafruit Feather M0](https://www.adafruit.com/product/2772) * [Adafruit Feather M0](https://www.adafruit.com/product/2772)
* [Adafruit Feather M4](https://www.adafruit.com/product/3857) * [Adafruit Feather M4](https://www.adafruit.com/product/3857)
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062) * [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
@@ -55,29 +55,22 @@ The following 55 microcontroller boards are currently supported:
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727) * [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800) * [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481) * [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 Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
* [Adafruit PyBadge](https://www.adafruit.com/product/4200) * [Adafruit PyBadge](https://www.adafruit.com/product/4200)
* [Adafruit PyGamer](https://www.adafruit.com/product/4242) * [Adafruit PyGamer](https://www.adafruit.com/product/4242)
* [Adafruit PyPortal](https://www.adafruit.com/product/4116) * [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) * [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3) * [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 Nano](https://store.arduino.cc/arduino-nano)
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot) * [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3) * [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero) * [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
* [BBC micro:bit](https://microbit.org/) * [BBC micro:bit](https://microbit.org/)
* [BBC micro:bit v2](https://microbit.org/new-microbit/)
* [Digispark](http://digistump.com/products/1) * [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) * [ESP32](https://www.espressif.com/en/products/socs/esp32)
* [ESP8266](https://www.espressif.com/en/products/socs/esp8266) * [ESP8266](https://www.espressif.com/en/products/socs/esp8266)
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance) * [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/) * [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/) * [Nintendo Switch](https://www.nintendo.com/switch/)
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle) * [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) * [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
@@ -88,16 +81,12 @@ The following 55 microcontroller boards are currently supported:
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/) * [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
* [PineTime DevKit](https://www.pine64.org/pinetime/) * [PineTime DevKit](https://www.pine64.org/pinetime/)
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html) * [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 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 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) * [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) * [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html) * [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html) * [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
* [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) * [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/) * [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
+8 -4
View File
@@ -12,7 +12,7 @@ jobs:
steps: steps:
- task: GoTool@0 - task: GoTool@0
inputs: inputs:
version: '1.16' version: '1.15'
- checkout: self - checkout: self
fetchDepth: 1 fetchDepth: 1
- task: Cache@2 - task: Cache@2
@@ -24,11 +24,15 @@ jobs:
displayName: Download LLVM source displayName: Download LLVM source
inputs: inputs:
targetType: inline targetType: inline
script: make llvm-source 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
- task: CacheBeta@0 - task: CacheBeta@0
displayName: Cache LLVM build displayName: Cache LLVM build
inputs: inputs:
key: llvm-build-11-windows-v4 key: llvm-build-11-windows-v3
path: llvm-build path: llvm-build
- task: Bash@3 - task: Bash@3
displayName: Build LLVM displayName: Build LLVM
@@ -52,7 +56,7 @@ jobs:
- task: CacheBeta@0 - task: CacheBeta@0
displayName: Cache wasi-libc sysroot displayName: Cache wasi-libc sysroot
inputs: inputs:
key: wasi-libc-sysroot-v4 key: wasi-libc-sysroot-v3
path: lib/wasi-libc/sysroot path: lib/wasi-libc/sysroot
- task: Bash@3 - task: Bash@3
displayName: Build wasi-libc displayName: Build wasi-libc
+102 -253
View File
@@ -8,7 +8,6 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"go/types"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@@ -20,7 +19,6 @@ import (
"github.com/tinygo-org/tinygo/compiler" "github.com/tinygo-org/tinygo/compiler"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp" "github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/loader"
"github.com/tinygo-org/tinygo/stacksize" "github.com/tinygo-org/tinygo/stacksize"
"github.com/tinygo-org/tinygo/transform" "github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
@@ -44,88 +42,101 @@ type BuildResult struct {
// //
// The error value may be of type *MultiError. Callers will likely want to check // The error value may be of type *MultiError. Callers will likely want to check
// for this case and print such errors individually. // for this case and print such errors individually.
func Build(pkgName, outpath string, config *compileopts.Config, preAction func() error, action func(BuildResult) error) error { func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
compilerConfig := &compiler.Config{ // Compile Go code to IR.
Triple: config.Triple(), machine, err := compiler.NewTargetMachine(config)
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 { if err != nil {
return err return err
} }
buildOutput, errs := compiler.Compile(pkgName, machine, config)
if errs != nil {
return newMultiError(errs)
}
mod := buildOutput.Mod
// Load entire program AST into memory. if config.Options.PrintIR {
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{ fmt.Println("; Generated LLVM IR:")
Sizes: compiler.Sizes(machine), fmt.Println(mod.String())
}) }
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after IR construction")
}
err = interp.Run(mod, config.DumpSSA())
if err != nil { if err != nil {
return err return err
} }
err = lprogram.Parse() if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after interpreting runtime.initAll")
}
if config.GOOS() != "darwin" {
transform.ApplyFunctionSections(mod) // -ffunction-sections
}
// Browsers cannot handle external functions that have type i64 because it
// cannot be represented exactly in JavaScript (JS only has doubles). To
// keep functions interoperable, pass int64 types as pointers to
// stack-allocated values.
// Use -wasm-abi=generic to disable this behaviour.
if config.WasmAbi() == "js" {
err := transform.ExternalInt64AsPtr(mod)
if err != nil { if err != nil {
return err 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. // Optimization levels here are roughly the same as Clang, but probably not
// TODO: parallelize this. // exactly.
var mod llvm.Module errs = nil
var stackSizeLoads []string switch config.Options.Opt {
programJob := &compileJob{ /*
description: "compile Go files", Currently, turning optimizations off causes compile failures.
run: func() (err error) { We rely on the optimizer removing some dead symbols.
mod, err = compileWholeProgram(pkgName, config, compilerConfig, lprogram, machine) Avoid providing an option that does not work right now.
if err != nil { In the future once everything has been fixed we can re-enable this.
return
case "none", "0":
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
*/
case "1":
errs = transform.Optimize(mod, config, 1, 0, 0) // -O1
case "2":
errs = transform.Optimize(mod, config, 2, 0, 225) // -O2
case "s":
errs = transform.Optimize(mod, config, 2, 1, 225) // -Os
case "z":
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
default:
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
} }
if len(errs) > 0 {
return newMultiError(errs)
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification failure after LLVM optimization passes")
}
// LLVM 11 by default tries to emit tail calls (even with the target feature
// disabled) unless it is explicitly disabled with a function attribute.
// This is a problem, as it tries to emit them and prints an error when it
// can't with this feature disabled.
// Because as of september 2020 tail calls are not yet widely supported,
// they need to be disabled until they are widely supported (at which point
// the +tail-call target feautre can be set).
if strings.HasPrefix(config.Triple(), "wasm") {
transform.DisableTailCalls(mod)
}
// Make sure stack sizes are loaded from a separate section so they can be // Make sure stack sizes are loaded from a separate section so they can be
// modified after linking. // modified after linking.
var stackSizeLoads []string
if config.AutomaticStackSize() { if config.AutomaticStackSize() {
stackSizeLoads = transform.CreateStackSizeLoads(mod, config) 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. // Generate output.
outext := filepath.Ext(outpath)
switch outext { switch outext {
case ".o": case ".o":
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile) llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
@@ -140,13 +151,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
data := []byte(mod.String()) data := []byte(mod.String())
return ioutil.WriteFile(outpath, data, 0666) return ioutil.WriteFile(outpath, data, 0666)
default: default:
panic("unreachable") // Act as a compiler driver.
}
}
// 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. // Create a temporary directory for intermediary files.
dir, err := ioutil.TempDir("", "tinygo") dir, err := ioutil.TempDir("", "tinygo")
@@ -155,127 +160,68 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
} }
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
// Add job to write the output object file. // Write the object file.
objfile := filepath.Join(dir, "main.o") 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) llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil { if err != nil {
return err return err
} }
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666) err = ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
}, if err != nil {
return err
} }
jobs = append(jobs, outputObjectFileJob)
// Prepare link command. // Prepare link command.
linkerDependencies := []*compileJob{outputObjectFileJob}
executable := filepath.Join(dir, "main") executable := filepath.Join(dir, "main")
tmppath := executable // final file tmppath := executable // final file
ldflags := append(config.LDFlags(), "-o", executable, objfile) ldflags := append(config.LDFlags(), "-o", executable, objfile)
// Add compiler-rt dependency if needed. Usually this is a simple load from // Load builtins library from the cache, possibly compiling it on the
// a cache. // fly.
if config.Target.RTLib == "compiler-rt" { if config.Target.RTLib == "compiler-rt" {
path, job, err := CompilerRT.load(config.Triple(), config.CPU(), dir) librt, err := CompilerRT.Load(config.Triple())
if err != nil { if err != nil {
return err return err
} }
if job != nil { ldflags = append(ldflags, librt)
// 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. // 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") 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() { for i, path := range config.ExtraFiles() {
abspath := filepath.Join(root, path) abspath := filepath.Join(root, path)
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o") 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)...) err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
if err != nil { if err != nil {
return &commandError{"failed to build", path, err} return &commandError{"failed to build", path, err}
} }
return nil
},
}
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
ldflags = append(ldflags, outpath) ldflags = append(ldflags, outpath)
} }
// Add jobs to compile C files in all packages. This is part of CGo. // Compile C files in packages.
// TODO: do this as part of building the package to be able to link the for i, file := range buildOutput.ExtraFiles {
// bitcode files together. outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
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)...) err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
if err != nil { if err != nil {
return &commandError{"failed to build", file, err} return &commandError{"failed to build", file, err}
} }
return nil
},
}
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
ldflags = append(ldflags, outpath) ldflags = append(ldflags, outpath)
} }
if len(buildOutput.ExtraLDFlags) > 0 {
ldflags = append(ldflags, buildOutput.ExtraLDFlags...)
} }
// Linker flags from CGo lines: // Link the object files together.
// #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...) err = link(config.Target.Linker, ldflags...)
if err != nil { if err != nil {
return &commandError{"failed to link", executable, err} return &commandError{"failed to link", executable, err}
@@ -325,18 +271,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
printStacks(calculatedStacks, stackSizes) 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. // Get an Intel .hex file or .bin file from the .elf file.
outputBinaryFormat := config.BinaryFormat(outext) outputBinaryFormat := config.BinaryFormat(outext)
switch outputBinaryFormat { switch outputBinaryFormat {
@@ -370,94 +304,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, preAction func()
} }
return action(BuildResult{ return action(BuildResult{
Binary: tmppath, Binary: tmppath,
MainDir: lprogram.MainPkg().Dir, MainDir: buildOutput.MainDir,
}) })
}
// 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)
} }
if config.Options.PrintIR {
fmt.Println("; Generated LLVM IR:")
fmt.Println(mod.String())
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return mod, errors.New("verification error after IR construction")
}
err := interp.Run(mod, config.DumpSSA())
if err != nil {
return mod, err
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return mod, errors.New("verification error after interpreting runtime.initAll")
}
if config.GOOS() != "darwin" {
transform.ApplyFunctionSections(mod) // -ffunction-sections
}
// Browsers cannot handle external functions that have type i64 because it
// cannot be represented exactly in JavaScript (JS only has doubles). To
// keep functions interoperable, pass int64 types as pointers to
// stack-allocated values.
// Use -wasm-abi=generic to disable this behaviour.
if config.WasmAbi() == "js" {
err := transform.ExternalInt64AsPtr(mod)
if err != nil {
return mod, err
}
}
// Optimization levels here are roughly the same as Clang, but probably not
// exactly.
errs = nil
switch config.Options.Opt {
/*
Currently, turning optimizations off causes compile failures.
We rely on the optimizer removing some dead symbols.
Avoid providing an option that does not work right now.
In the future once everything has been fixed we can re-enable this.
case "none", "0":
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
*/
case "1":
errs = transform.Optimize(mod, config, 1, 0, 0) // -O1
case "2":
errs = transform.Optimize(mod, config, 2, 0, 225) // -O2
case "s":
errs = transform.Optimize(mod, config, 2, 1, 225) // -Os
case "z":
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
default:
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
}
if len(errs) > 0 {
return mod, newMultiError(errs)
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return mod, errors.New("verification failure after LLVM optimization passes")
}
// LLVM 11 by default tries to emit tail calls (even with the target feature
// disabled) unless it is explicitly disabled with a function attribute.
// This is a problem, as it tries to emit them and prints an error when it
// can't with this feature disabled.
// Because as of september 2020 tail calls are not yet widely supported,
// they need to be disabled until they are widely supported (at which point
// the +tail-call target feautre can be set).
if strings.HasPrefix(config.Triple(), "wasm") {
transform.DisableTailCalls(mod)
}
return mod, nil
} }
// functionStackSizes keeps stack size information about a single function // functionStackSizes keeps stack size information about a single function
+19 -6
View File
@@ -30,7 +30,10 @@ func cacheTimestamp(paths []string) (time.Time, error) {
// Try to load a given file from the cache. Return "", nil if no cached file can // 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 // be found (or the file is stale), return the absolute path if there is a cache
// and return an error on I/O errors. // and return an error on I/O errors.
func cacheLoad(name string, sourceFiles []string) (string, error) { //
// 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) {
cachepath := filepath.Join(goenv.Get("GOCACHE"), name) cachepath := filepath.Join(goenv.Get("GOCACHE"), name)
cacheStat, err := os.Stat(cachepath) cacheStat, err := os.Stat(cachepath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
@@ -55,7 +58,9 @@ func cacheLoad(name string, sourceFiles []string) (string, error) {
// Store the file located at tmppath in the cache with the given name. The // Store the file located at tmppath in the cache with the given name. The
// tmppath may or may not be gone afterwards. // tmppath may or may not be gone afterwards.
func cacheStore(tmppath, name string, sourceFiles []string) (string, error) { //
// Note: the configKey is ignored, see cacheLoad.
func cacheStore(tmppath, name, configKey string, sourceFiles []string) (string, error) {
// get the last modified time // get the last modified time
if len(sourceFiles) == 0 { if len(sourceFiles) == 0 {
panic("cache: no source files") panic("cache: no source files")
@@ -69,16 +74,24 @@ func cacheStore(tmppath, name string, sourceFiles []string) (string, error) {
return "", err return "", err
} }
cachepath := filepath.Join(dir, name) cachepath := filepath.Join(dir, name)
err = copyFile(tmppath, cachepath) err = moveFile(tmppath, cachepath)
if err != nil { if err != nil {
return "", err return "", err
} }
return cachepath, nil return cachepath, nil
} }
// copyFile copies the given file from src to dst. It can copy over // moveFile renames the file from src to dst. If renaming doesn't work (for
// a possibly already existing file at the destination. // example, the rename crosses a filesystem boundary), the file is copied and
func copyFile(src, dst string) error { // 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.
inf, err := os.Open(src) inf, err := os.Open(src)
if err != nil { if err != nil {
return err return err
+2 -2
View File
@@ -25,8 +25,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err) return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
} }
if major != 1 || minor < 13 || minor > 16 { if major != 1 || minor < 11 || minor > 15 {
return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor) return nil, fmt.Errorf("requires go version 1.11 through 1.15, got go%d.%d", major, minor)
} }
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
return &compileopts.Config{ return &compileopts.Config{
-160
View File
@@ -1,160 +0,0 @@
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
}
}
+27 -72
View File
@@ -1,6 +1,7 @@
package builder package builder
import ( import (
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -39,64 +40,34 @@ func (l *Library) sourcePaths(target string) []string {
} }
// Load the library archive, possibly generating and caching it if needed. // Load the library archive, possibly generating and caching it if needed.
// The resulting file is stored in the provided tmpdir, which is expected to be func (l *Library) Load(target string) (path string, err error) {
// 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. // Try to load a precompiled library.
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a") precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
if _, err := os.Stat(precompiledPath); err == nil { if _, err := os.Stat(precompiledPath); err == nil {
// Found a precompiled library for this OS/architecture. Return the path // Found a precompiled library for this OS/architecture. Return the path
// directly. // directly.
return precompiledPath, nil, nil return precompiledPath, nil
} }
var outfile string outfile := l.name + "-" + target + ".a"
if cpu != "" {
outfile = l.name + "-" + target + "-" + cpu + ".a"
} else {
outfile = l.name + "-" + target + ".a"
}
// Try to fetch this library from the cache. // Try to fetch this library from the cache.
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil { if path, err := cacheLoad(outfile, commands["clang"][0], l.sourcePaths(target)); path != "" || err != nil {
// Cache hit. // Cache hit.
return path, nil, err return path, err
} }
// Cache miss, build it now. // Cache miss, build it now.
remapDir := filepath.Join(os.TempDir(), "tinygo-"+l.name) dirPrefix := "tinygo-" + l.name
dir := filepath.Join(tmpdir, "build-lib-"+l.name) remapDir := filepath.Join(os.TempDir(), dirPrefix)
err = os.Mkdir(dir, 0777) dir, err := ioutil.TempDir(os.TempDir(), dirPrefix)
if err != nil { if err != nil {
return "", nil, err return "", err
} }
defer os.RemoveAll(dir)
// Precalculate the flags to the compiler invocation. // 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) 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") { if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft") args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft")
} }
@@ -107,44 +78,28 @@ func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob
args = append(args, "-march=rv64gc", "-mabi=lp64") args = append(args, "-march=rv64gc", "-mabi=lp64")
} }
// Create job to put all the object files in a single archive. This archive // Compile all sources.
// file is the (static) library file.
var objs []string 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) { for _, srcpath := range l.sourcePaths(target) {
srcpath := srcpath // avoid concurrency issues by redefining inside the loop
objpath := filepath.Join(dir, filepath.Base(srcpath)+".o") objpath := filepath.Join(dir, filepath.Base(srcpath)+".o")
objs = append(objs, objpath) objs = append(objs, objpath)
job.dependencies = append(job.dependencies, &compileJob{ // Note: -fdebug-prefix-map is necessary to make the output archive
description: "compile " + srcpath, // reproducible. Otherwise the temporary directory is stored in the
run: func() error { // archive itself, which varies each run.
var compileArgs []string err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...)
compileArgs = append(compileArgs, args...)
compileArgs = append(compileArgs, "-o", objpath, srcpath)
err := runCCompiler("clang", compileArgs...)
if err != nil { if err != nil {
return &commandError{"failed to build", srcpath, err} return "", &commandError{"failed to build", srcpath, err}
} }
return nil
},
})
} }
return arpath, job, 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))
} }
-11
View File
@@ -57,7 +57,6 @@ type functionInfo struct {
args []paramInfo args []paramInfo
results *ast.FieldList results *ast.FieldList
pos token.Pos pos token.Pos
variadic bool
} }
// paramInfo is a parameter of a CGo function (see functionInfo). // paramInfo is a parameter of a CGo function (see functionInfo).
@@ -485,16 +484,6 @@ func (p *cgoPackage) addFuncDecls() {
Results: fn.results, Results: fn.results,
}, },
} }
if fn.variadic {
decl.Doc = &ast.CommentGroup{
List: []*ast.Comment{
&ast.Comment{
Slash: fn.pos,
Text: "//go:variadic",
},
},
}
}
obj.Decl = decl obj.Decl = decl
for i, arg := range fn.args { for i, arg := range fn.args {
args[i] = &ast.Field{ args[i] = &ast.Field{
+3 -18
View File
@@ -5,7 +5,6 @@ import (
"flag" "flag"
"fmt" "fmt"
"go/ast" "go/ast"
"go/build"
"go/format" "go/format"
"go/parser" "go/parser"
"go/token" "go/token"
@@ -24,7 +23,7 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
// normalizeResult normalizes Go source code that comes out of tests across // normalizeResult normalizes Go source code that comes out of tests across
// platforms and Go versions. // platforms and Go versions.
func normalizeResult(result string) string { func normalizeResult(result string) string {
actual := strings.ReplaceAll(result, "\r\n", "\n") actual := strings.Replace(result, "\r\n", "\n", -1)
// Make sure all functions are wrapped, even those that would otherwise be // 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 // single-line functions. This is necessary because Go 1.14 changed the way
@@ -42,20 +41,6 @@ func TestCGo(t *testing.T) {
for _, name := range []string{"basic", "errors", "types", "flags", "const"} { for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
name := name // avoid a race condition name := name // avoid a race condition
t.Run(name, func(t *testing.T) { 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. // Read the AST in memory.
path := filepath.Join("testdata", name+".go") path := filepath.Join("testdata", name+".go")
fset := token.NewFileSet() fset := token.NewFileSet()
@@ -113,7 +98,7 @@ func TestCGo(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("could not read expected output: %v", err) t.Fatalf("could not read expected output: %v", err)
} }
expected := strings.ReplaceAll(string(expectedBytes), "\r\n", "\n") expected := strings.Replace(string(expectedBytes), "\r\n", "\n", -1)
// Check whether the output is as expected. // Check whether the output is as expected.
if expected != actual { if expected != actual {
@@ -154,7 +139,7 @@ func formatDiagnostic(err error) string {
msg := err.Error() msg := err.Error()
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
// Fix Windows path slashes. // Fix Windows path slashes.
msg = strings.ReplaceAll(msg, "testdata\\", "testdata/") msg = strings.Replace(msg, "testdata\\", "testdata/", -1)
} }
return "// " + msg + "\n" return "// " + msg + "\n"
} }
+3 -1
View File
@@ -152,10 +152,12 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
return C.CXChildVisit_Continue return C.CXChildVisit_Continue
} }
cursorType := C.tinygo_clang_getCursorType(c) 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)) numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
fn := &functionInfo{ fn := &functionInfo{
pos: pos, pos: pos,
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
} }
p.functions[name] = fn p.functions[name] = fn
for i := 0; i < numArgs; i++ { for i := 0; i < numArgs; i++ {
+7 -7
View File
@@ -1,14 +1,14 @@
// +build !byollvm // +build !byollvm
// +build !llvm10 // +build !llvm9,!llvm11
package cgo package cgo
/* /*
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include #cgo linux CFLAGS: -I/usr/lib/llvm-10/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include #cgo darwin CFLAGS: -I/usr/local/opt/llvm@10/include
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include #cgo freebsd CFLAGS: -I/usr/local/llvm10/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang #cgo linux LDFLAGS: -L/usr/lib/llvm-10/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi #cgo darwin LDFLAGS: -L/usr/local/opt/llvm@10/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang #cgo freebsd LDFLAGS: -L/usr/local/llvm10/lib -lclang
*/ */
import "C" import "C"
-14
View File
@@ -1,14 +0,0 @@
// +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"
+14
View File
@@ -0,0 +1,14 @@
// +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"
+14
View File
@@ -0,0 +1,14 @@
// +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"
+2 -2
View File
@@ -4,10 +4,10 @@
// testdata/errors.go:13:23: unexpected token ) // testdata/errors.go:13:23: unexpected token )
// Type checking errors after CGo processing: // Type checking errors after CGo processing:
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows) // testdata/errors.go:102: 2 << 10 (untyped int constant 2048) overflows uint8
// testdata/errors.go:105: unknown field z in struct literal // testdata/errors.go:105: unknown field z in struct literal
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1 // testdata/errors.go:108: undeclared name: C.SOME_CONST_1
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows) // testdata/errors.go:110: C.SOME_CONST_3 (untyped int constant 1234) overflows byte
package main package main
-10
View File
@@ -104,10 +104,6 @@ typedef struct {
unsigned char e : 3; unsigned char e : 3;
// Note that C++ allows bitfields bigger than the underlying type. // Note that C++ allows bitfields bigger than the underlying type.
} bitfield_t; } bitfield_t;
// Function signatures.
void variadic0();
void variadic2(int x, int y, ...);
*/ */
import "C" import "C"
@@ -167,9 +163,3 @@ func accessUnion() {
var _ *C.int = union2d.unionfield_i() var _ *C.int = union2d.unionfield_i()
var _ *[2]float64 = union2d.unionfield_d() var _ *[2]float64 = union2d.unionfield_d()
} }
// Test function signatures.
func accessFunctions() {
C.variadic0()
C.variadic2(3, 5)
}
-5
View File
@@ -4,11 +4,6 @@ import "unsafe"
var _ unsafe.Pointer 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.option2A = 20
const C.optionA = 0 const C.optionA = 0
const C.optionB = 1 const C.optionB = 1
+38 -21
View File
@@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
@@ -21,6 +22,29 @@ type Config struct {
TestConfig TestConfig 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. // Triple returns the LLVM target triple, like armv6m-none-eabi.
func (c *Config) Triple() string { func (c *Config) Triple() string {
return c.Target.Triple return c.Target.Triple
@@ -120,24 +144,14 @@ func (c *Config) Scheduler() string {
// FuncImplementation picks an appropriate func value implementation for the // FuncImplementation picks an appropriate func value implementation for the
// target. // target.
func (c *Config) FuncImplementation() string { 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.
switch c.Scheduler() { switch c.Scheduler() {
case "tasks":
// 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": case "none", "coroutines":
// As "doubleword", but with the function pointer replaced by a unique return FuncValueSwitch
// ID per function signature. Function values are called by using a case "tasks":
// switch statement and choosing which function to call. return FuncValueDoubleword
// 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: default:
panic("unknown scheduler type") panic("unknown scheduler type")
} }
@@ -165,7 +179,7 @@ func (c *Config) AutomaticStackSize() bool {
func (c *Config) CFlags() []string { func (c *Config) CFlags() []string {
cflags := append([]string{}, c.Options.CFlags...) cflags := append([]string{}, c.Options.CFlags...)
for _, flag := range c.Target.CFlags { for _, flag := range c.Target.CFlags {
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT"))) cflags = append(cflags, strings.Replace(flag, "{root}", goenv.Get("TINYGOROOT"), -1))
} }
if c.Target.Libc == "picolibc" { if c.Target.Libc == "picolibc" {
root := goenv.Get("TINYGOROOT") root := goenv.Get("TINYGOROOT")
@@ -186,9 +200,15 @@ func (c *Config) LDFlags() []string {
// Merge and adjust LDFlags. // Merge and adjust LDFlags.
ldflags := append([]string{}, c.Options.LDFlags...) ldflags := append([]string{}, c.Options.LDFlags...)
for _, flag := range c.Target.LDFlags { for _, flag := range c.Target.LDFlags {
ldflags = append(ldflags, strings.ReplaceAll(flag, "{root}", root)) ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
} }
ldflags = append(ldflags, "-L", 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 != "" { if c.Target.LinkerScript != "" {
ldflags = append(ldflags, "-T", c.Target.LinkerScript) ldflags = append(ldflags, "-T", c.Target.LinkerScript)
} }
@@ -283,9 +303,6 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
return nil, fmt.Errorf("unknown OpenOCD transport: %#v", c.Target.OpenOCDTransport) return nil, fmt.Errorf("unknown OpenOCD transport: %#v", c.Target.OpenOCDTransport)
} }
args = []string{"-f", "interface/" + openocdInterface + ".cfg"} args = []string{"-f", "interface/" + openocdInterface + ".cfg"}
for _, cmd := range c.Target.OpenOCDCommands {
args = append(args, "-c", cmd)
}
if c.Target.OpenOCDTransport != "" { if c.Target.OpenOCDTransport != "" {
args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport) args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
} }
+1 -1
View File
@@ -23,7 +23,6 @@ type Options struct {
PrintIR bool PrintIR bool
DumpSSA bool DumpSSA bool
VerifyIR bool VerifyIR bool
PrintCommands bool
Debug bool Debug bool
PrintSizes string PrintSizes string
PrintStacks bool PrintStacks bool
@@ -31,6 +30,7 @@ type Options struct {
LDFlags []string LDFlags []string
Tags string Tags string
WasmAbi string WasmAbi string
HeapSize int64
TestConfig TestConfig TestConfig TestConfig
Programmer string Programmer string
} }
-1
View File
@@ -52,7 +52,6 @@ type TargetSpec struct {
OpenOCDInterface string `json:"openocd-interface"` OpenOCDInterface string `json:"openocd-interface"`
OpenOCDTarget string `json:"openocd-target"` OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"` OpenOCDTransport string `json:"openocd-transport"`
OpenOCDCommands []string `json:"openocd-commands"`
JLinkDevice string `json:"jlink-device"` JLinkDevice string `json:"jlink-device"`
CodeModel string `json:"code-model"` CodeModel string `json:"code-model"`
RelocationModel string `json:"relocation-model"` RelocationModel string `json:"relocation-model"`
+6 -6
View File
@@ -16,7 +16,7 @@ import (
// slice. This is required by the Go language spec: an index out of bounds must // slice. This is required by the Go language spec: an index out of bounds must
// cause a panic. // cause a panic.
func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) { func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) {
if b.info.nobounds { if b.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds // The //go:nobounds pragma was added to the function to avoid bounds
// checking. // checking.
return 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 // biggest possible slice capacity, 'low' means len and 'high' means cap. The
// logic is the same in both cases. // logic is the same in both cases.
func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) { func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lowType, highType, maxType *types.Basic) {
if b.info.nobounds { if b.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds // The //go:nobounds pragma was added to the function to avoid bounds
// checking. // checking.
return 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 // createChanBoundsCheck creates a bounds check before creating a new channel to
// check that the value is not too big for runtime.chanMake. // 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) { func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
if b.info.nobounds { if b.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds // The //go:nobounds pragma was added to the function to avoid bounds
// checking. // checking.
return 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. // createNegativeShiftCheck creates an assertion that panics if the given shift value is negative.
// This function assumes that the shift value is signed. // This function assumes that the shift value is signed.
func (b *builder) createNegativeShiftCheck(shift llvm.Value) { func (b *builder) createNegativeShiftCheck(shift llvm.Value) {
if b.info.nobounds { if b.fn.IsNoBounds() {
// Function disabled bounds checking - skip shift check. // Function disabled bounds checking - skip shift check.
return return
} }
@@ -212,8 +212,8 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
} }
} }
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw") faultBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".throw")
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".next") nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, blockPrefix+".next")
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Now branch to the out-of-bounds or the regular block. // Now branch to the out-of-bounds or the regular block.
-27
View File
@@ -1,8 +1,6 @@
package compiler package compiler
import ( import (
"strings"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -37,31 +35,6 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
ptr := b.getValue(call.Args[0]) ptr := b.getValue(call.Args[0])
old := b.getValue(call.Args[1]) old := b.getValue(call.Args[1])
newVal := b.getValue(call.Args[2]) newVal := b.getValue(call.Args[2])
if strings.HasSuffix(name, "64") {
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) tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
swapped := b.CreateExtractValue(tuple, 1, "") swapped := b.CreateExtractValue(tuple, 1, "")
return swapped, true return swapped, true
+5 -6
View File
@@ -4,7 +4,6 @@ import (
"go/types" "go/types"
"strconv" "strconv"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -35,14 +34,14 @@ const (
// createCall creates a new call to runtime.<fnName> with the given arguments. // 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 { func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function) fullName := "runtime." + fnName
llvmFn := b.getFunction(fn) fn := b.mod.NamedFunction(fullName)
if llvmFn.IsNil() { if fn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil)) panic("trying to call non-existent function: " + fullName)
} }
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
args = append(args, llvm.ConstPointerNull(b.i8ptrType)) // coroutine handle args = append(args, llvm.ConstPointerNull(b.i8ptrType)) // coroutine handle
return b.createCall(llvmFn, args, name) return b.createCall(fn, args, name)
} }
// createCall creates a call to the given function with the arguments possibly // createCall creates a call to the given function with the arguments possibly
+2 -1
View File
@@ -79,7 +79,8 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
} }
// createChanClose closes the given channel. // createChanClose closes the given channel.
func (b *builder) createChanClose(ch llvm.Value) { func (b *builder) createChanClose(param ssa.Value) {
ch := b.getValue(param)
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "") b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
} }
+382 -385
View File
File diff suppressed because it is too large Load Diff
-172
View File
@@ -1,172 +0,0 @@
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
View File
@@ -14,9 +14,9 @@ package compiler
// frames. // frames.
import ( import (
"go/types"
"github.com/tinygo-org/tinygo/compiler/llvmutil" "github.com/tinygo-org/tinygo/compiler/llvmutil"
"github.com/tinygo-org/tinygo/ir"
"go/types"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -26,9 +26,9 @@ import (
// calls. // calls.
func (b *builder) deferInitFunc() { func (b *builder) deferInitFunc() {
// Some setup. // Some setup.
b.deferFuncs = make(map[*ssa.Function]int) b.deferFuncs = make(map[*ir.Function]int)
b.deferInvokeFuncs = make(map[string]int) b.deferInvokeFuncs = make(map[string]int)
b.deferClosureFuncs = make(map[*ssa.Function]int) b.deferClosureFuncs = make(map[*ir.Function]int)
b.deferExprFuncs = make(map[ssa.Value]int) b.deferExprFuncs = make(map[ssa.Value]int)
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin) b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
@@ -107,11 +107,13 @@ func (b *builder) createDefer(instr *ssa.Defer) {
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok { } else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
// Regular function call. // Regular function call.
if _, ok := b.deferFuncs[callee]; !ok { fn := b.ir.GetFunction(callee)
b.deferFuncs[callee] = len(b.allDeferFuncs)
b.allDeferFuncs = append(b.allDeferFuncs, callee) if _, ok := b.deferFuncs[fn]; !ok {
b.deferFuncs[fn] = len(b.allDeferFuncs)
b.allDeferFuncs = append(b.allDeferFuncs, fn)
} }
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[callee]), false) callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[fn]), false)
// Collect all values to be put in the struct (starting with // Collect all values to be put in the struct (starting with
// runtime._defer fields). // runtime._defer fields).
@@ -133,7 +135,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
context := b.CreateExtractValue(closure, 0, "") context := b.CreateExtractValue(closure, 0, "")
// Get the callback number. // Get the callback number.
fn := makeClosure.Fn.(*ssa.Function) fn := b.ir.GetFunction(makeClosure.Fn.(*ssa.Function))
if _, ok := b.deferClosureFuncs[fn]; !ok { if _, ok := b.deferClosureFuncs[fn]; !ok {
b.deferClosureFuncs[fn] = len(b.allDeferFuncs) b.deferClosureFuncs[fn] = len(b.allDeferFuncs)
b.allDeferFuncs = append(b.allDeferFuncs, makeClosure) b.allDeferFuncs = append(b.allDeferFuncs, makeClosure)
@@ -153,19 +155,19 @@ func (b *builder) createDefer(instr *ssa.Defer) {
valueTypes = append(valueTypes, context.Type()) valueTypes = append(valueTypes, context.Type())
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
var argTypes []types.Type var funcName string
var argValues []llvm.Value switch builtin.Name() {
for _, arg := range instr.Call.Args { case "close":
argTypes = append(argTypes, arg.Type()) funcName = "chanClose"
argValues = append(argValues, b.getValue(arg)) default:
b.addError(instr.Pos(), "todo: Implement defer for "+builtin.Name())
return
} }
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok { if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{ b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
callName: builtin.Name(), funcName,
pos: builtin.Pos(), len(b.allDeferFuncs),
argTypes: argTypes,
callback: len(b.allDeferFuncs),
} }
b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value) b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
} }
@@ -174,9 +176,10 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// Collect all values to be put in the struct (starting with // Collect all values to be put in the struct (starting with
// runtime._defer fields). // runtime._defer fields).
values = []llvm.Value{callback, next} values = []llvm.Value{callback, next}
for _, param := range argValues { for _, param := range instr.Call.Args {
values = append(values, param) llvmParam := b.getValue(param)
valueTypes = append(valueTypes, param.Type()) values = append(values, llvmParam)
valueTypes = append(valueTypes, llvmParam.Type())
} }
} else { } else {
@@ -220,7 +223,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call") allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc") alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
} }
if b.NeedsStackObjects { if b.NeedsStackObjects() {
b.trackPointer(alloca) b.trackPointer(alloca)
} }
b.CreateStore(deferFrame, alloca) b.CreateStore(deferFrame, alloca)
@@ -248,10 +251,10 @@ func (b *builder) createRunDefers() {
// } // }
// Create loop. // Create loop.
loophead := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loophead") loophead := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loophead")
loop := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.loop") loop := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loop")
unreachable := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.default") unreachable := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.default")
end := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.end") end := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.end")
b.CreateBr(loophead) b.CreateBr(loophead)
// Create loop head: // Create loop head:
@@ -283,7 +286,7 @@ func (b *builder) createRunDefers() {
// Create switch case, for example: // Create switch case, for example:
// case 0: // case 0:
// // run first deferred call // // run first deferred call
block := b.ctx.AddBasicBlock(b.llvmFn, "rundefers.callback") block := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.callback")
sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block) sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
b.SetInsertPointAtEnd(block) b.SetInsertPointAtEnd(block)
switch callback := callback.(type) { switch callback := callback.(type) {
@@ -347,7 +350,7 @@ func (b *builder) createRunDefers() {
b.createCall(fnPtr, forwardParams, "") b.createCall(fnPtr, forwardParams, "")
case *ssa.Function: case *ir.Function:
// Direct call. // Direct call.
// Get the real defer struct type and cast to it. // Get the real defer struct type and cast to it.
@@ -369,7 +372,7 @@ func (b *builder) createRunDefers() {
// Plain TinyGo functions add some extra parameters to implement async functionality and function recievers. // 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. // These parameters should not be supplied when calling into an external C/ASM function.
if !b.getFunctionInfo(callback).exported { if !callback.IsExported() {
// Add the context parameter. We know it is ignored by the receiving // Add the context parameter. We know it is ignored by the receiving
// function, but we have to pass one anyway. // function, but we have to pass one anyway.
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
@@ -379,11 +382,11 @@ func (b *builder) createRunDefers() {
} }
// Call real function. // Call real function.
b.createCall(b.getFunction(callback), forwardParams, "") b.createCall(callback.LLVMFn, forwardParams, "")
case *ssa.MakeClosure: case *ssa.MakeClosure:
// Get the real defer struct type and cast to it. // Get the real defer struct type and cast to it.
fn := callback.Fn.(*ssa.Function) fn := b.ir.GetFunction(callback.Fn.(*ssa.Function))
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
params := fn.Signature.Params() params := fn.Signature.Params()
for i := 0; i < params.Len(); i++ { for i := 0; i < params.Len(); i++ {
@@ -406,7 +409,7 @@ func (b *builder) createRunDefers() {
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
// Call deferred function. // Call deferred function.
b.createCall(b.getFunction(fn), forwardParams, "") b.createCall(fn.LLVMFn, forwardParams, "")
case *ssa.Builtin: case *ssa.Builtin:
db := b.deferBuiltinFuncs[callback] db := b.deferBuiltinFuncs[callback]
@@ -423,18 +426,15 @@ func (b *builder) createRunDefers() {
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
// Extract the params from the struct. // Extract the params from the struct.
var argValues []llvm.Value var forwardParams []llvm.Value
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
for i := 0; i < params.Len(); i++ { for i := 0; i < params.Len(); i++ {
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
forwardParam := b.CreateLoad(gep, "param") forwardParam := b.CreateLoad(gep, "param")
argValues = append(argValues, forwardParam) forwardParams = append(forwardParams, forwardParam)
} }
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos) b.createRuntimeCall(db.funcName, forwardParams, "")
if err != nil {
b.diagnostics = append(b.diagnostics, err)
}
default: default:
panic("unknown deferred function type") panic("unknown deferred function type")
} }
+1 -1
View File
@@ -14,7 +14,7 @@ import (
// makeError makes it easy to create an error from a token.Pos with a message. // 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 { func (c *compilerContext) makeError(pos token.Pos, msg string) types.Error {
return types.Error{ return types.Error{
Fset: c.program.Fset, Fset: c.ir.Program.Fset,
Pos: pos, Pos: pos,
Msg: msg, Msg: msg,
} }
+12 -11
View File
@@ -6,6 +6,7 @@ package compiler
import ( import (
"go/types" "go/types"
"github.com/tinygo-org/tinygo/compileopts"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -20,11 +21,11 @@ func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signat
// context. // context.
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
var funcValueScalar llvm.Value var funcValueScalar llvm.Value
switch c.FuncImplementation { switch c.FuncImplementation() {
case "doubleword": case compileopts.FuncValueDoubleword:
// Closure is: {context, function pointer} // Closure is: {context, function pointer}
funcValueScalar = funcPtr funcValueScalar = funcPtr
case "switch": case compileopts.FuncValueSwitch:
sigGlobal := c.getTypeCode(sig) sigGlobal := c.getTypeCode(sig)
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature" funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName) funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
@@ -66,10 +67,10 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
// value. This may be an expensive operation. // value. This may be an expensive operation.
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) { func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
context = b.CreateExtractValue(funcValue, 0, "") context = b.CreateExtractValue(funcValue, 0, "")
switch b.FuncImplementation { switch b.FuncImplementation() {
case "doubleword": case compileopts.FuncValueDoubleword:
funcPtr = b.CreateExtractValue(funcValue, 1, "") funcPtr = b.CreateExtractValue(funcValue, 1, "")
case "switch": case compileopts.FuncValueSwitch:
llvmSig := b.getRawFuncType(sig) llvmSig := b.getRawFuncType(sig)
sigGlobal := b.getTypeCode(sig) sigGlobal := b.getTypeCode(sig)
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
@@ -82,11 +83,11 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
// getFuncType returns the type of a func value given a signature. // getFuncType returns the type of a func value given a signature.
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type { func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
switch c.FuncImplementation { switch c.FuncImplementation() {
case "doubleword": case compileopts.FuncValueDoubleword:
rawPtr := c.getRawFuncType(typ) rawPtr := c.getRawFuncType(typ)
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false) return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
case "switch": case compileopts.FuncValueSwitch:
return c.getLLVMRuntimeType("funcValue") return c.getLLVMRuntimeType("funcValue")
default: default:
panic("unimplemented func value variant") panic("unimplemented func value variant")
@@ -148,7 +149,7 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
if len(expr.Bindings) == 0 { if len(expr.Bindings) == 0 {
panic("unexpected: MakeClosure without bound variables") panic("unexpected: MakeClosure without bound variables")
} }
f := expr.Fn.(*ssa.Function) f := b.ir.GetFunction(expr.Fn.(*ssa.Function))
// Collect all bound variables. // Collect all bound variables.
boundVars := make([]llvm.Value, len(expr.Bindings)) boundVars := make([]llvm.Value, len(expr.Bindings))
@@ -163,5 +164,5 @@ func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
context := b.emitPointerPack(boundVars) context := b.emitPointerPack(boundVars)
// Create the closure. // Create the closure.
return b.createFuncValue(b.getFunction(f), context, f.Signature), nil return b.createFuncValue(f.LLVMFn, context, f.Signature), nil
} }
+9 -12
View File
@@ -7,7 +7,6 @@ import (
"go/token" "go/token"
"github.com/tinygo-org/tinygo/compiler/llvmutil" "github.com/tinygo-org/tinygo/compiler/llvmutil"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -21,20 +20,19 @@ import (
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value { func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
paramBundle := b.emitPointerPack(params) paramBundle := b.emitPointerPack(params)
var callee, stackSize llvm.Value var callee, stackSize llvm.Value
switch b.Scheduler { switch b.Scheduler() {
case "none", "tasks": case "none", "tasks":
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos) callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
if b.AutomaticStackSize { if b.AutomaticStackSize() {
// The stack size is not known until after linking. Call a dummy // The stack size is not known until after linking. Call a dummy
// function that will be replaced with a load from a special ELF // function that will be replaced with a load from a special ELF
// section that contains the stack size (and is modified after // section that contains the stack size (and is modified after
// linking). // linking).
stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function)) stackSize = b.createCall(b.mod.NamedFunction("internal/task.getGoroutineStackSize"), []llvm.Value{callee, llvm.Undef(b.i8ptrType), llvm.Undef(b.i8ptrType)}, "stacksize")
stackSize = b.createCall(stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType), llvm.Undef(b.i8ptrType)}, "stacksize")
} else { } else {
// The stack size is fixed at compile time. By emitting it here as a // The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized. // constant, it can be optimized.
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false) stackSize = llvm.ConstInt(b.uintptrType, b.Target.DefaultStackSize, false)
} }
case "coroutines": case "coroutines":
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "") callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
@@ -44,8 +42,7 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
default: default:
panic("unreachable") panic("unreachable")
} }
start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function)) b.createCall(b.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
b.createCall(start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
return llvm.Undef(funcPtr.Type().ElementType().ReturnType()) return llvm.Undef(funcPtr.Type().ElementType().ReturnType())
} }
@@ -90,8 +87,8 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
entry := c.ctx.AddBasicBlock(wrapper, "entry") entry := c.ctx.AddBasicBlock(wrapper, "entry")
builder.SetInsertPointAtEnd(entry) builder.SetInsertPointAtEnd(entry)
if c.Debug { if c.Debug() {
pos := c.program.Fset.Position(pos) pos := c.ir.Program.Fset.Position(pos)
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{ diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
File: c.getDIFile(pos.Filename), File: c.getDIFile(pos.Filename),
Parameters: nil, // do not show parameters in debugger Parameters: nil, // do not show parameters in debugger
@@ -147,8 +144,8 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
entry := c.ctx.AddBasicBlock(wrapper, "entry") entry := c.ctx.AddBasicBlock(wrapper, "entry")
builder.SetInsertPointAtEnd(entry) builder.SetInsertPointAtEnd(entry)
if c.Debug { if c.Debug() {
pos := c.program.Fset.Position(pos) pos := c.ir.Program.Fset.Position(pos)
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{ diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
File: c.getDIFile(pos.Filename), File: c.getDIFile(pos.Filename),
Parameters: nil, // do not show parameters in debugger Parameters: nil, // do not show parameters in debugger
+21 -68
View File
@@ -11,6 +11,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/ir"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -235,7 +236,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
return llvm.ConstGEP(global, []llvm.Value{zero, zero}) return llvm.ConstGEP(global, []llvm.Value{zero, zero})
} }
ms := c.program.MethodSets.MethodSet(typ) ms := c.ir.Program.MethodSets.MethodSet(typ)
if ms.Len() == 0 { if ms.Len() == 0 {
// no methods, so can leave that one out // no methods, so can leave that one out
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0)) return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
@@ -246,16 +247,15 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
for i := 0; i < ms.Len(); i++ { for i := 0; i < ms.Len(); i++ {
method := ms.At(i) method := ms.At(i)
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func)) signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
fn := c.program.MethodValue(method) f := c.ir.GetFunction(c.ir.Program.MethodValue(method))
llvmFn := c.getFunction(fn) if f.LLVMFn.IsNil() {
if llvmFn.IsNil() {
// compiler error, so panic // compiler error, so panic
panic("cannot find function: " + c.getFunctionInfo(fn).linkName) panic("cannot find function: " + f.LinkName())
} }
wrapper := c.getInterfaceInvokeWrapper(fn, llvmFn) fn := c.getInterfaceInvokeWrapper(f)
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{ methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
signatureGlobal, signatureGlobal,
llvm.ConstPtrToInt(wrapper, c.uintptrType), llvm.ConstPtrToInt(fn, c.uintptrType),
}) })
methods[i] = methodInfo 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 // external *i8 indicating the indicating the signature of this method. It is
// used during the interface lowering pass. // used during the interface lowering pass.
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value { func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
signature := methodSignature(method) signature := ir.MethodSignature(method)
signatureGlobal := c.mod.NamedGlobal("func " + signature) signatureGlobal := c.mod.NamedGlobal("func " + signature)
if signatureGlobal.IsNil() { if signatureGlobal.IsNil() {
signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature) signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature)
@@ -357,8 +357,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
// value. // value.
prevBlock := b.GetInsertBlock() prevBlock := b.GetInsertBlock()
okBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.ok") okBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.ok")
nextBlock := b.ctx.AddBasicBlock(b.llvmFn, "typeassert.next") nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.next")
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
b.CreateCondBr(commaOk, okBlock, nextBlock) 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. // 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 // If the method to wrap has a pointer receiver, no wrapping is necessary and
// the function is returned directly. // the function is returned directly.
func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llvm.Value) llvm.Value { func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
wrapperName := llvmFn.Name() + "$invoke" wrapperName := f.LinkName() + "$invoke"
wrapper := c.mod.NamedFunction(wrapperName) wrapper := c.mod.NamedFunction(wrapperName)
if !wrapper.IsNil() { if !wrapper.IsNil() {
// Wrapper already created. Return it directly. // Wrapper already created. Return it directly.
@@ -445,7 +445,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
} }
// Get the expanded receiver type. // Get the expanded receiver type.
receiverType := c.getLLVMType(fn.Params[0].Type()) receiverType := c.getLLVMType(f.Params[0].Type())
var expandedReceiverType []llvm.Type var expandedReceiverType []llvm.Type
for _, info := range expandFormalParamType(receiverType, "", nil) { for _, info := range expandFormalParamType(receiverType, "", nil) {
expandedReceiverType = append(expandedReceiverType, info.llvmType) expandedReceiverType = append(expandedReceiverType, info.llvmType)
@@ -457,11 +457,11 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
// Casting a function signature to a different signature and calling it // Casting a function signature to a different signature and calling it
// with a receiver pointer bitcasted to *i8 (as done in calls on an // with a receiver pointer bitcasted to *i8 (as done in calls on an
// interface) is hopefully a safe (defined) operation. // interface) is hopefully a safe (defined) operation.
return llvmFn return f.LLVMFn
} }
// create wrapper function // create wrapper function
fnType := llvmFn.Type().ElementType() fnType := f.LLVMFn.Type().ElementType()
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...) paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false) wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType) wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
@@ -478,9 +478,9 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
defer b.Builder.Dispose() defer b.Builder.Dispose()
// add debug info if needed // add debug info if needed
if c.Debug { if c.Debug() {
pos := c.program.Fset.Position(fn.Pos()) pos := c.ir.Program.Fset.Position(f.Pos())
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line) difunc := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line)
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
} }
@@ -490,60 +490,13 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0] receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...) params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
if llvmFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind { if f.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
b.CreateCall(llvmFn, params, "") b.CreateCall(f.LLVMFn, params, "")
b.CreateRetVoid() b.CreateRetVoid()
} else { } else {
ret := b.CreateCall(llvmFn, params, "ret") ret := b.CreateCall(f.LLVMFn, params, "ret")
b.CreateRet(ret) b.CreateRet(ret)
} }
return wrapper 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
}
+4 -4
View File
@@ -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 // Create a new global of type runtime/interrupt.handle. Globals of this
// type are lowered in the interrupt lowering pass. // type are lowered in the interrupt lowering pass.
globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type() globalType := b.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
globalLLVMType := b.getLLVMType(globalType) globalLLVMType := b.getLLVMType(globalType)
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10) globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
if global := b.mod.NamedGlobal(globalName); !global.IsNil() { 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) global.SetInitializer(initializer)
// Add debug info to the interrupt global. // Add debug info to the interrupt global.
if b.Debug { if b.Debug() {
pos := b.program.Fset.Position(instr.Pos()) pos := b.ir.Program.Fset.Position(instr.Pos())
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{ diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10), Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
LinkageName: globalName, 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 // PLIC where each interrupt must be enabled using the interrupt number, and
// thus keeps the Interrupt object alive. // thus keeps the Interrupt object alive.
// This call is removed during interrupt lowering. // 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") useFn := b.mod.NamedFunction("runtime/interrupt.use")
if useFn.IsNil() { if useFn.IsNil() {
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false) useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
+1 -1
View File
@@ -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 // 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. // pointer value directly. It returns the pointer with the packed data.
func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value { func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.NeedsStackObjects, values) return llvmutil.EmitPointerPack(b.Builder, b.mod, b.Config, values)
} }
// emitPointerUnpack extracts a list of values packed using emitPointerPack. // emitPointerUnpack extracts a list of values packed using emitPointerPack.
+3 -2
View File
@@ -5,6 +5,7 @@ package llvmutil
// itself if possible and legal. // itself if possible and legal.
import ( import (
"github.com/tinygo-org/tinygo/compileopts"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@@ -12,7 +13,7 @@ import (
// bitcasts, or else allocates a value on the heap if it cannot be packed in the // 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. // 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. // If the values are all constants, they are be stored in a constant global and deduplicated.
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bool, values []llvm.Value) llvm.Value { func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.Config, values []llvm.Value) llvm.Value {
ctx := mod.Context() ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout()) targetData := llvm.NewTargetData(mod.DataLayout())
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
@@ -100,7 +101,7 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, needsStackObjects bo
llvm.Undef(i8ptrType), // unused context parameter llvm.Undef(i8ptrType), // unused context parameter
llvm.ConstPointerNull(i8ptrType), // coroutine handle llvm.ConstPointerNull(i8ptrType), // coroutine handle
}, "") }, "")
if needsStackObjects { if config.NeedsStackObjects() {
trackPointer := mod.NamedFunction("runtime.trackPointer") trackPointer := mod.NamedFunction("runtime.trackPointer")
builder.CreateCall(trackPointer, []llvm.Value{ builder.CreateCall(trackPointer, []llvm.Value{
packedHeapAlloc, packedHeapAlloc,
-164
View File
@@ -1,164 +0,0 @@
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
}
+2 -285
View File
@@ -15,269 +15,6 @@ import (
"tinygo.org/x/go-llvm" "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&paramIsDeferenceableOrNull == 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, // 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 // 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). // some symbols this is different (due to //go:extern for example).
@@ -345,11 +82,11 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
} }
} }
if c.Debug && !info.extern { if c.Debug() && !info.extern {
// Add debug info. // Add debug info.
// TODO: this should be done for every global in the program, not just // TODO: this should be done for every global in the program, not just
// the ones that are referenced from some code. // the ones that are referenced from some code.
pos := c.program.Fset.Position(g.Pos()) pos := c.ir.Program.Fset.Position(g.Pos())
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{ diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
Name: g.RelString(nil), Name: g.RelString(nil),
LinkageName: info.linkName, LinkageName: info.linkName,
@@ -408,23 +145,3 @@ 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
View File
@@ -16,8 +16,8 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
num := b.getValue(call.Args[0]) num := b.getValue(call.Args[0])
var syscallResult llvm.Value var syscallResult llvm.Value
switch { switch {
case b.GOARCH == "amd64": case b.GOARCH() == "amd64":
if b.GOOS == "darwin" { if b.GOOS() == "darwin" {
// Darwin adds this magic number to system call numbers: // Darwin adds this magic number to system call numbers:
// //
// > Syscall classes for 64-bit system call entry. // > 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) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
syscallResult = b.CreateCall(target, args, "") syscallResult = b.CreateCall(target, args, "")
case b.GOARCH == "386" && b.GOOS == "linux": case b.GOARCH() == "386" && b.GOOS() == "linux":
// Sources: // Sources:
// syscall(2) man page // syscall(2) man page
// https://stackoverflow.com/a/2538212 // 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) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel) target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
syscallResult = b.CreateCall(target, args, "") 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. // Implement the EABI system call convention for Linux.
// Source: syscall(2) man page. // Source: syscall(2) man page.
args := []llvm.Value{} args := []llvm.Value{}
@@ -116,7 +116,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, argTypes, false) fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
syscallResult = b.CreateCall(target, args, "") syscallResult = b.CreateCall(target, args, "")
case b.GOARCH == "arm64" && b.GOOS == "linux": case b.GOARCH() == "arm64" && b.GOOS() == "linux":
// Source: syscall(2) man page. // Source: syscall(2) man page.
args := []llvm.Value{} args := []llvm.Value{}
argTypes := []llvm.Type{} 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) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
syscallResult = b.CreateCall(target, args, "") syscallResult = b.CreateCall(target, args, "")
default: 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": case "linux", "freebsd":
// Return values: r0, r1 uintptr, err Errno // Return values: r0, r1 uintptr, err Errno
// Pseudocode: // Pseudocode:
@@ -187,6 +187,6 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
retval = b.CreateInsertValue(retval, errResult, 2, "") retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil return retval, nil
default: 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())
} }
} }
-57
View File
@@ -1,57 +0,0 @@
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)
-98
View File
@@ -1,98 +0,0 @@
; 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
}
-39
View File
@@ -1,39 +0,0 @@
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)
}
-80
View File
@@ -1,80 +0,0 @@
; 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
}
-41
View File
@@ -1,41 +0,0 @@
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))
}
-49
View File
@@ -1,49 +0,0 @@
; 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
}
-9
View File
@@ -1,9 +0,0 @@
package main
func sliceLen(ints []int) int {
return len(ints)
}
func sliceCap(ints []int) int {
return cap(ints)
}
-19
View File
@@ -1,19 +0,0 @@
; 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
}
+5 -7
View File
@@ -1,16 +1,14 @@
module github.com/tinygo-org/tinygo module github.com/tinygo-org/tinygo
go 1.13 go 1.11
require ( require (
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b
github.com/chromedp/chromedp v0.6.4 github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
github.com/mattn/go-colorable v0.1.8 go.bug.st/serial v1.0.0
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 golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9
) )
+19 -28
View File
@@ -1,39 +1,33 @@
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI= github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 h1:11eq/RkpaotwdF6b1TRMcdgQUPNmyFEJOB7zLvh0O/Y= github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693/go.mod h1:55pim6Ht4LJKdVLlyFJV/g++HsEA1hQxPbB5JyNdZC0= github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b h1:LF+GRwyzxrO3MUzPvejv+yBup0lNG+/QdIRrkxOPseA=
github.com/chromedp/chromedp v0.6.4 h1:Gx7ZkRyrSVmbbDDja/ieNgNGJIvElroPOyeqYQGVDSY= github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b/go.mod h1:E6LPWRdIJc11h/di5p0rwvRmUYbhGpBEH7ZbPfzDIOE=
github.com/chromedp/chromedp v0.6.4/go.mod h1:vodUdJf5dF/b8n0UBJv6NeM/QK28RjP3j+eM7fq4+84= github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e h1:Hv0JVyHhbIXb9NiYQe4NsrfgrSofAp0q2FnhhJOXgi8=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e/go.mod h1:vmQMRHFZrY3T+Jv51T0n87OK/i6bK+5P9a+Fg5jPwgQ=
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 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= 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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs= github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g=
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/ws v1.0.3/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 h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 h1:6J+qramlHVLmiBOgRiBOnQkno8uprqG6YFFQTt6uYIw= 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/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/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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q= 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
@@ -45,11 +39,8 @@ 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-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 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-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/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 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -57,5 +48,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= 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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f h1:FP5Do5omlQ/dLQ3Hfy7oyJo69VS5Hn46rZw004r0lGU= tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9 h1:l2kTQOhqEoeDTK3ckUnwReOQwMPUmURMIdjJbeAuDT4=
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE= tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
+2 -21
View File
@@ -142,11 +142,10 @@ func getHomeDir() string {
// getGoroot returns an appropriate GOROOT from various sources. If it can't be // getGoroot returns an appropriate GOROOT from various sources. If it can't be
// found, it returns an empty string. // found, it returns an empty string.
func getGoroot() string { func getGoroot() string {
// An explicitly set GOROOT always has preference.
goroot := os.Getenv("GOROOT") goroot := os.Getenv("GOROOT")
if goroot != "" { if goroot != "" {
// Convert to the standard GOROOT being referenced, if it's a TinyGo cache. // An explicitly set GOROOT always has preference.
return getStandardGoroot(goroot) return goroot
} }
// Check for the location of the 'go' binary and base GOROOT on that. // Check for the location of the 'go' binary and base GOROOT on that.
@@ -196,21 +195,3 @@ func isGoroot(goroot string) bool {
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go")) _, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
return err == nil 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
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo. // Version of TinyGo.
// Update this value before release of new version of software. // Update this value before release of new version of software.
const Version = "0.18.0-dev" const Version = "0.17.0-dev"
// GetGorootVersion returns the major and minor version for a given GOROOT path. // GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned. // If the goroot cannot be determined, (0, 0) is returned.
+271
View File
@@ -0,0 +1,271 @@
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
View File
@@ -0,0 +1,149 @@
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
}
Submodule lib/stm32-svd deleted from c6b5be976f
+6 -16
View File
@@ -16,6 +16,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/tinygo-org/tinygo/cgo" "github.com/tinygo-org/tinygo/cgo"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
@@ -42,7 +43,6 @@ type Program struct {
type PackageJSON struct { type PackageJSON struct {
Dir string Dir string
ImportPath string ImportPath string
Name string
ForTest string ForTest string
// Source files // Source files
@@ -52,7 +52,6 @@ type PackageJSON struct {
// Dependency information // Dependency information
Imports []string Imports []string
ImportMap map[string]string
// Error information // Error information
Error *struct { Error *struct {
@@ -109,7 +108,10 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok { if exitErr, ok := err.(*exec.ExitError); ok {
os.Exit(exitErr.ExitCode()) if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
os.Exit(1)
} }
return nil, fmt.Errorf("failed to run `go list`: %s", err) return nil, fmt.Errorf("failed to run `go list`: %s", err)
} }
@@ -333,16 +335,7 @@ func (p *Package) Check() error {
// Do typechecking of the package. // Do typechecking of the package.
checker.Importer = p checker.Importer = p
packageName := p.ImportPath typesPkg, err := checker.Check(p.ImportPath, p.program.fset, p.Files, &p.info)
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 != nil {
if err, ok := err.(Errors); ok { if err, ok := err.(Errors); ok {
return err return err
@@ -408,9 +401,6 @@ func (p *Package) Import(to string) (*types.Package, error) {
if to == "unsafe" { if to == "unsafe" {
return types.Unsafe, nil 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 { if imported, ok := p.program.Packages[to]; ok {
return imported.Pkg, nil return imported.Pkg, nil
} else { } else {
-8
View File
@@ -16,11 +16,3 @@ func (p *Program) LoadSSA() *ssa.Program {
return prog 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)
}
+78 -77
View File
@@ -14,17 +14,16 @@ import (
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync/atomic" "syscall"
"time" "time"
"github.com/mattn/go-colorable"
"github.com/tinygo-org/tinygo/builder" "github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp" "github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/loader" "github.com/tinygo-org/tinygo/loader"
"github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
"go.bug.st/serial" "go.bug.st/serial"
@@ -90,14 +89,6 @@ func copyFile(src, dst string) error {
return err 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. // Build compiles and links the given package and writes it to outpath.
func Build(pkgName, outpath string, options *compileopts.Options) error { func Build(pkgName, outpath string, options *compileopts.Options) error {
config, err := builder.NewConfig(options) config, err := builder.NewConfig(options)
@@ -105,7 +96,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
return err return err
} }
return builder.Build(pkgName, outpath, config, nil, func(result builder.BuildResult) error { return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
if err := os.Rename(result.Binary, outpath); err != nil { if err := os.Rename(result.Binary, outpath); err != nil {
// Moving failed. Do a file copy. // Moving failed. Do a file copy.
inf, err := os.Open(result.Binary) inf, err := os.Open(result.Binary)
@@ -141,7 +132,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
return err return err
} }
return builder.Build(pkgName, outpath, config, nil, func(result builder.BuildResult) error { return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
if testCompileOnly || outpath != "" { if testCompileOnly || outpath != "" {
// Write test binary to the specified file name. // Write test binary to the specified file name.
if outpath == "" { if outpath == "" {
@@ -157,7 +148,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
} }
if len(config.Target.Emulator) == 0 { if len(config.Target.Emulator) == 0 {
// Run directly. // Run directly.
cmd := executeCommand(config.Options, result.Binary) cmd := exec.Command(result.Binary)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Dir = result.MainDir cmd.Dir = result.MainDir
@@ -165,7 +156,10 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
if err != nil { if err != nil {
// Propagate the exit code // Propagate the exit code
if err, ok := err.(*exec.ExitError); ok { if err, ok := err.(*exec.ExitError); ok {
os.Exit(err.ExitCode()) if status, ok := err.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
os.Exit(1)
} }
return &commandError{"failed to run compiled binary", result.Binary, err} return &commandError{"failed to run compiled binary", result.Binary, err}
} }
@@ -173,7 +167,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
} else { } else {
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary) args := append(config.Target.Emulator[1:], result.Binary)
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...) cmd := exec.Command(config.Target.Emulator[0], args...)
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
w := io.MultiWriter(os.Stdout, buf) w := io.MultiWriter(os.Stdout, buf)
cmd.Stdout = w cmd.Stdout = w
@@ -237,7 +231,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return errors.New("unknown flash method: " + flashMethod) return errors.New("unknown flash method: " + flashMethod)
} }
return builder.Build(pkgName, fileExt, config, func() error { return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
// do we need port reset to put MCU into bootloader mode? // do we need port reset to put MCU into bootloader mode?
if config.Target.PortReset == "true" && flashMethod != "openocd" { if config.Target.PortReset == "true" && flashMethod != "openocd" {
if port == "" { if port == "" {
@@ -250,21 +244,19 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
err := touchSerialPortAt1200bps(port) err := touchSerialPortAt1200bps(port)
if err != nil { if err != nil {
return &commandError{"failed to reset port", "", err} return &commandError{"failed to reset port", result.Binary, err}
} }
// give the target MCU a chance to restart into bootloader // give the target MCU a chance to restart into bootloader
time.Sleep(3 * time.Second) 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) // this flashing method copies the binary data to a Mass Storage Device (msd)
switch flashMethod { switch flashMethod {
case "", "command": case "", "command":
// Create the command. // Create the command.
flashCmd := config.Target.FlashCommand flashCmd := config.Target.FlashCommand
fileToken := "{" + fileExt[1:] + "}" fileToken := "{" + fileExt[1:] + "}"
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary) flashCmd = strings.Replace(flashCmd, fileToken, result.Binary, -1)
if port == "" && strings.Contains(flashCmd, "{port}") { if port == "" && strings.Contains(flashCmd, "{port}") {
var err error var err error
@@ -274,7 +266,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
} }
} }
flashCmd = strings.ReplaceAll(flashCmd, "{port}", port) flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
// Execute the command. // Execute the command.
var cmd *exec.Cmd var cmd *exec.Cmd
@@ -284,9 +276,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
if len(command) < 2 { if len(command) < 2 {
return errors.New("invalid flash command") return errors.New("invalid flash command")
} }
cmd = executeCommand(config.Options, command[0], command[1:]...) cmd = exec.Command(command[0], command[1:]...)
default: default:
cmd = executeCommand(config.Options, "/bin/sh", "-c", flashCmd) cmd = exec.Command("/bin/sh", "-c", flashCmd)
} }
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@@ -300,13 +292,13 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
case "msd": case "msd":
switch fileExt { switch fileExt {
case ".uf2": case ".uf2":
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary, config.Options) err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary)
if err != nil { if err != nil {
return &commandError{"failed to flash", result.Binary, err} return &commandError{"failed to flash", result.Binary, err}
} }
return nil return nil
case ".hex": case ".hex":
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary, config.Options) err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary)
if err != nil { if err != nil {
return &commandError{"failed to flash", result.Binary, err} return &commandError{"failed to flash", result.Binary, err}
} }
@@ -320,7 +312,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return err return err
} }
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit") args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
cmd := executeCommand(config.Options, "openocd", args...) cmd := exec.Command("openocd", args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err = cmd.Run() err = cmd.Run()
@@ -350,7 +342,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
return errors.New("gdb not configured in the target specification") return errors.New("gdb not configured in the target specification")
} }
return builder.Build(pkgName, "", config, nil, func(result builder.BuildResult) error { return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
// Find a good way to run GDB. // Find a good way to run GDB.
gdbInterface, openocdInterface := config.Programmer() gdbInterface, openocdInterface := config.Programmer()
switch gdbInterface { switch gdbInterface {
@@ -389,11 +381,11 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
if err != nil { if err != nil {
return err return err
} }
daemon = executeCommand(config.Options, "openocd", args...) daemon = exec.Command("openocd", args...)
if ocdOutput { if ocdOutput {
// Make it clear which output is from the daemon. // Make it clear which output is from the daemon.
w := &ColorWriter{ w := &ColorWriter{
Out: colorable.NewColorableStderr(), Out: os.Stderr,
Prefix: "openocd: ", Prefix: "openocd: ",
Color: TermColorYellow, Color: TermColorYellow,
} }
@@ -404,11 +396,11 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt") gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt")
// We need a separate debugging daemon for on-chip debugging. // We need a separate debugging daemon for on-chip debugging.
daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice) daemon = exec.Command("JLinkGDBServer", "-device", config.Target.JLinkDevice)
if ocdOutput { if ocdOutput {
// Make it clear which output is from the daemon. // Make it clear which output is from the daemon.
w := &ColorWriter{ w := &ColorWriter{
Out: colorable.NewColorableStderr(), Out: os.Stderr,
Prefix: "jlink: ", Prefix: "jlink: ",
Color: TermColorYellow, Color: TermColorYellow,
} }
@@ -420,7 +412,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S") args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...) daemon = exec.Command(config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "qemu-user": case "qemu-user":
@@ -428,7 +420,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary) args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary)
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...) daemon = exec.Command(config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "mgba": case "mgba":
@@ -436,7 +428,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary, "-g") args := append(config.Target.Emulator[1:], result.Binary, "-g")
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...) daemon = exec.Command(config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "simavr": case "simavr":
@@ -444,7 +436,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], "-g", result.Binary) args := append(config.Target.Emulator[1:], "-g", result.Binary)
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...) daemon = exec.Command(config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr daemon.Stderr = os.Stderr
case "msd": case "msd":
@@ -465,15 +457,8 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
} }
defer func() { defer func() {
daemon.Process.Signal(os.Interrupt) daemon.Process.Signal(os.Interrupt)
var stopped uint32 // Maybe we should send a .Kill() after x seconds?
go func() {
time.Sleep(time.Millisecond * 100)
if atomic.LoadUint32(&stopped) == 0 {
daemon.Process.Kill()
}
}()
daemon.Wait() daemon.Wait()
atomic.StoreUint32(&stopped, 1)
}() }()
} }
@@ -492,7 +477,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
for _, cmd := range gdbCommands { for _, cmd := range gdbCommands {
params = append(params, "-ex", cmd) params = append(params, "-ex", cmd)
} }
cmd := executeCommand(config.Options, config.Target.GDB, params...) cmd := exec.Command(config.Target.GDB, params...)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -514,10 +499,10 @@ func Run(pkgName string, options *compileopts.Options) error {
return err return err
} }
return builder.Build(pkgName, ".elf", config, nil, func(result builder.BuildResult) error { return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
if len(config.Target.Emulator) == 0 { if len(config.Target.Emulator) == 0 {
// Run directly. // Run directly.
cmd := executeCommand(config.Options, result.Binary) cmd := exec.Command(result.Binary)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err := cmd.Run() err := cmd.Run()
@@ -532,7 +517,7 @@ func Run(pkgName string, options *compileopts.Options) error {
} else { } else {
// Run in an emulator. // Run in an emulator.
args := append(config.Target.Emulator[1:], result.Binary) args := append(config.Target.Emulator[1:], result.Binary)
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...) cmd := exec.Command(config.Target.Emulator[0], args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err := cmd.Run() err := cmd.Run()
@@ -575,7 +560,7 @@ func touchSerialPortAt1200bps(port string) (err error) {
const maxMSDRetries = 10 const maxMSDRetries = 10
func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) error { func flashUF2UsingMSD(volume, tmppath string) error {
// find standard UF2 info path // find standard UF2 info path
var infoPath string var infoPath string
switch runtime.GOOS { switch runtime.GOOS {
@@ -584,7 +569,7 @@ func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) erro
case "darwin": case "darwin":
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT" infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
case "windows": case "windows":
path, err := windowsFindUSBDrive(volume, options) path, err := windowsFindUSBDrive(volume)
if err != nil { if err != nil {
return err return err
} }
@@ -599,7 +584,7 @@ func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) erro
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2") return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
} }
func flashHexUsingMSD(volume, tmppath string, options *compileopts.Options) error { func flashHexUsingMSD(volume, tmppath string) error {
// find expected volume path // find expected volume path
var destPath string var destPath string
switch runtime.GOOS { switch runtime.GOOS {
@@ -608,7 +593,7 @@ func flashHexUsingMSD(volume, tmppath string, options *compileopts.Options) erro
case "darwin": case "darwin":
destPath = "/Volumes/" + volume destPath = "/Volumes/" + volume
case "windows": case "windows":
path, err := windowsFindUSBDrive(volume, options) path, err := windowsFindUSBDrive(volume)
if err != nil { if err != nil {
return err return err
} }
@@ -642,8 +627,8 @@ func locateDevice(volume, path string) (string, error) {
return d[0], nil return d[0], nil
} }
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) { func windowsFindUSBDrive(volume string) (string, error) {
cmd := executeCommand(options, "wmic", cmd := exec.Command("wmic",
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'", "PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
"get", "DeviceID,VolumeName,FileSystem,DriveType") "get", "DeviceID,VolumeName,FileSystem,DriveType")
@@ -665,6 +650,30 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e
return "", errors.New("unable to locate a USB device to be flashed") 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. // getDefaultPort returns the default serial port depending on the operating system.
func getDefaultPort() (port string, err error) { func getDefaultPort() (port string, err error) {
var portPath string var portPath string
@@ -775,15 +784,6 @@ func printCompilerError(logln func(...interface{}), err error) {
logln() 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: case loader.Errors:
logln("#", err.Pkg.ImportPath) logln("#", err.Pkg.ImportPath)
for _, err := range err.Errs { for _, err := range err.Errs {
@@ -832,7 +832,6 @@ func main() {
target := flag.String("target", "", "LLVM target | .json file with TargetSpec") target := flag.String("target", "", "LLVM target | .json file with TargetSpec")
printSize := flag.String("size", "", "print sizes (none, short, full)") printSize := flag.String("size", "", "print sizes (none, short, full)")
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines") 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") nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug") ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
port := flag.String("port", "", "flash port") port := flag.String("port", "", "flash port")
@@ -840,6 +839,7 @@ func main() {
cFlags := flag.String("cflags", "", "additional cflags for compiler") cFlags := flag.String("cflags", "", "additional cflags for compiler")
ldFlags := flag.String("ldflags", "", "additional ldflags for linker") ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic") 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 var flagJSON, flagDeps *bool
if command == "help" || command == "list" { if command == "help" || command == "list" {
@@ -880,7 +880,6 @@ func main() {
Debug: !*nodebug, Debug: !*nodebug,
PrintSizes: *printSize, PrintSizes: *printSize,
PrintStacks: *printStacks, PrintStacks: *printStacks,
PrintCommands: *printCommands,
Tags: *tags, Tags: *tags,
WasmAbi: *wasmAbi, WasmAbi: *wasmAbi,
Programmer: *programmer, Programmer: *programmer,
@@ -894,9 +893,16 @@ func main() {
options.LDFlags = strings.Split(*ldFlags, " ") 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) os.Setenv("CC", "clang -target="+*target)
err := options.Verify() err = options.Verify()
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err.Error()) fmt.Fprintln(os.Stderr, err.Error())
usage() usage()
@@ -949,17 +955,9 @@ func main() {
fmt.Fprintf(os.Stderr, "Unknown library: %s\n", name) fmt.Fprintf(os.Stderr, "Unknown library: %s\n", name)
os.Exit(1) os.Exit(1)
} }
tmpdir, err := ioutil.TempDir("", "tinygo*") path, err := lib.Load(*target)
if err != nil {
handleCompilerError(err) handleCompilerError(err)
} copyFile(path, outpath)
defer os.RemoveAll(tmpdir)
path, err := lib.Load(*target, tmpdir)
handleCompilerError(err)
err = copyFile(path, outpath)
if err != nil {
handleCompilerError(err)
}
case "flash", "gdb": case "flash", "gdb":
pkgName := filepath.ToSlash(flag.Arg(0)) pkgName := filepath.ToSlash(flag.Arg(0))
if command == "flash" { if command == "flash" {
@@ -1078,7 +1076,10 @@ func main() {
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok { if exitErr, ok := err.(*exec.ExitError); ok {
os.Exit(exitErr.ExitCode()) if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
os.Exit(1)
} }
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err) fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
os.Exit(1) os.Exit(1)
+2 -14
View File
@@ -58,9 +58,6 @@ func TestCompiler(t *testing.T) {
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
t.Run("Host", func(t *testing.T) { t.Run("Host", func(t *testing.T) {
runPlatTests("", matches, t) runPlatTests("", matches, t)
if runtime.GOOS == "darwin" {
runTest("testdata/libc/env.go", "", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
}
}) })
} }
@@ -107,7 +104,6 @@ func TestCompiler(t *testing.T) {
t.Run("WASI", func(t *testing.T) { t.Run("WASI", func(t *testing.T) {
runPlatTests("wasi", matches, t) runPlatTests("wasi", matches, t)
runTest("testdata/libc/env.go", "wasi", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
}) })
} }
} }
@@ -117,6 +113,7 @@ func runPlatTests(target string, matches []string, t *testing.T) {
for _, path := range matches { for _, path := range matches {
path := path // redefine to avoid race condition path := path // redefine to avoid race condition
t.Run(filepath.Base(path), func(t *testing.T) { t.Run(filepath.Base(path), func(t *testing.T) {
t.Parallel() t.Parallel()
runTest(path, target, t) runTest(path, target, t)
@@ -136,7 +133,7 @@ func runBuild(src, out string, opts *compileopts.Options) error {
return Build(src, out, opts) return Build(src, out, opts)
} }
func runTest(path, target string, t *testing.T, environmentVars ...string) { func runTest(path, target string, t *testing.T) {
// Get the expected output for this test. // Get the expected output for this test.
txtpath := path[:len(path)-3] + ".txt" txtpath := path[:len(path)-3] + ".txt"
if path[len(path)-1] == os.PathSeparator { if path[len(path)-1] == os.PathSeparator {
@@ -185,7 +182,6 @@ func runTest(path, target string, t *testing.T, environmentVars ...string) {
ranTooLong := false ranTooLong := false
if target == "" { if target == "" {
cmd = exec.Command(binary) cmd = exec.Command(binary)
cmd.Env = append(cmd.Env, environmentVars...)
} else { } else {
spec, err := compileopts.LoadTarget(target) spec, err := compileopts.LoadTarget(target)
if err != nil { if err != nil {
@@ -197,14 +193,6 @@ func runTest(path, target string, t *testing.T, environmentVars ...string) {
args := append(spec.Emulator[1:], binary) args := append(spec.Emulator[1:], binary)
cmd = exec.Command(spec.Emulator[0], args...) 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{} stdout := &bytes.Buffer{}
cmd.Stdout = stdout cmd.Stdout = stdout
+57
View File
@@ -0,0 +1,57 @@
// 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
)
+88
View File
@@ -0,0 +1,88 @@
// 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
)
@@ -0,0 +1,28 @@
// 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
)
+1 -1
View File
@@ -15,7 +15,7 @@ func main() {
led.Configure(machine.PinConfig{Mode: machine.PinOutput}) led.Configure(machine.PinConfig{Mode: machine.PinOutput})
sensor := machine.ADC{machine.ADC2} sensor := machine.ADC{machine.ADC2}
sensor.Configure(machine.ADCConfig{}) sensor.Configure()
for { for {
val := sensor.Get() val := sensor.Get()
-12
View File
@@ -1,12 +0,0 @@
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)
}
+3 -3
View File
@@ -58,7 +58,7 @@ const (
// UART0 is the USB device // UART0 is the USB device
var ( var (
UART0 = &USB UART0 = USB
) )
// I2C pins // I2C pins
@@ -66,8 +66,8 @@ const (
SDA_PIN = P0_05 // I2C0 external SDA_PIN = P0_05 // I2C0 external
SCL_PIN = P0_04 // I2C0 external SCL_PIN = P0_04 // I2C0 external
SDA1_PIN = P1_10 // I2C1 internal SDA1_PIN = P0_00 // I2C1 internal
SCL1_PIN = P1_12 // I2C1 internal SCL1_PIN = P0_01 // I2C1 internal
) )
// SPI pins (internal flash) // SPI pins (internal flash)
+2 -2
View File
@@ -2,7 +2,7 @@
package machine package machine
const HasLowFrequencyCrystal = false const HasLowFrequencyCrystal = true
// GPIO Pins // GPIO Pins
const ( const (
@@ -105,7 +105,7 @@ const (
// UART0 is the USB device // UART0 is the USB device
var ( var (
UART0 = &USB UART0 = USB
) )
// I2C pins // I2C pins
+1 -1
View File
@@ -77,7 +77,7 @@ const (
// UART0 is the USB device // UART0 is the USB device
var ( var (
UART0 = &USB UART0 = USB
) )
// I2C pins // I2C pins
+9 -9
View File
@@ -122,17 +122,17 @@ var (
UART1 = UART{ UART1 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: stm32.USART3, Bus: stm32.USART3,
AltFuncSelector: AF7_USART1_2_3, AltFuncSelector: stm32.AF7_USART1_2_3,
} }
UART2 = UART{ UART2 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: stm32.USART6, Bus: stm32.USART6,
AltFuncSelector: AF8_USART4_5_6, AltFuncSelector: stm32.AF8_USART4_5_6,
} }
UART3 = UART{ UART3 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: stm32.USART1, Bus: stm32.USART1,
AltFuncSelector: AF7_USART1_2_3, AltFuncSelector: stm32.AF7_USART1_2_3,
} }
UART0 = UART1 UART0 = UART1
) )
@@ -181,15 +181,15 @@ const (
var ( var (
SPI1 = SPI{ SPI1 = SPI{
Bus: stm32.SPI2, Bus: stm32.SPI2,
AltFuncSelector: AF5_SPI1_SPI2, AltFuncSelector: stm32.AF5_SPI1_SPI2,
} }
SPI2 = SPI{ SPI2 = SPI{
Bus: stm32.SPI3, Bus: stm32.SPI3,
AltFuncSelector: AF6_SPI3, AltFuncSelector: stm32.AF6_SPI3,
} }
SPI3 = SPI{ SPI3 = SPI{
Bus: stm32.SPI1, Bus: stm32.SPI1,
AltFuncSelector: AF5_SPI1_SPI2, AltFuncSelector: stm32.AF5_SPI1_SPI2,
} }
SPI0 = SPI1 SPI0 = SPI1
) )
@@ -229,15 +229,15 @@ const (
var ( var (
I2C1 = I2C{ I2C1 = I2C{
Bus: stm32.I2C1, Bus: stm32.I2C1,
AltFuncSelector: AF4_I2C1_2_3, AltFuncSelector: stm32.AF4_I2C1_2_3,
} }
I2C2 = I2C{ I2C2 = I2C{
Bus: stm32.I2C2, Bus: stm32.I2C2,
AltFuncSelector: AF4_I2C1_2_3, AltFuncSelector: stm32.AF4_I2C1_2_3,
} }
I2C3 = I2C{ I2C3 = I2C{
Bus: stm32.I2C1, Bus: stm32.I2C1,
AltFuncSelector: AF4_I2C1_2_3, AltFuncSelector: stm32.AF4_I2C1_2_3,
} }
I2C0 = I2C1 I2C0 = I2C1
) )
-243
View File
@@ -1,243 +0,0 @@
// +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
)
@@ -1,63 +0,0 @@
// +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,
}
)
+1 -1
View File
@@ -72,7 +72,7 @@ const (
// UART0 is the USB device // UART0 is the USB device
var ( var (
UART0 = &USB UART0 = USB
) )
// I2C pins // I2C pins
-80
View File
@@ -1,80 +0,0 @@
// +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)
}
-114
View File
@@ -1,114 +0,0 @@
// +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
)
-84
View File
@@ -1,84 +0,0 @@
// +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 cant 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
)
@@ -1,53 +0,0 @@
// +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
)
+1 -1
View File
@@ -38,7 +38,7 @@ const (
// USB CDC identifiers // USB CDC identifiers
const ( const (
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK" usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK USB Dongle"
usb_STRING_MANUFACTURER = "Makerdiary" usb_STRING_MANUFACTURER = "Makerdiary"
) )
-45
View File
@@ -1,45 +0,0 @@
// +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)
}
-120
View File
@@ -1,120 +0,0 @@
// +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
)
-47
View File
@@ -1,47 +0,0 @@
// +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
)
+1 -1
View File
@@ -41,7 +41,7 @@ const (
// UART // UART
var ( var (
Serial = &USB Serial = USB
UART0 = NRF_UART0 UART0 = NRF_UART0
) )
+1 -1
View File
@@ -41,7 +41,7 @@ const (
// UART // UART
var ( var (
Serial = &USB Serial = USB
UART0 = NRF_UART0 UART0 = NRF_UART0
) )
+1 -1
View File
@@ -41,7 +41,7 @@ const (
// UART // UART
var ( var (
Serial = &USB Serial = USB
UART0 = NRF_UART0 UART0 = NRF_UART0
) )
+2 -14
View File
@@ -30,7 +30,7 @@ var (
UART0 = UART{ UART0 = UART{
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
Bus: stm32.USART2, Bus: stm32.USART2,
AltFuncSelector: AF7_USART1_2_3, AltFuncSelector: stm32.AF7_USART1_2_3,
} }
UART1 = &UART0 UART1 = &UART0
) )
@@ -62,19 +62,7 @@ const (
var ( var (
SPI0 = SPI{ SPI0 = SPI{
Bus: stm32.SPI1, Bus: stm32.SPI1,
AltFuncSelector: AF5_SPI1_SPI2, AltFuncSelector: stm32.AF5_SPI1_SPI2,
} }
SPI1 = &SPI0 SPI1 = &SPI0
) )
const (
I2C0_SCL_PIN = PB6
I2C0_SDA_PIN = PB9
)
var (
I2C0 = I2C{
Bus: stm32.I2C1,
AltFuncSelector: AF4_I2C1_2_3,
}
)
+7 -15
View File
@@ -136,11 +136,9 @@ const (
) )
var ( var (
UART0 = &UART1 // alias UART0 to UART1
UART1 = UART{ UART1 = UART{
Bus: nxp.LPUART6,
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(), Bus: nxp.LPUART6,
muxRX: muxSelect{ // D0 (PA3 [AD_B0_03]) muxRX: muxSelect{ // D0 (PA3 [AD_B0_03])
mux: nxp.IOMUXC_LPUART6_RX_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT2, mux: nxp.IOMUXC_LPUART6_RX_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT2,
sel: &nxp.IOMUXC.LPUART6_RX_SELECT_INPUT, sel: &nxp.IOMUXC.LPUART6_RX_SELECT_INPUT,
@@ -151,9 +149,8 @@ var (
}, },
} }
UART2 = UART{ UART2 = UART{
Bus: nxp.LPUART4,
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(), Bus: nxp.LPUART4,
muxRX: muxSelect{ // D7 (PB17 [B1_01]) muxRX: muxSelect{ // D7 (PB17 [B1_01])
mux: nxp.IOMUXC_LPUART4_RX_SELECT_INPUT_DAISY_GPIO_B1_01_ALT2, mux: nxp.IOMUXC_LPUART4_RX_SELECT_INPUT_DAISY_GPIO_B1_01_ALT2,
sel: &nxp.IOMUXC.LPUART4_RX_SELECT_INPUT, sel: &nxp.IOMUXC.LPUART4_RX_SELECT_INPUT,
@@ -164,9 +161,8 @@ var (
}, },
} }
UART3 = UART{ UART3 = UART{
Bus: nxp.LPUART2,
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(), Bus: nxp.LPUART2,
muxRX: muxSelect{ // D15 (PA19 [AD_B1_03]) muxRX: muxSelect{ // D15 (PA19 [AD_B1_03])
mux: nxp.IOMUXC_LPUART2_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_03_ALT2, mux: nxp.IOMUXC_LPUART2_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_03_ALT2,
sel: &nxp.IOMUXC.LPUART2_RX_SELECT_INPUT, sel: &nxp.IOMUXC.LPUART2_RX_SELECT_INPUT,
@@ -177,9 +173,8 @@ var (
}, },
} }
UART4 = UART{ UART4 = UART{
Bus: nxp.LPUART3,
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(), Bus: nxp.LPUART3,
muxRX: muxSelect{ // D16 (PA23 [AD_B1_07]) muxRX: muxSelect{ // D16 (PA23 [AD_B1_07])
mux: nxp.IOMUXC_LPUART3_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_07_ALT2, mux: nxp.IOMUXC_LPUART3_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_07_ALT2,
sel: &nxp.IOMUXC.LPUART3_RX_SELECT_INPUT, sel: &nxp.IOMUXC.LPUART3_RX_SELECT_INPUT,
@@ -190,9 +185,8 @@ var (
}, },
} }
UART5 = UART{ UART5 = UART{
Bus: nxp.LPUART8,
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(), Bus: nxp.LPUART8,
muxRX: muxSelect{ // D21 (PA27 [AD_B1_11]) muxRX: muxSelect{ // D21 (PA27 [AD_B1_11])
mux: nxp.IOMUXC_LPUART8_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_11_ALT2, mux: nxp.IOMUXC_LPUART8_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_11_ALT2,
sel: &nxp.IOMUXC.LPUART8_RX_SELECT_INPUT, sel: &nxp.IOMUXC.LPUART8_RX_SELECT_INPUT,
@@ -203,17 +197,15 @@ var (
}, },
} }
UART6 = UART{ UART6 = UART{
Bus: nxp.LPUART1,
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(), Bus: nxp.LPUART1,
// LPUART1 not connected via IOMUXC // LPUART1 not connected via IOMUXC
// RX: D24 (PA12 [AD_B0_12]) // RX: D24 (PA12 [AD_B0_12])
// TX: D25 (PA13 [AD_B0_13]) // TX: D25 (PA13 [AD_B0_13])
} }
UART7 = UART{ UART7 = UART{
Bus: nxp.LPUART7,
Buffer: NewRingBuffer(), Buffer: NewRingBuffer(),
txBuffer: NewRingBuffer(), Bus: nxp.LPUART7,
muxRX: muxSelect{ // D28 (PC18 [EMC_32]) muxRX: muxSelect{ // D28 (PC18 [EMC_32])
mux: nxp.IOMUXC_LPUART7_RX_SELECT_INPUT_DAISY_GPIO_EMC_32_ALT2, mux: nxp.IOMUXC_LPUART7_RX_SELECT_INPUT_DAISY_GPIO_EMC_32_ALT2,
sel: &nxp.IOMUXC.LPUART7_RX_SELECT_INPUT, sel: &nxp.IOMUXC.LPUART7_RX_SELECT_INPUT,
+1 -1
View File
@@ -1,4 +1,4 @@
// +build avr nrf sam stm32,!stm32f7x2,!stm32l5x2,!stm32l0 fe310 k210 // +build avr nrf sam stm32,!stm32f407,!stm32f7x2 fe310 k210
package machine package machine
+1 -116
View File
@@ -5,8 +5,6 @@ package machine
import ( import (
"device/avr" "device/avr"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile"
"unsafe"
) )
// I2CConfig is used to store config info for I2C. // I2CConfig is used to store config info for I2C.
@@ -15,7 +13,7 @@ type I2CConfig struct {
} }
// Configure is intended to setup the I2C interface. // Configure is intended to setup the I2C interface.
func (i2c I2C) Configure(config I2CConfig) error { func (i2c I2C) Configure(config I2CConfig) {
// Default I2C bus speed is 100 kHz. // Default I2C bus speed is 100 kHz.
if config.Frequency == 0 { if config.Frequency == 0 {
config.Frequency = TWI_FREQ_100KHZ config.Frequency = TWI_FREQ_100KHZ
@@ -35,8 +33,6 @@ func (i2c I2C) Configure(config I2CConfig) error {
// Enable twi module. // Enable twi module.
avr.TWCR.Set(avr.TWCR_TWEN) avr.TWCR.Set(avr.TWCR_TWEN)
return nil
} }
// Tx does a single I2C transaction at the specified address. // Tx does a single I2C transaction at the specified address.
@@ -114,12 +110,6 @@ func (i2c I2C) readByte() byte {
return byte(avr.TWDR.Get()) return byte(avr.TWDR.Get())
} }
// UART
var (
// UART0 is the hardware serial port on the AVR.
UART0 = UART{Buffer: NewRingBuffer()}
)
// UART on the AVR. // UART on the AVR.
type UART struct { type UART struct {
Buffer *RingBuffer Buffer *RingBuffer
@@ -165,108 +155,3 @@ func (uart UART) WriteByte(c byte) error {
avr.UDR0.Set(c) // send char avr.UDR0.Set(c) // send char
return nil 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
}
-10
View File
@@ -69,13 +69,3 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
return avr.PORTD, 1 << uint8(p-portD) 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}
-10
View File
@@ -126,13 +126,3 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
return avr.PORTA, 255 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}
-10
View File
@@ -88,13 +88,3 @@ func (pwm PWM) Set(value uint16) {
panic("Invalid PWM pin") 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}
-109
View File
@@ -1,109 +0,0 @@
// +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}
+18 -140
View File
@@ -11,7 +11,6 @@ import (
"device/arm" "device/arm"
"device/sam" "device/sam"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile"
"unsafe" "unsafe"
) )
@@ -308,30 +307,13 @@ func InitADC() {
// set calibration // set calibration
sam.ADC.CALIB.Set((bias << 8) | linearity) 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 // Wait for synchronization
waitADCSync() 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 // 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.Set((sam.ADC_CTRLB_PRESCALER_DIV32 << sam.ADC_CTRLB_PRESCALER_Pos) |
uint16(resolution<<sam.ADC_CTRLB_RESSEL_Pos)) (sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos))
// Sampling Time Length // Sampling Time Length
sam.ADC.SAMPCTRL.Set(5) sam.ADC.SAMPCTRL.Set(5)
@@ -343,44 +325,18 @@ func (a ADC) Configure(config ADCConfig) {
sam.ADC.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos) sam.ADC.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
// Averaging (see datasheet table in AVGCTRL register description) // Averaging (see datasheet table in AVGCTRL register description)
var samples uint32 sam.ADC.AVGCTRL.Set((sam.ADC_AVGCTRL_SAMPLENUM_1 << sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
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)) (0x0 << sam.ADC_AVGCTRL_ADJRES_Pos))
// TODO: use config.Reference to set AREF level
// Analog Reference is AREF pin (3.3v) // Analog Reference is AREF pin (3.3v)
sam.ADC.INPUTCTRL.SetBits(sam.ADC_INPUTCTRL_GAIN_DIV2 << sam.ADC_INPUTCTRL_GAIN_Pos) sam.ADC.INPUTCTRL.SetBits(sam.ADC_INPUTCTRL_GAIN_DIV2 << sam.ADC_INPUTCTRL_GAIN_Pos)
// 1/2 VDDANA = 0.5 * 3V3 = 1.65V // 1/2 VDDANA = 0.5 * 3V3 = 1.65V
sam.ADC.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1 << sam.ADC_REFCTRL_REFSEL_Pos) 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}) a.Pin.Configure(PinConfig{Mode: PinAnalog})
return return
} }
@@ -1466,103 +1422,35 @@ func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21. // USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
type USBCDC struct { type USBCDC struct {
Buffer *RingBuffer Buffer *RingBuffer
TxIdx volatile.Register8
waitTxc bool
waitTxcRetryCount uint8
sent bool
} }
const ( // WriteByte writes a byte of data to the USB CDC interface.
usbcdcTxSizeMask uint8 = 0x3F func (usbcdc USBCDC) WriteByte(c byte) error {
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask // Supposedly to handle problem with Windows USB serial ports?
usbcdcTxBank1st uint8 = 0x00
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
usbcdcTxMaxRetriesAllowed uint8 = 5
)
// Flush flushes buffered data.
func (usbcdc *USBCDC) Flush() error {
if usbLineInfo.lineState > 0 { 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 // 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])))) udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c
if bk == usbcdcTxBank1st {
usbcdc.TxIdx.Set(usbcdcTxBank2nd) usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN]))))
} else {
usbcdc.TxIdx.Set(usbcdcTxBank1st)
}
// clean multi packet size of bytes already sent // 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) 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 // 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((1 & 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 // clear transfer complete flag
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1) setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
// send data by setting bank ready // send data by setting bank ready
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY) 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. // wait for transfer to complete
func (usbcdc *USBCDC) WriteByte(c byte) error { timeout := 3000
// Supposedly to handle problem with Windows USB serial ports? for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
if usbLineInfo.lineState > 0 { timeout--
ok := false if timeout == 0 {
for { return errUSBCDCWriteByteTimeout
mask := interrupt.Disable()
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
}
interrupt.Restore(mask)
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)
} }
} }
} }
@@ -1769,19 +1657,9 @@ func handleUSB(intr interrupt.Interrupt) {
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY) setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1) 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) { func initEndpoint(ep, config uint32) {
+69 -257
View File
@@ -10,9 +10,7 @@ package machine
import ( import (
"device/arm" "device/arm"
"device/sam" "device/sam"
"errors"
"runtime/interrupt" "runtime/interrupt"
"runtime/volatile"
"unsafe" "unsafe"
) )
@@ -722,83 +720,65 @@ func InitADC() {
// calibrate ADC1 // calibrate ADC1
sam.ADC1.CALIB.Set(uint16((biascomp | biasr2r | biasref) >> 16)) sam.ADC1.CALIB.Set(uint16((biascomp | biasr2r | biasref) >> 16))
sam.ADC0.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
// adcs[i]->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val;
sam.ADC0.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
}
// sampling Time Length
sam.ADC0.SAMPCTRL.Set(5)
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_SAMPCTRL) {
}
// No Negative input (Internal Ground)
sam.ADC0.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_INPUTCTRL) {
}
// Averaging (see datasheet table in AVGCTRL register description)
// 1 sample only (no oversampling nor averaging), adjusting result by 0
sam.ADC0.AVGCTRL.Set(sam.ADC_AVGCTRL_SAMPLENUM_1 | (0 << sam.ADC_AVGCTRL_ADJRES_Pos))
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_AVGCTRL) {
}
// same for ADC1, as for ADC0
sam.ADC1.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
sam.ADC1.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
}
sam.ADC1.SAMPCTRL.Set(5)
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_SAMPCTRL) {
}
sam.ADC1.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_INPUTCTRL) {
}
sam.ADC1.AVGCTRL.Set(sam.ADC_AVGCTRL_SAMPLENUM_1 | (0 << sam.ADC_AVGCTRL_ADJRES_Pos))
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_AVGCTRL) {
}
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_REFCTRL) {
}
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_REFCTRL) {
}
// default is 3V3 reference voltage
sam.ADC0.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1)
sam.ADC1.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1)
} }
// Configure configures a ADCPin to be able to be used to read data. // Configure configures a ADCPin to be able to be used to read data.
func (a ADC) Configure(config ADCConfig) { func (a ADC) Configure() {
for _, adc := range []*sam.ADC_Type{sam.ADC0, sam.ADC1} {
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
} // wait for sync
adc.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
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
}
adc.CTRLB.SetBits(uint16(resolution << sam.ADC_CTRLB_RESSEL_Pos))
adc.SAMPCTRL.Set(5) // sampling Time Length
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_SAMPCTRL) {
} // wait for sync
// No Negative input (Internal Ground)
adc.INPUTCTRL.Set(sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_INPUTCTRL) {
} // wait for sync
// Averaging (see datasheet table in AVGCTRL register description)
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: // 1 sample only (no oversampling nor averaging), adjusting result by 0
samples = sam.ADC_AVGCTRL_SAMPLENUM_1
}
adc.AVGCTRL.Set(uint8(samples<<sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
(0 << sam.ADC_AVGCTRL_ADJRES_Pos))
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_AVGCTRL) {
} // wait for sync
for adc.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_REFCTRL) {
} // wait for sync
// TODO: use config.Reference to set AREF level
// default is 3V3 reference voltage
adc.REFCTRL.SetBits(sam.ADC_REFCTRL_REFSEL_INTVCC1)
}
a.Pin.Configure(PinConfig{Mode: PinAnalog}) a.Pin.Configure(PinConfig{Mode: PinAnalog})
} }
@@ -1470,96 +1450,6 @@ func (spi SPI) Transfer(w byte) (byte, error) {
return byte(spi.Bus.DATA.Get()), nil return byte(spi.Bus.DATA.Get()), nil
} }
var (
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
)
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
// interface, there must always be the same number of bytes written as bytes read.
// The Tx method knows about this, and offers a few different ways of calling it.
//
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
// Note that the tx and rx buffers must be the same size:
//
// spi.Tx(tx, rx)
//
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
// until all the bytes in the command packet have been received:
//
// spi.Tx(tx, nil)
//
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
//
// spi.Tx(nil, rx)
//
func (spi SPI) Tx(w, r []byte) error {
switch {
case w == nil:
// read only, so write zero and read a result.
spi.rx(r)
case r == nil:
// write only
spi.tx(w)
default:
// write/read
if len(w) != len(r) {
return ErrTxInvalidSliceSize
}
spi.txrx(w, r)
}
return nil
}
func (spi SPI) tx(tx []byte) {
for i := 0; i < len(tx); i++ {
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
spi.Bus.DATA.Set(uint32(tx[i]))
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_TXC) {
}
// read to clear RXC register
for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
spi.Bus.DATA.Get()
}
}
func (spi SPI) rx(rx []byte) {
spi.Bus.DATA.Set(0)
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
for i := 1; i < len(rx); i++ {
spi.Bus.DATA.Set(0)
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[i-1] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
func (spi SPI) txrx(tx, rx []byte) {
spi.Bus.DATA.Set(uint32(tx[0]))
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
for i := 1; i < len(rx); i++ {
spi.Bus.DATA.Set(uint32(tx[i]))
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[i-1] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
// The QSPI peripheral on ATSAMD51 is only available on the following pins // The QSPI peripheral on ATSAMD51 is only available on the following pins
const ( const (
QSPI_SCK = PB10 QSPI_SCK = PB10
@@ -1826,103 +1716,35 @@ func (pwm PWM) getMux() PinMode {
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21. // USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
type USBCDC struct { type USBCDC struct {
Buffer *RingBuffer Buffer *RingBuffer
TxIdx volatile.Register8
waitTxc bool
waitTxcRetryCount uint8
sent bool
} }
const ( // WriteByte writes a byte of data to the USB CDC interface.
usbcdcTxSizeMask uint8 = 0x3F func (usbcdc USBCDC) WriteByte(c byte) error {
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask // Supposedly to handle problem with Windows USB serial ports?
usbcdcTxBank1st uint8 = 0x00
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
usbcdcTxMaxRetriesAllowed uint8 = 5
)
// Flush flushes buffered data.
func (usbcdc *USBCDC) Flush() error {
if usbLineInfo.lineState > 0 { 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 // 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])))) udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c
if bk == usbcdcTxBank1st {
usbcdc.TxIdx.Set(usbcdcTxBank2nd) usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN]))))
} else {
usbcdc.TxIdx.Set(usbcdcTxBank1st)
}
// clean multi packet size of bytes already sent // 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) 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 // 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((1 & 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 // clear transfer complete flag
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
// send data by setting bank ready // send data by setting bank ready
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY) setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
UART0.sent = true
}
}
return nil
}
// WriteByte writes a byte of data to the USB CDC interface. // wait for transfer to complete
func (usbcdc *USBCDC) WriteByte(c byte) error { timeout := 3000
// Supposedly to handle problem with Windows USB serial ports? for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
if usbLineInfo.lineState > 0 { timeout--
ok := false if timeout == 0 {
for { return errUSBCDCWriteByteTimeout
mask := interrupt.Disable()
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
}
interrupt.Restore(mask)
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_ENDPOINT_EPINTFLAG_TRCPT1) != 0 {
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
UART0.waitTxc = false
UART0.Flush()
}
} else {
UART0.Flush()
}
}
interrupt.Restore(mask)
} }
} }
} }
@@ -2131,19 +1953,9 @@ func handleUSBIRQ(interrupt.Interrupt) {
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY) setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_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) { func initEndpoint(ep, config uint32) {
-70
View File
@@ -1,70 +0,0 @@
// +build sam,atsamd51,atsamd51p20
// Peripheral abstraction layer for the atsamd51.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
//
package machine
import "device/sam"
const HSRAM_SIZE = 0x00040000
// InitPWM initializes the PWM interface.
func InitPWM() {
// turn on timer clocks used for PWM
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
//use clock generator 0
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
// getTimer returns the timer to be used for PWM on this pin
func (pwm PWM) getTimer() *sam.TCC_Type {
switch pwm.Pin {
case PC18:
return sam.TCC0
case PC19:
return sam.TCC0
case PC20:
return sam.TCC0
case PC21:
return sam.TCC0
case PD20:
return sam.TCC1
case PD21:
return sam.TCC1
case PB18:
return sam.TCC1
case PB12:
return sam.TCC3
case PB13:
return sam.TCC3
case PA15:
return sam.TCC2
case PC17:
return sam.TCC0
case PC16:
return sam.TCC0
case PA14:
return sam.TCC2
case PB15:
return sam.TCC4
case PB14:
return sam.TCC4
case PB20:
return sam.TCC1
case PB21:
return sam.TCC1
default:
return nil // not supported on this pin
}
}
+17
View File
@@ -2,6 +2,23 @@
package machine package machine
// UART on the AVR is a dummy implementation. UART has not been implemented for ATtiny
// devices.
type UART struct {
Buffer *RingBuffer
}
// Configure is a dummy implementation. UART has not been implemented for ATtiny
// devices.
func (uart UART) Configure(config UARTConfig) {
}
// WriteByte is a dummy implementation. UART has not been implemented for ATtiny
// devices.
func (uart UART) WriteByte(c byte) error {
return nil
}
// Tx is a dummy implementation. I2C has not been implemented for ATtiny // Tx is a dummy implementation. I2C has not been implemented for ATtiny
// devices. // devices.
func (i2c I2C) Tx(addr uint16, w, r []byte) error { func (i2c I2C) Tx(addr uint16, w, r []byte) error {
+7 -1
View File
@@ -118,7 +118,7 @@ func InitADC() {
} }
// Configure configures a ADCPin to be able to be used to read data. // Configure configures a ADCPin to be able to be used to read data.
func (a ADC) Configure(ADCConfig) { func (a ADC) Configure() {
return // no pin specific setup on AVR machine. return // no pin specific setup on AVR machine.
} }
@@ -148,3 +148,9 @@ type I2C struct {
// I2C0 is the only I2C interface on most AVRs. // I2C0 is the only I2C interface on most AVRs.
var I2C0 = I2C{} var I2C0 = I2C{}
// UART
var (
// UART0 is the hardware serial port on the AVR.
UART0 = UART{Buffer: NewRingBuffer()}
)

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