mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 02:27:48 +00:00
Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 18c97e9b55 | |||
| a867b56e5f | |||
| f159429152 | |||
| a848d720db | |||
| dbc438b2ae | |||
| c4d642007e | |||
| da0161d6ab | |||
| 5af4c073cd | |||
| 154c7c691b | |||
| 65caf777dd | |||
| a4d0877cf0 | |||
| 6ec868710b | |||
| 086645153e | |||
| cf930f652e | |||
| 0dcff35d3a | |||
| 75f13491b6 | |||
| 533d2c9ce0 | |||
| 2f4200a01b | |||
| 939b393325 | |||
| a5ee1ba4b3 | |||
| fb0bb69f62 | |||
| 5917b8baa2 | |||
| 30df912565 | |||
| e9d549d211 | |||
| 6ad631539d | |||
| cda5fffd98 | |||
| ce539ce583 | |||
| 43a31467d3 | |||
| ae92ea149c | |||
| 4568de556e | |||
| 7a4ccd916f | |||
| 4cc1cdf672 | |||
| bb27bbcb41 | |||
| 9c2d2b662b | |||
| 098f900363 | |||
| caf35cfc41 | |||
| abb09e869e | |||
| bb58783158 | |||
| 7abc67107d | |||
| 2540172cc5 | |||
| 0c4f0b1ebf | |||
| 15361c829e | |||
| 3f680b75f3 | |||
| 9a7e633997 | |||
| 39b1f8b6f5 | |||
| 9aa50853b8 | |||
| 9ca0e3f2d1 | |||
| 3cdc110462 | |||
| 7cc687d416 | |||
| ce57a034c3 | |||
| 30bee3afef | |||
| b1d24a72c1 | |||
| 6e24c86320 | |||
| 19a0270303 | |||
| 47410a4b54 | |||
| 0d9c46b59e | |||
| f93b28057a | |||
| 691185f5f4 | |||
| 163df7670a | |||
| 77c70d2758 | |||
| 7c4e83f5c0 | |||
| db27541b1a | |||
| 3bb994da9f | |||
| b3bd891ee0 | |||
| 387bca8e32 | |||
| d424b3d7ea | |||
| c20328472b | |||
| 171f793c1e | |||
| 3364da6f25 | |||
| e99b8a24fe | |||
| 69e1aa4878 | |||
| 7a78b2dc0e | |||
| 2b1d4ce96e | |||
| 3e40b08ba0 | |||
| ffeff55706 | |||
| 1dec9dcbc4 | |||
| ff833ef998 | |||
| 6eeebfeb5c | |||
| 6ab0106af3 | |||
| e690ff0d8c | |||
| 06564cbdb2 | |||
| 47dc76fc34 | |||
| d382f3a259 | |||
| 47dc1087e8 | |||
| c7d8223ab7 | |||
| 3eb33dff5d | |||
| bb146edb47 | |||
| ed9b97cc0d | |||
| b40f250530 | |||
| 184175378f | |||
| d8dbe5748a | |||
| c2bfe6bc8d | |||
| 9a015f4f64 | |||
| 9ad2315079 | |||
| 431e51b8a0 | |||
| 67de8b490d | |||
| bfa29f17da | |||
| 7123941df0 | |||
| 05d2f2c412 | |||
| 9a12d129ab | |||
| 0ecfe9eade | |||
| f50ad3585d | |||
| d39c7abb4d | |||
| 9e61e6fe4d | |||
| d1f90ef59c | |||
| 54602fe0c3 | |||
| c10dcd429c | |||
| b713001313 | |||
| 1096596b69 | |||
| 7b601b3e3c | |||
| ec54e7763d | |||
| 41afb77080 | |||
| 13fe668929 | |||
| 2a72262c33 |
+31
-95
@@ -28,6 +28,7 @@ commands:
|
||||
qemu-user \
|
||||
gcc-avr \
|
||||
avr-libc
|
||||
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-8-dev
|
||||
install-node:
|
||||
steps:
|
||||
- run:
|
||||
@@ -44,6 +45,13 @@ commands:
|
||||
command: |
|
||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||
sudo apt install ./google-chrome-stable_current_amd64.deb
|
||||
install-wasmtime:
|
||||
steps:
|
||||
- run:
|
||||
name: "Install wasmtime"
|
||||
command: |
|
||||
curl https://wasmtime.dev/install.sh -sSf | bash
|
||||
sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime
|
||||
install-xtensa-toolchain:
|
||||
parameters:
|
||||
variant:
|
||||
@@ -60,12 +68,12 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-10-v1
|
||||
- llvm-source-11-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-10-v1
|
||||
key: llvm-source-11-v1
|
||||
paths:
|
||||
- llvm-project
|
||||
build-wasi-libc:
|
||||
@@ -91,6 +99,7 @@ commands:
|
||||
llvm: "<<parameters.llvm>>"
|
||||
- install-node
|
||||
- install-chrome
|
||||
- install-wasmtime
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
@@ -105,7 +114,7 @@ commands:
|
||||
key: wasi-libc-sysroot-systemclang-v2
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
|
||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest XTENSA=0
|
||||
- run: make tinygo-test
|
||||
@@ -125,7 +134,6 @@ commands:
|
||||
command: |
|
||||
sudo apt-get install \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
binutils-arm-none-eabi \
|
||||
libc6-dev-armel-cross \
|
||||
gcc-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross \
|
||||
@@ -133,7 +141,9 @@ commands:
|
||||
qemu-user \
|
||||
gcc-avr \
|
||||
avr-libc
|
||||
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-6-dev
|
||||
- install-node
|
||||
- install-wasmtime
|
||||
- install-xtensa-toolchain:
|
||||
variant: "linux-amd64"
|
||||
- restore_cache:
|
||||
@@ -143,17 +153,14 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-10-linux-v1-assert
|
||||
- llvm-build-11-linux-v1-assert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# install dependencies
|
||||
sudo apt-get install cmake clang ninja-build
|
||||
# make build faster
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
sudo apt-get install cmake ninja-build
|
||||
# hack ninja to use less jobs
|
||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
||||
chmod +x /go/bin/ninja
|
||||
@@ -161,7 +168,7 @@ commands:
|
||||
make ASSERT=1 llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-10-linux-v1-assert
|
||||
key: llvm-build-11-linux-v1-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- run: make ASSERT=1
|
||||
@@ -185,7 +192,6 @@ commands:
|
||||
command: |
|
||||
sudo apt-get install \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
binutils-arm-none-eabi \
|
||||
libc6-dev-armel-cross \
|
||||
gcc-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross \
|
||||
@@ -193,7 +199,9 @@ commands:
|
||||
qemu-user \
|
||||
gcc-avr \
|
||||
avr-libc
|
||||
sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-6-dev
|
||||
- install-node
|
||||
- install-wasmtime
|
||||
- install-xtensa-toolchain:
|
||||
variant: "linux-amd64"
|
||||
- restore_cache:
|
||||
@@ -203,17 +211,14 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-10-linux-v1
|
||||
- llvm-build-11-linux-v1-noassert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# install dependencies
|
||||
sudo apt-get install cmake clang ninja-build
|
||||
# make build faster
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
sudo apt-get install cmake ninja-build
|
||||
# hack ninja to use less jobs
|
||||
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
|
||||
chmod +x /go/bin/ninja
|
||||
@@ -221,7 +226,7 @@ commands:
|
||||
make llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-10-linux-v1
|
||||
key: llvm-build-11-linux-v1-noassert
|
||||
paths:
|
||||
llvm-build
|
||||
- build-wasi-libc
|
||||
@@ -263,8 +268,8 @@ commands:
|
||||
- run:
|
||||
name: "Install dependencies"
|
||||
command: |
|
||||
curl https://dl.google.com/go/go1.14.darwin-amd64.tar.gz -o go1.14.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.14.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.15.5.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||
- install-xtensa-toolchain:
|
||||
@@ -275,17 +280,17 @@ commands:
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-10-macos-v1
|
||||
- llvm-source-11-macos-v1
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-10-macos-v1
|
||||
key: llvm-source-11-macos-v1
|
||||
paths:
|
||||
- llvm-project
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-10-macos-v1
|
||||
- llvm-build-11-macos-v1
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -297,7 +302,7 @@ commands:
|
||||
make llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-10-macos-v1
|
||||
key: llvm-build-11-macos-v1
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
@@ -333,60 +338,6 @@ commands:
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
arch-release:
|
||||
steps:
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: pacman -Sy --noconfirm openssh git pacman-contrib binutils
|
||||
- run:
|
||||
name: Create TinyGo user
|
||||
command: useradd -m tinygo
|
||||
- run:
|
||||
name: Start SSH Agent
|
||||
user: tinygo
|
||||
command: eval $(ssh-agent -s)
|
||||
- run:
|
||||
name: Add ARCH_RELEASE_SSH_PRIVATE_KEY identity
|
||||
user: tinygo
|
||||
command: echo "${ARCH_RELEASE_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add -
|
||||
- run:
|
||||
name: Create SSH directory
|
||||
user: tinygo
|
||||
command: mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
||||
- run:
|
||||
name: Add aur.archlinux.org to known hosts
|
||||
user: tinygo
|
||||
command: ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
|
||||
- run:
|
||||
name: Clone tinygo-bin repo
|
||||
user: tinygo
|
||||
command: git clone ssh://aur@aur.archlinux.org/tinygo-bin.git ~/tinygo-bin
|
||||
- run:
|
||||
name: Update package version
|
||||
user: tinygo
|
||||
command: sed -i -E "s/(pkgver=)(.*)$/\1${CIRCLE_TAG}/" ~/tinygo-bin/PKGBUILD
|
||||
- run:
|
||||
name: Update file checksums
|
||||
user: tinygo
|
||||
command: cd ~/tinygo-bin && updpkgsums
|
||||
- run:
|
||||
name: Update .SRCINFO
|
||||
user: tinygo
|
||||
command: cd ~/tinygo-bin && makepkg --printsrcinfo > .SRCINFO
|
||||
# Commit the update
|
||||
- run:
|
||||
name: Set git commit config
|
||||
user: tinygo
|
||||
command: |
|
||||
git config --global user.email "tinygo-bot@tinygo.org" &&
|
||||
git config --global user.name "TinyGo Release Bot"
|
||||
- run:
|
||||
name: Commit and push changes
|
||||
user: tinygo
|
||||
command: |
|
||||
cd ~/tinygo-bin &&
|
||||
git commit -a -m "Update tinygo-bin to v${CIRCLE_TAG}" &&
|
||||
git push origin master
|
||||
|
||||
jobs:
|
||||
test-llvm9-go111:
|
||||
@@ -413,12 +364,12 @@ jobs:
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "10"
|
||||
test-llvm10-go115:
|
||||
test-llvm11-go115:
|
||||
docker:
|
||||
- image: circleci/golang:1.15-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "10"
|
||||
llvm: "11"
|
||||
assert-test-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.14-stretch
|
||||
@@ -434,12 +385,6 @@ jobs:
|
||||
xcode: "10.1.0"
|
||||
steps:
|
||||
- build-macos
|
||||
arch-release:
|
||||
docker:
|
||||
- image: archlinux:latest
|
||||
steps:
|
||||
- arch-release
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -450,16 +395,7 @@ workflows:
|
||||
- test-llvm10-go112
|
||||
- test-llvm10-go113
|
||||
- test-llvm10-go114
|
||||
- test-llvm10-go115
|
||||
- test-llvm11-go115
|
||||
- build-linux
|
||||
- build-macos
|
||||
- assert-test-linux
|
||||
release:
|
||||
jobs:
|
||||
- arch-release:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
# Runs on every semver release
|
||||
only: /v[0-9]+(\.[0-9]+)*(-.*)*/
|
||||
|
||||
@@ -20,3 +20,6 @@
|
||||
[submodule "lib/picolibc"]
|
||||
path = lib/picolibc
|
||||
url = https://github.com/keith-packard/picolibc.git
|
||||
[submodule "lib/stm32-svd"]
|
||||
path = lib/stm32-svd
|
||||
url = https://github.com/tinygo-org/stm32-svd
|
||||
|
||||
@@ -1,3 +1,57 @@
|
||||
0.16.0
|
||||
---
|
||||
|
||||
* **command-line**
|
||||
- add initial support for LLVM 11
|
||||
- make lib64 clang include path check more robust
|
||||
- `build`: improve support for GOARCH=386 and add tests
|
||||
- `gdb`: add support for qemu-user targets
|
||||
- `test`: support non-host tests
|
||||
- `test`: add support for -c and -o flags
|
||||
- `test`: implement some benchmark stubs
|
||||
* **compiler**
|
||||
- `builder`: improve detection of clang on Fedora
|
||||
- `compiler`: fix floating point comparison bugs
|
||||
- `compiler`: implement negate for complex numbers
|
||||
- `loader`: fix linkname in test binaries
|
||||
- `transform`: add missing return pointer restore for regular coroutine tail
|
||||
calls
|
||||
* **standard library**
|
||||
- `machine`: switch default frequency to 4MHz
|
||||
- `machine`: clarify caller's responsibility in `SetInterrupt`
|
||||
- `os`: add `LookupEnv()` stub
|
||||
- `reflect`: implement `Swapper`
|
||||
- `runtime`: fix UTF-8 decoding
|
||||
- `runtime`: gc: use raw stack access whenever possible
|
||||
- `runtime`: use dedicated printfloat32
|
||||
- `runtime`: allow ranging over a nil map
|
||||
- `runtime`: avoid device/nxp dependency in HardFault handler
|
||||
- `testing`: implement dummy Helper method
|
||||
- `testing`: add Run method
|
||||
* **targets**
|
||||
- `arm64`: add support for SVCall intrinsic
|
||||
- `atsamd51`: avoid panic when configuring SPI with SDI=NoPin
|
||||
- `avr`: properly support the `.rodata` section
|
||||
- `esp8266`: implement `Pin.Get` function
|
||||
- `nintendoswitch`: fix crash when printing long lines (> 120)
|
||||
- `nintendoswitch`: add env parser and removed unused stuff
|
||||
- `nrf`: add I2C error checking
|
||||
- `nrf`: give more flexibility in picking SPI speeds
|
||||
- `nrf`: fix nrf52832 flash size
|
||||
- `stm32f103`: support wakeups from interrupts
|
||||
- `stm32f405`: add SPI support
|
||||
- `stm32f405`: add I2C support
|
||||
- `wasi`: add support for this target
|
||||
- `wasi`: use 'generic' ABI by default
|
||||
- `wasi`: remove --no-threads flag from wasm-ld
|
||||
- `wasm`: add instanceof support for WebAssembly
|
||||
- `wasm`: use fixed length buffer for putchar
|
||||
* **boards**
|
||||
- `d1mini`: add this ESP8266 based board
|
||||
- `esp32`: use board definitions instead of chip names
|
||||
- `qtpy`: add board definition for Adafruit QTPy
|
||||
- `teensy40`: add this board
|
||||
|
||||
0.15.0
|
||||
---
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
# TinyGo base stage installs Go 1.14, LLVM 10 and the TinyGo compiler itself.
|
||||
FROM golang:1.14 AS tinygo-base
|
||||
# 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
|
||||
|
||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
|
||||
|
||||
@@ -9,25 +9,10 @@ CLANG_SRC ?= $(LLVM_PROJECTDIR)/clang
|
||||
LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
|
||||
|
||||
# Try to autodetect LLVM build tools.
|
||||
ifneq (, $(shell command -v llvm-build/bin/clang 2> /dev/null))
|
||||
CLANG ?= $(abspath llvm-build/bin/clang)
|
||||
else
|
||||
CLANG ?= clang-10
|
||||
endif
|
||||
ifneq (, $(shell command -v llvm-build/bin/llvm-ar 2> /dev/null))
|
||||
LLVM_AR ?= $(abspath llvm-build/bin/llvm-ar)
|
||||
else ifneq (, $(shell command -v llvm-ar-10 2> /dev/null))
|
||||
LLVM_AR ?= llvm-ar-10
|
||||
else
|
||||
LLVM_AR ?= llvm-ar
|
||||
endif
|
||||
ifneq (, $(shell command -v llvm-build/bin/llvm-nm 2> /dev/null))
|
||||
LLVM_NM ?= $(abspath llvm-build/bin/llvm-nm)
|
||||
else ifneq (, $(shell command -v llvm-nm-10 2> /dev/null))
|
||||
LLVM_NM ?= llvm-nm-10
|
||||
else
|
||||
LLVM_NM ?= llvm-nm
|
||||
endif
|
||||
detect = $(shell command -v $(1) 2> /dev/null && echo $(1))
|
||||
CLANG ?= $(word 1,$(abspath $(call detect,llvm-build/bin/clang))$(call detect,clang-11)$(call detect,clang-10)$(call detect,clang))
|
||||
LLVM_AR ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-ar))$(call detect,llvm-ar-11)$(call detect,llvm-ar-10)$(call detect,llvm-ar))
|
||||
LLVM_NM ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-nm))$(call detect,llvm-nm-11)$(call detect,llvm-nm-10)$(call detect,llvm-nm))
|
||||
|
||||
# Go binary and GOROOT to select
|
||||
GO ?= go
|
||||
@@ -37,7 +22,7 @@ export GOROOT = $(shell $(GO) env GOROOT)
|
||||
MD5SUM = md5sum
|
||||
|
||||
# tinygo binary for tests
|
||||
TINYGO ?= tinygo
|
||||
TINYGO ?= $(word 1,$(call detect,tinygo)$(call detect,build/tinygo))
|
||||
|
||||
# Use CCACHE for LLVM if possible
|
||||
ifneq (, $(shell command -v ccache 2> /dev/null))
|
||||
@@ -63,23 +48,13 @@ ifeq ($(OS),Windows_NT)
|
||||
# LLVM compiled using MinGW on Windows appears to have problems with threads.
|
||||
# Without this flag, linking results in errors like these:
|
||||
# libLLVMSupport.a(Threading.cpp.obj):Threading.cpp:(.text+0x55): undefined reference to `std::thread::hardware_concurrency()'
|
||||
LLVM_OPTION += -DLLVM_ENABLE_THREADS=OFF
|
||||
LLVM_OPTION += -DLLVM_ENABLE_THREADS=OFF -DLLVM_ENABLE_PIC=OFF
|
||||
|
||||
CGO_CPPFLAGS += -DCINDEX_NO_EXPORTS
|
||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||
CGO_LDFLAGS_EXTRA += -lversion
|
||||
|
||||
# Build libclang manually because the CMake-based build system on Windows
|
||||
# doesn't allow building libclang as a static library.
|
||||
LIBCLANG_PATH = $(abspath build/libclang-custom.a)
|
||||
LIBCLANG_FILES = $(abspath $(wildcard $(LLVM_BUILDDIR)/tools/clang/tools/libclang/CMakeFiles/libclang.dir/*.cpp.obj))
|
||||
|
||||
# Add the libclang dependency to the tinygo binary target.
|
||||
tinygo: $(LIBCLANG_PATH)
|
||||
test: $(LIBCLANG_PATH)
|
||||
# Build libclang.
|
||||
$(LIBCLANG_PATH): $(LIBCLANG_FILES)
|
||||
@mkdir -p build
|
||||
ar rcs $(LIBCLANG_PATH) $^
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/liblibclang.a
|
||||
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
MD5SUM = md5
|
||||
@@ -102,9 +77,9 @@ LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++14
|
||||
CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++14 -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
|
||||
|
||||
|
||||
@@ -155,13 +130,13 @@ gen-device-kendryte: build/gen-device-svd
|
||||
GO111MODULE=off $(GO) fmt ./src/device/kendryte
|
||||
|
||||
gen-device-stm32: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro lib/cmsis-svd/data/STMicro/ src/device/stm32/
|
||||
./build/gen-device-svd -source=https://github.com/tinygo-org/stm32-svd lib/stm32-svd/svd src/device/stm32/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/README.md:
|
||||
git clone -b xtensa_release_10.0.1 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/README.md
|
||||
|
||||
# Configure LLVM.
|
||||
@@ -188,16 +163,27 @@ tinygo:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||
|
||||
test: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./interp ./transform .
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: do this in one command, parallelize, and only show failing tests (no
|
||||
# implied -v flag).
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
$(TINYGO) test container/heap
|
||||
$(TINYGO) test container/list
|
||||
$(TINYGO) test container/ring
|
||||
$(TINYGO) test crypto/des
|
||||
$(TINYGO) test encoding/ascii85
|
||||
$(TINYGO) test encoding/base32
|
||||
$(TINYGO) test encoding/hex
|
||||
$(TINYGO) test hash/adler32
|
||||
$(TINYGO) test hash/fnv
|
||||
$(TINYGO) test hash/crc64
|
||||
$(TINYGO) test math
|
||||
$(TINYGO) test math/cmplx
|
||||
$(TINYGO) test text/scanner
|
||||
$(TINYGO) test unicode/utf8
|
||||
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
@@ -223,6 +209,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
||||
@@ -234,8 +222,6 @@ smoketest:
|
||||
# test simulated boards on play.tinygo.org
|
||||
$(TINYGO) build -o test.wasm -tags=arduino examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -o test.wasm -tags=hifive1-qemu examples/serial
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -o test.wasm -tags=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -o test.wasm -tags=reelboard examples/blinky1
|
||||
@@ -287,7 +273,7 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=clue_alpha examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=clue-alpha examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
|
||||
@$(MD5SUM) test.gba
|
||||
@@ -311,6 +297,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=lgt92 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=x9pro examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
|
||||
@@ -331,6 +319,16 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=qtpy examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=teensy36 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -357,13 +355,15 @@ ifneq ($(AVR), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(XTENSA), 0)
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-wroom-32 examples/blinky1
|
||||
$(TINYGO) build -size short -o test.bin -target=esp32-mini32 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
|
||||
|
||||
@@ -86,7 +86,7 @@ The following 44 microcontroller boards are currently supported:
|
||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
|
||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
|
||||
|
||||
@@ -148,6 +148,6 @@ The original reasoning was: if [Python](https://micropython.org/) can run on mic
|
||||
|
||||
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
|
||||
|
||||
Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/10.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files.
|
||||
Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/11.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files.
|
||||
|
||||
Some code has been copied and/or ported from Paul Stoffregen's Teensy libraries and is therefore licensed under PJRC's license. This has been clearly indicated in the header of these files.
|
||||
|
||||
+6
-2
@@ -18,7 +18,7 @@ jobs:
|
||||
- task: Cache@2
|
||||
displayName: Cache LLVM source
|
||||
inputs:
|
||||
key: llvm-source-10-windows-v1
|
||||
key: llvm-source-11-windows-v1
|
||||
path: llvm-project
|
||||
- task: Bash@3
|
||||
displayName: Download LLVM source
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
- task: CacheBeta@0
|
||||
displayName: Cache LLVM build
|
||||
inputs:
|
||||
key: llvm-build-10-windows-v1
|
||||
key: llvm-build-11-windows-v3
|
||||
path: llvm-build
|
||||
- task: Bash@3
|
||||
displayName: Build LLVM
|
||||
@@ -41,7 +41,11 @@ jobs:
|
||||
script: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# install dependencies
|
||||
choco install ninja
|
||||
# hack ninja to use fewer jobs
|
||||
echo -e 'C:\\ProgramData\\Chocolatey\\bin\\ninja -j4 %*' > /usr/bin/ninja.bat
|
||||
# build!
|
||||
make llvm-build
|
||||
fi
|
||||
- task: Bash@3
|
||||
|
||||
+43
-23
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
"github.com/tinygo-org/tinygo/compiler"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/interp"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"github.com/tinygo-org/tinygo/stacksize"
|
||||
"github.com/tinygo-org/tinygo/transform"
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -43,16 +45,31 @@ type BuildResult struct {
|
||||
// The error value may be of type *MultiError. Callers will likely want to check
|
||||
// for this case and print such errors individually.
|
||||
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
||||
// Compile Go code to IR.
|
||||
// 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(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buildOutput, errs := compiler.Compile(pkgName, machine, config)
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
||||
Sizes: compiler.Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Compile AST to IR.
|
||||
mod, errs := compiler.CompileProgram(pkgName, lprogram, machine, config)
|
||||
if errs != nil {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
mod := buildOutput.Mod
|
||||
|
||||
if config.Options.PrintIR {
|
||||
fmt.Println("; Generated LLVM IR:")
|
||||
@@ -79,7 +96,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// keep functions interoperable, pass int64 types as pointers to
|
||||
// stack-allocated values.
|
||||
// Use -wasm-abi=generic to disable this behaviour.
|
||||
if config.Options.WasmAbi == "js" && strings.HasPrefix(config.Triple(), "wasm") {
|
||||
if config.WasmAbi() == "js" {
|
||||
err := transform.ExternalInt64AsPtr(mod)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -117,16 +134,15 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return errors.New("verification failure after LLVM optimization passes")
|
||||
}
|
||||
|
||||
// On the AVR, pointers can point either to flash or to RAM, but we don't
|
||||
// know. As a temporary fix, load all global variables in RAM.
|
||||
// In the future, there should be a compiler pass that determines which
|
||||
// pointers are flash and which are in RAM so that pointers can have a
|
||||
// correct address space parameter (address space 1 is for flash).
|
||||
if strings.HasPrefix(config.Triple(), "avr") {
|
||||
transform.NonConstGlobals(mod)
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return errors.New("verification error after making all globals non-constant on AVR")
|
||||
}
|
||||
// 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
|
||||
@@ -209,17 +225,21 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
}
|
||||
|
||||
// Compile C files in packages.
|
||||
for i, file := range buildOutput.ExtraFiles {
|
||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", file, err}
|
||||
// Gather the list of (C) file paths that should be included in the build.
|
||||
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")
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", file, err}
|
||||
}
|
||||
ldflags = append(ldflags, outpath)
|
||||
}
|
||||
ldflags = append(ldflags, outpath)
|
||||
}
|
||||
|
||||
if len(buildOutput.ExtraLDFlags) > 0 {
|
||||
ldflags = append(ldflags, buildOutput.ExtraLDFlags...)
|
||||
if len(lprogram.LDFlags) > 0 {
|
||||
ldflags = append(ldflags, lprogram.LDFlags...)
|
||||
}
|
||||
|
||||
// Link the object files together.
|
||||
@@ -305,7 +325,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
}
|
||||
return action(BuildResult{
|
||||
Binary: tmppath,
|
||||
MainDir: buildOutput.MainDir,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+21
-18
@@ -101,7 +101,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
|
||||
// Target Options
|
||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||
Opts.CPU = Args.getLastArgValue(OPT_target_cpu);
|
||||
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
|
||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||
|
||||
// Use the default target triple if unspecified.
|
||||
@@ -132,13 +132,19 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
|
||||
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
|
||||
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
||||
Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags);
|
||||
Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer);
|
||||
Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir);
|
||||
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
|
||||
Opts.DwarfDebugFlags =
|
||||
std::string(Args.getLastArgValue(OPT_dwarf_debug_flags));
|
||||
Opts.DwarfDebugProducer =
|
||||
std::string(Args.getLastArgValue(OPT_dwarf_debug_producer));
|
||||
Opts.DebugCompilationDir =
|
||||
std::string(Args.getLastArgValue(OPT_fdebug_compilation_dir));
|
||||
Opts.MainFileName = std::string(Args.getLastArgValue(OPT_main_file_name));
|
||||
|
||||
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ))
|
||||
Opts.DebugPrefixMap.insert(StringRef(Arg).split('='));
|
||||
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) {
|
||||
auto Split = StringRef(Arg).split('=');
|
||||
Opts.DebugPrefixMap.insert(
|
||||
{std::string(Split.first), std::string(Split.second)});
|
||||
}
|
||||
|
||||
// Frontend Options
|
||||
if (Args.hasArg(OPT_INPUT)) {
|
||||
@@ -154,8 +160,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
}
|
||||
}
|
||||
Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm);
|
||||
Opts.OutputPath = Args.getLastArgValue(OPT_o);
|
||||
Opts.SplitDwarfOutput = Args.getLastArgValue(OPT_split_dwarf_output);
|
||||
Opts.OutputPath = std::string(Args.getLastArgValue(OPT_o));
|
||||
Opts.SplitDwarfOutput =
|
||||
std::string(Args.getLastArgValue(OPT_split_dwarf_output));
|
||||
if (Arg *A = Args.getLastArg(OPT_filetype)) {
|
||||
StringRef Name = A->getValue();
|
||||
unsigned OutputType = StringSwitch<unsigned>(Name)
|
||||
@@ -183,8 +190,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
|
||||
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
|
||||
Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn);
|
||||
Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic");
|
||||
Opts.TargetABI = Args.getLastArgValue(OPT_target_abi);
|
||||
Opts.RelocationModel =
|
||||
std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic"));
|
||||
Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi));
|
||||
Opts.IncrementalLinkerCompatible =
|
||||
Args.hasArg(OPT_mincremental_linker_compatible);
|
||||
Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym);
|
||||
@@ -314,12 +322,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
SrcMgr.getMemoryBuffer(BufferIndex)->getBuffer());
|
||||
|
||||
// Build up the feature string from the target feature list.
|
||||
std::string FS;
|
||||
if (!Opts.Features.empty()) {
|
||||
FS = Opts.Features[0];
|
||||
for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i)
|
||||
FS += "," + Opts.Features[i];
|
||||
}
|
||||
std::string FS = llvm::join(Opts.Features, ",");
|
||||
|
||||
std::unique_ptr<MCStreamer> Str;
|
||||
|
||||
@@ -383,7 +386,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
MCSection *AsmLabel = Ctx.getMachOSection(
|
||||
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
|
||||
Str.get()->SwitchSection(AsmLabel);
|
||||
Str.get()->EmitZeros(1);
|
||||
Str.get()->emitZeros(1);
|
||||
}
|
||||
|
||||
// Assembly to object compilation should leverage assembly info.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <clang/FrontendTool/Utils.h>
|
||||
#include <llvm/ADT/IntrusiveRefCntPtr.h>
|
||||
#include <llvm/Option/Option.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
+29
-9
@@ -6,6 +6,9 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
|
||||
@@ -26,6 +29,7 @@ func getClangHeaderPath(TINYGOROOT string) string {
|
||||
|
||||
// It looks like we are built with a system-installed LLVM. Do a last
|
||||
// attempt: try to use Clang headers relative to the clang binary.
|
||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
||||
for _, cmdName := range commands["clang"] {
|
||||
binpath, err := exec.LookPath(cmdName)
|
||||
if err == nil {
|
||||
@@ -40,22 +44,38 @@ func getClangHeaderPath(TINYGOROOT string) string {
|
||||
// Example executable:
|
||||
// /usr/lib/llvm-9/bin/clang
|
||||
// Example include path:
|
||||
// /usr/lib/llvm-9/lib/clang/9.0.1/include/
|
||||
// /usr/lib/llvm-9/lib64/clang/9.0.1/include/
|
||||
llvmRoot := filepath.Dir(filepath.Dir(binpath))
|
||||
clangVersionRoot := filepath.Join(llvmRoot, "lib", "clang")
|
||||
dirs, err := ioutil.ReadDir(clangVersionRoot)
|
||||
if err != nil {
|
||||
clangVersionRoot := filepath.Join(llvmRoot, "lib64", "clang")
|
||||
dirs64, err64 := ioutil.ReadDir(clangVersionRoot)
|
||||
// Example include path:
|
||||
// /usr/lib/llvm-9/lib/clang/9.0.1/include/
|
||||
clangVersionRoot = filepath.Join(llvmRoot, "lib", "clang")
|
||||
dirs32, err32 := ioutil.ReadDir(clangVersionRoot)
|
||||
if err64 != nil && err32 != nil {
|
||||
// Unexpected.
|
||||
continue
|
||||
}
|
||||
dirnames := make([]string, len(dirs))
|
||||
for i, d := range dirs {
|
||||
dirnames[i] = d.Name()
|
||||
dirnames := make([]string, len(dirs64)+len(dirs32))
|
||||
dirCount := 0
|
||||
for _, d := range dirs32 {
|
||||
name := d.Name()
|
||||
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
|
||||
dirnames[dirCount] = filepath.Join(llvmRoot, "lib", "clang", name)
|
||||
dirCount++
|
||||
}
|
||||
}
|
||||
for _, d := range dirs64 {
|
||||
name := d.Name()
|
||||
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
|
||||
dirnames[dirCount] = filepath.Join(llvmRoot, "lib64", "clang", name)
|
||||
dirCount++
|
||||
}
|
||||
}
|
||||
sort.Strings(dirnames)
|
||||
// Check for the highest version first.
|
||||
for i := len(dirnames) - 1; i >= 0; i-- {
|
||||
path := filepath.Join(clangVersionRoot, dirnames[i], "include")
|
||||
for i := dirCount - 1; i >= 0; i-- {
|
||||
path := filepath.Join(dirnames[i], "include")
|
||||
_, err := os.Stat(filepath.Join(path, "stdint.h"))
|
||||
if err == nil {
|
||||
return path
|
||||
|
||||
+7
-1
@@ -61,7 +61,7 @@ func extractROM(path string) (uint64, []byte, error) {
|
||||
|
||||
progs := make(progSlice, 0, 2)
|
||||
for _, prog := range f.Progs {
|
||||
if prog.Type != elf.PT_LOAD || prog.Filesz == 0 {
|
||||
if prog.Type != elf.PT_LOAD || prog.Filesz == 0 || prog.Off == 0 {
|
||||
continue
|
||||
}
|
||||
progs = append(progs, prog)
|
||||
@@ -73,6 +73,12 @@ func extractROM(path string) (uint64, []byte, error) {
|
||||
|
||||
var rom []byte
|
||||
for _, prog := range progs {
|
||||
romEnd := progs[0].Paddr + uint64(len(rom))
|
||||
if prog.Paddr > romEnd && prog.Paddr < romEnd+16 {
|
||||
// Sometimes, the linker seems to insert a bit of padding between
|
||||
// segments. Simply zero-fill these parts.
|
||||
rom = append(rom, make([]byte, prog.Paddr-romEnd)...)
|
||||
}
|
||||
if prog.Paddr != progs[0].Paddr+uint64(len(rom)) {
|
||||
diff := prog.Paddr - (progs[0].Paddr + uint64(len(rom)))
|
||||
if diff > maxPadBytes {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// +build !byollvm
|
||||
// +build !llvm9
|
||||
// +build !llvm9,!llvm11
|
||||
|
||||
package cgo
|
||||
|
||||
|
||||
@@ -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"
|
||||
+15
-10
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
@@ -118,12 +117,12 @@ func (c *Config) NeedsStackObjects() bool {
|
||||
switch c.GC() {
|
||||
case "conservative", "extalloc":
|
||||
for _, tag := range c.BuildTags() {
|
||||
if tag == "baremetal" {
|
||||
return false
|
||||
if tag == "wasm" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -203,12 +202,6 @@ func (c *Config) LDFlags() []string {
|
||||
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1))
|
||||
}
|
||||
ldflags = append(ldflags, "-L", root)
|
||||
if c.Target.GOARCH == "wasm" {
|
||||
// Round heap size to next multiple of 65536 (the WebAssembly page
|
||||
// size).
|
||||
heapSize := (c.Options.HeapSize + (65536 - 1)) &^ (65536 - 1)
|
||||
ldflags = append(ldflags, "--initial-memory="+strconv.FormatInt(heapSize, 10))
|
||||
}
|
||||
if c.Target.LinkerScript != "" {
|
||||
ldflags = append(ldflags, "-T", c.Target.LinkerScript)
|
||||
}
|
||||
@@ -303,6 +296,9 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
|
||||
return nil, fmt.Errorf("unknown OpenOCD transport: %#v", c.Target.OpenOCDTransport)
|
||||
}
|
||||
args = []string{"-f", "interface/" + openocdInterface + ".cfg"}
|
||||
for _, cmd := range c.Target.OpenOCDCommands {
|
||||
args = append(args, "-c", cmd)
|
||||
}
|
||||
if c.Target.OpenOCDTransport != "" {
|
||||
args = append(args, "-c", "transport select "+c.Target.OpenOCDTransport)
|
||||
}
|
||||
@@ -329,6 +325,15 @@ func (c *Config) RelocationModel() string {
|
||||
return "static"
|
||||
}
|
||||
|
||||
// WasmAbi returns the WASM ABI which is specified in the target JSON file, and
|
||||
// the value is overridden by `-wasm-abi` flag if it is provided
|
||||
func (c *Config) WasmAbi() string {
|
||||
if c.Options.WasmAbi != "" {
|
||||
return c.Options.WasmAbi
|
||||
}
|
||||
return c.Target.WasmAbi
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
CompileTestBinary bool
|
||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||
|
||||
@@ -23,6 +23,7 @@ type Options struct {
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands bool
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintStacks bool
|
||||
@@ -30,7 +31,6 @@ type Options struct {
|
||||
LDFlags []string
|
||||
Tags string
|
||||
WasmAbi string
|
||||
HeapSize int64
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
}
|
||||
|
||||
+62
-111
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@@ -39,7 +40,7 @@ type TargetSpec struct {
|
||||
LDFlags []string `json:"ldflags"`
|
||||
LinkerScript string `json:"linkerscript"`
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
Emulator []string `json:"emulator"`
|
||||
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
|
||||
FlashCommand string `json:"flash-command"`
|
||||
GDB string `json:"gdb"`
|
||||
PortReset string `json:"flash-1200-bps-reset"`
|
||||
@@ -51,105 +52,53 @@ type TargetSpec struct {
|
||||
OpenOCDInterface string `json:"openocd-interface"`
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
OpenOCDCommands []string `json:"openocd-commands"`
|
||||
JLinkDevice string `json:"jlink-device"`
|
||||
CodeModel string `json:"code-model"`
|
||||
RelocationModel string `json:"relocation-model"`
|
||||
WasmAbi string `json:"wasm-abi"`
|
||||
}
|
||||
|
||||
// copyProperties copies all properties that are set in spec2 into itself.
|
||||
func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
||||
// TODO: simplify this using reflection? Inherits and BuildTags are special
|
||||
// cases, but the rest can simply be copied if set.
|
||||
spec.Inherits = append(spec.Inherits, spec2.Inherits...)
|
||||
if spec2.Triple != "" {
|
||||
spec.Triple = spec2.Triple
|
||||
}
|
||||
if spec2.CPU != "" {
|
||||
spec.CPU = spec2.CPU
|
||||
}
|
||||
spec.Features = append(spec.Features, spec2.Features...)
|
||||
if spec2.GOOS != "" {
|
||||
spec.GOOS = spec2.GOOS
|
||||
}
|
||||
if spec2.GOARCH != "" {
|
||||
spec.GOARCH = spec2.GOARCH
|
||||
}
|
||||
spec.BuildTags = append(spec.BuildTags, spec2.BuildTags...)
|
||||
if spec2.GC != "" {
|
||||
spec.GC = spec2.GC
|
||||
}
|
||||
if spec2.Scheduler != "" {
|
||||
spec.Scheduler = spec2.Scheduler
|
||||
}
|
||||
if spec2.Compiler != "" {
|
||||
spec.Compiler = spec2.Compiler
|
||||
}
|
||||
if spec2.Linker != "" {
|
||||
spec.Linker = spec2.Linker
|
||||
}
|
||||
if spec2.RTLib != "" {
|
||||
spec.RTLib = spec2.RTLib
|
||||
}
|
||||
if spec2.Libc != "" {
|
||||
spec.Libc = spec2.Libc
|
||||
}
|
||||
if spec2.AutoStackSize != nil {
|
||||
spec.AutoStackSize = spec2.AutoStackSize
|
||||
}
|
||||
if spec2.DefaultStackSize != 0 {
|
||||
spec.DefaultStackSize = spec2.DefaultStackSize
|
||||
}
|
||||
spec.CFlags = append(spec.CFlags, spec2.CFlags...)
|
||||
spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...)
|
||||
if spec2.LinkerScript != "" {
|
||||
spec.LinkerScript = spec2.LinkerScript
|
||||
}
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, spec2.ExtraFiles...)
|
||||
if len(spec2.Emulator) != 0 {
|
||||
spec.Emulator = spec2.Emulator
|
||||
}
|
||||
if spec2.FlashCommand != "" {
|
||||
spec.FlashCommand = spec2.FlashCommand
|
||||
}
|
||||
if spec2.GDB != "" {
|
||||
spec.GDB = spec2.GDB
|
||||
}
|
||||
if spec2.PortReset != "" {
|
||||
spec.PortReset = spec2.PortReset
|
||||
}
|
||||
if spec2.FlashMethod != "" {
|
||||
spec.FlashMethod = spec2.FlashMethod
|
||||
}
|
||||
if spec2.FlashVolume != "" {
|
||||
spec.FlashVolume = spec2.FlashVolume
|
||||
}
|
||||
if spec2.FlashFilename != "" {
|
||||
spec.FlashFilename = spec2.FlashFilename
|
||||
}
|
||||
if spec2.UF2FamilyID != "" {
|
||||
spec.UF2FamilyID = spec2.UF2FamilyID
|
||||
}
|
||||
if spec2.BinaryFormat != "" {
|
||||
spec.BinaryFormat = spec2.BinaryFormat
|
||||
}
|
||||
if spec2.OpenOCDInterface != "" {
|
||||
spec.OpenOCDInterface = spec2.OpenOCDInterface
|
||||
}
|
||||
if spec2.OpenOCDTarget != "" {
|
||||
spec.OpenOCDTarget = spec2.OpenOCDTarget
|
||||
}
|
||||
if spec2.OpenOCDTransport != "" {
|
||||
spec.OpenOCDTransport = spec2.OpenOCDTransport
|
||||
}
|
||||
if spec2.JLinkDevice != "" {
|
||||
spec.JLinkDevice = spec2.JLinkDevice
|
||||
}
|
||||
if spec2.CodeModel != "" {
|
||||
spec.CodeModel = spec2.CodeModel
|
||||
}
|
||||
// overrideProperties overrides all properties that are set in child into itself using reflection.
|
||||
func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
|
||||
specType := reflect.TypeOf(spec).Elem()
|
||||
specValue := reflect.ValueOf(spec).Elem()
|
||||
childValue := reflect.ValueOf(child).Elem()
|
||||
|
||||
if spec2.RelocationModel != "" {
|
||||
spec.RelocationModel = spec2.RelocationModel
|
||||
for i := 0; i < specType.NumField(); i++ {
|
||||
field := specType.Field(i)
|
||||
src := childValue.Field(i)
|
||||
dst := specValue.Field(i)
|
||||
|
||||
switch kind := field.Type.Kind(); kind {
|
||||
case reflect.String: // for strings, just copy the field of child to spec if not empty
|
||||
if src.Len() > 0 {
|
||||
dst.Set(src)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint32, reflect.Uint64: // for Uint, copy if not zero
|
||||
if src.Uint() != 0 {
|
||||
dst.Set(src)
|
||||
}
|
||||
case reflect.Ptr: // for pointers, copy if not nil
|
||||
if !src.IsNil() {
|
||||
dst.Set(src)
|
||||
}
|
||||
case reflect.Slice: // for slices...
|
||||
if src.Len() > 0 { // ... if not empty ...
|
||||
switch tag := field.Tag.Get("override"); tag {
|
||||
case "copy":
|
||||
// copy the field of child to spec
|
||||
dst.Set(src)
|
||||
case "append", "":
|
||||
// or append the field of child to spec
|
||||
dst.Set(reflect.AppendSlice(src, dst))
|
||||
default:
|
||||
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic("unknown field type : " + kind.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,11 +147,11 @@ func (spec *TargetSpec) resolveInherits() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newSpec.copyProperties(subtarget)
|
||||
newSpec.overrideProperties(subtarget)
|
||||
}
|
||||
|
||||
// When all properties are loaded, make sure they are properly inherited.
|
||||
newSpec.copyProperties(spec)
|
||||
newSpec.overrideProperties(spec)
|
||||
*spec = *newSpec
|
||||
|
||||
return nil
|
||||
@@ -268,6 +217,7 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
||||
}
|
||||
goarch := map[string]string{ // map from LLVM arch to Go arch
|
||||
"i386": "386",
|
||||
"i686": "386",
|
||||
"x86_64": "amd64",
|
||||
"aarch64": "arm64",
|
||||
"armv7": "arm",
|
||||
@@ -283,39 +233,40 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// No target spec available. Use the default one, useful on most systems
|
||||
// with a regular OS.
|
||||
spec := TargetSpec{
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: "clang",
|
||||
Linker: "cc",
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: "gdb",
|
||||
PortReset: "false",
|
||||
FlashMethod: "native",
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: "clang",
|
||||
Linker: "cc",
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: "gdb",
|
||||
PortReset: "false",
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
if goarch != "wasm" {
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S")
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
spec.GDB = "gdb-multiarch"
|
||||
if goarch == "arm" && goos == "linux" {
|
||||
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
|
||||
spec.Linker = "arm-linux-gnueabihf-gcc"
|
||||
spec.GDB = "arm-linux-gnueabihf-gdb"
|
||||
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
|
||||
}
|
||||
if goarch == "arm64" && goos == "linux" {
|
||||
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/aarch64-linux-gnu")
|
||||
spec.Linker = "aarch64-linux-gnu-gcc"
|
||||
spec.GDB = "aarch64-linux-gnu-gdb"
|
||||
spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"}
|
||||
}
|
||||
if goarch == "386" {
|
||||
spec.CFlags = []string{"-m32"}
|
||||
spec.LDFlags = []string{"-m32"}
|
||||
if goarch == "386" && runtime.GOARCH == "amd64" {
|
||||
spec.CFlags = append(spec.CFlags, "-m32")
|
||||
spec.LDFlags = append(spec.LDFlags, "-m32")
|
||||
}
|
||||
}
|
||||
return &spec, nil
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package compileopts
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadTarget(t *testing.T) {
|
||||
_, err := LoadTarget("arduino")
|
||||
@@ -17,3 +20,67 @@ func TestLoadTarget(t *testing.T) {
|
||||
t.Error("LoadTarget failed for wrong reason:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverrideProperties(t *testing.T) {
|
||||
baseAutoStackSize := true
|
||||
base := &TargetSpec{
|
||||
GOOS: "baseGoos",
|
||||
CPU: "baseCpu",
|
||||
Features: []string{"bf1", "bf2"},
|
||||
BuildTags: []string{"bt1", "bt2"},
|
||||
Emulator: []string{"be1", "be2"},
|
||||
DefaultStackSize: 42,
|
||||
AutoStackSize: &baseAutoStackSize,
|
||||
}
|
||||
childAutoStackSize := false
|
||||
child := &TargetSpec{
|
||||
GOOS: "",
|
||||
CPU: "chlidCpu",
|
||||
Features: []string{"cf1", "cf2"},
|
||||
Emulator: []string{"ce1", "ce2"},
|
||||
AutoStackSize: &childAutoStackSize,
|
||||
DefaultStackSize: 64,
|
||||
}
|
||||
|
||||
base.overrideProperties(child)
|
||||
|
||||
if base.GOOS != "baseGoos" {
|
||||
t.Errorf("Overriding failed : got %v", base.GOOS)
|
||||
}
|
||||
if base.CPU != "chlidCpu" {
|
||||
t.Errorf("Overriding failed : got %v", base.CPU)
|
||||
}
|
||||
if !reflect.DeepEqual(base.Features, []string{"cf1", "cf2", "bf1", "bf2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.Features)
|
||||
}
|
||||
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.BuildTags)
|
||||
}
|
||||
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
|
||||
t.Errorf("Overriding failed : got %v", base.Emulator)
|
||||
}
|
||||
if *base.AutoStackSize != false {
|
||||
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
|
||||
}
|
||||
if base.DefaultStackSize != 64 {
|
||||
t.Errorf("Overriding failed : got %v", base.DefaultStackSize)
|
||||
}
|
||||
|
||||
baseAutoStackSize = true
|
||||
base = &TargetSpec{
|
||||
AutoStackSize: &baseAutoStackSize,
|
||||
DefaultStackSize: 42,
|
||||
}
|
||||
child = &TargetSpec{
|
||||
AutoStackSize: nil,
|
||||
DefaultStackSize: 0,
|
||||
}
|
||||
base.overrideProperties(child)
|
||||
if *base.AutoStackSize != true {
|
||||
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
|
||||
}
|
||||
if base.DefaultStackSize != 42 {
|
||||
t.Errorf("Overriding failed : got %v", base.DefaultStackSize)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+157
-97
@@ -9,6 +9,7 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -53,6 +54,45 @@ type compilerContext struct {
|
||||
astComments map[string]*ast.CommentGroup
|
||||
}
|
||||
|
||||
// newCompilerContext returns a new compiler context ready for use, most
|
||||
// importantly with a newly created LLVM context and module.
|
||||
func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *compileopts.Config) *compilerContext {
|
||||
c := &compilerContext{
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
machine: machine,
|
||||
targetData: machine.CreateTargetData(),
|
||||
}
|
||||
|
||||
c.ctx = llvm.NewContext()
|
||||
c.mod = c.ctx.NewModule(moduleName)
|
||||
c.mod.SetTarget(config.Triple())
|
||||
c.mod.SetDataLayout(c.targetData.String())
|
||||
if c.Debug() {
|
||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||
}
|
||||
|
||||
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
||||
if c.targetData.PointerSize() <= 4 {
|
||||
// 8, 16, 32 bits targets
|
||||
c.intType = c.ctx.Int32Type()
|
||||
} else if c.targetData.PointerSize() == 8 {
|
||||
// 64 bits target
|
||||
c.intType = c.ctx.Int64Type()
|
||||
} else {
|
||||
panic("unknown pointer size")
|
||||
}
|
||||
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
|
||||
|
||||
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
|
||||
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
|
||||
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
||||
dummyFunc.EraseFromParentAsFunction()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// builder contains all information relevant to build a single function.
|
||||
type builder struct {
|
||||
*compilerContext
|
||||
@@ -76,6 +116,18 @@ type builder struct {
|
||||
deferBuiltinFuncs map[ssa.Value]deferBuiltin
|
||||
}
|
||||
|
||||
func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ir.Function) *builder {
|
||||
return &builder{
|
||||
compilerContext: c,
|
||||
Builder: irbuilder,
|
||||
fn: f,
|
||||
locals: make(map[ssa.Value]llvm.Value),
|
||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||
}
|
||||
}
|
||||
|
||||
type deferBuiltin struct {
|
||||
funcName string
|
||||
callback int
|
||||
@@ -127,94 +179,47 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
||||
return machine, nil
|
||||
}
|
||||
|
||||
// CompilerOutput is returned from the Compile() call. It contains the compile
|
||||
// output and information necessary to continue to compile and link the program.
|
||||
type CompilerOutput struct {
|
||||
// The LLVM module that contains the compiled but not optimized LLVM module
|
||||
// for all the Go code in the program.
|
||||
Mod llvm.Module
|
||||
// Sizes returns a types.Sizes appropriate for the given target machine. It
|
||||
// includes the correct int size and aligment as is necessary for the Go
|
||||
// typechecker.
|
||||
func Sizes(machine llvm.TargetMachine) types.Sizes {
|
||||
targetData := machine.CreateTargetData()
|
||||
defer targetData.Dispose()
|
||||
|
||||
// ExtraFiles is a list of C source files included in packages that should
|
||||
// be built and linked together with the main executable to form one
|
||||
// program. They can be used from CGo, for example.
|
||||
ExtraFiles []string
|
||||
|
||||
// ExtraLDFlags are linker flags obtained during CGo processing. These flags
|
||||
// must be passed to the linker which links the entire executable.
|
||||
ExtraLDFlags []string
|
||||
|
||||
// MainDir is the absolute directory path to the directory of the main
|
||||
// package. This is useful for testing: tests must be run in the package
|
||||
// directory that is being tested.
|
||||
MainDir string
|
||||
}
|
||||
|
||||
// Compile the given package path or .go file path. Return an error when this
|
||||
// fails (in any stage). If successful it returns the LLVM module and a list of
|
||||
// extra C files to be compiled. If not, one or more errors will be returned.
|
||||
//
|
||||
// The fact that it returns a list of filenames to compile is a layering
|
||||
// violation. Eventually, this Compile function should only compile a single
|
||||
// package and not the whole program, and loading of the program (including CGo
|
||||
// processing) should be moved outside the compiler package.
|
||||
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (output CompilerOutput, errors []error) {
|
||||
c := &compilerContext{
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
machine: machine,
|
||||
targetData: machine.CreateTargetData(),
|
||||
intPtrType := targetData.IntPtrType()
|
||||
if intPtrType.IntTypeWidth()/8 <= 32 {
|
||||
}
|
||||
|
||||
c.ctx = llvm.NewContext()
|
||||
c.mod = c.ctx.NewModule(pkgName)
|
||||
c.mod.SetTarget(config.Triple())
|
||||
c.mod.SetDataLayout(c.targetData.String())
|
||||
if c.Debug() {
|
||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||
}
|
||||
output.Mod = c.mod
|
||||
|
||||
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
||||
if c.targetData.PointerSize() <= 4 {
|
||||
var intWidth int
|
||||
if targetData.PointerSize() <= 4 {
|
||||
// 8, 16, 32 bits targets
|
||||
c.intType = c.ctx.Int32Type()
|
||||
} else if c.targetData.PointerSize() == 8 {
|
||||
intWidth = 32
|
||||
} else if targetData.PointerSize() == 8 {
|
||||
// 64 bits target
|
||||
c.intType = c.ctx.Int64Type()
|
||||
intWidth = 64
|
||||
} else {
|
||||
panic("unknown pointer size")
|
||||
}
|
||||
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
|
||||
|
||||
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
|
||||
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
|
||||
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
||||
dummyFunc.EraseFromParentAsFunction()
|
||||
|
||||
lprogram, err := loader.Load(c.Config, []string{pkgName}, c.ClangHeaders, types.Config{
|
||||
Sizes: &stdSizes{
|
||||
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
|
||||
PtrSize: int64(c.targetData.PointerSize()),
|
||||
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
||||
}})
|
||||
if err != nil {
|
||||
return output, []error{err}
|
||||
return &stdSizes{
|
||||
IntSize: int64(intWidth / 8),
|
||||
PtrSize: int64(targetData.PointerSize()),
|
||||
MaxAlign: int64(targetData.PrefTypeAlignment(intPtrType)),
|
||||
}
|
||||
}
|
||||
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
return output, []error{err}
|
||||
}
|
||||
output.ExtraLDFlags = lprogram.LDFlags
|
||||
output.MainDir = lprogram.MainPkg().Dir
|
||||
// CompileProgram compiles the given package path or .go file path. Return an
|
||||
// error when this fails (in any stage). If successful it returns the LLVM
|
||||
// module. If not, one or more errors will be returned.
|
||||
func CompileProgram(pkgName string, lprogram *loader.Program, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []error) {
|
||||
c := newCompilerContext(pkgName, machine, config)
|
||||
|
||||
c.ir = ir.NewProgram(lprogram)
|
||||
|
||||
// Run a simple dead code elimination pass.
|
||||
err = c.ir.SimpleDCE()
|
||||
err := c.ir.SimpleDCE()
|
||||
if err != nil {
|
||||
return output, []error{err}
|
||||
return llvm.Module{}, []error{err}
|
||||
}
|
||||
|
||||
// Initialize debug information.
|
||||
@@ -265,15 +270,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||
}
|
||||
|
||||
// Create the function definition.
|
||||
b := builder{
|
||||
compilerContext: c,
|
||||
Builder: irbuilder,
|
||||
fn: f,
|
||||
locals: make(map[ssa.Value]llvm.Value),
|
||||
dilocals: make(map[*types.Var]llvm.Metadata),
|
||||
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
|
||||
}
|
||||
b := newBuilder(c, irbuilder, f)
|
||||
b.createFunctionDefinition()
|
||||
}
|
||||
|
||||
@@ -352,14 +349,64 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
||||
c.dibuilder.Finalize()
|
||||
}
|
||||
|
||||
// Gather the list of (C) file paths that should be included in the build.
|
||||
for _, pkg := range c.ir.LoaderProgram.Sorted() {
|
||||
for _, filename := range pkg.CFiles {
|
||||
output.ExtraFiles = append(output.ExtraFiles, filepath.Join(pkg.Dir, filename))
|
||||
return c.mod, c.diagnostics
|
||||
}
|
||||
|
||||
// CompilePackage compiles a single package to a LLVM module.
|
||||
func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []error) {
|
||||
c := newCompilerContext(moduleName, machine, config)
|
||||
|
||||
// Build SSA from AST.
|
||||
ssaPkg := pkg.LoadSSA()
|
||||
ssaPkg.Build()
|
||||
|
||||
// Sort by position, so that the order of the functions in the IR matches
|
||||
// the order of functions in the source file. This is useful for testing,
|
||||
// for example.
|
||||
var members []string
|
||||
for name := range ssaPkg.Members {
|
||||
members = append(members, name)
|
||||
}
|
||||
sort.Slice(members, func(i, j int) bool {
|
||||
iPos := ssaPkg.Members[members[i]].Pos()
|
||||
jPos := ssaPkg.Members[members[j]].Pos()
|
||||
if i == j {
|
||||
// Cannot sort by pos, so do it by name.
|
||||
return members[i] < members[j]
|
||||
}
|
||||
return iPos < jPos
|
||||
})
|
||||
|
||||
// Create *ir.Functions objects.
|
||||
var functions []*ir.Function
|
||||
for _, name := range members {
|
||||
member := ssaPkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
functions = append(functions, &ir.Function{
|
||||
Function: member,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return output, c.diagnostics
|
||||
// Declare all functions.
|
||||
for _, fn := range functions {
|
||||
c.createFunctionDeclaration(fn)
|
||||
}
|
||||
|
||||
// Add definitions to declarations.
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
defer irbuilder.Dispose()
|
||||
for _, f := range functions {
|
||||
if f.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, f)
|
||||
b.createFunctionDefinition()
|
||||
}
|
||||
|
||||
return c.mod, nil
|
||||
}
|
||||
|
||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||
@@ -1336,12 +1383,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.createMemoryCopyCall(fn, instr.Args)
|
||||
case name == "runtime.memzero":
|
||||
return b.createMemoryZeroCall(instr.Args)
|
||||
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||
return b.createInlineAsm(instr.Args)
|
||||
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||
return b.createInlineAsmFull(instr)
|
||||
case strings.HasPrefix(name, "device/arm.SVCall"):
|
||||
return b.emitSVCall(instr.Args)
|
||||
case strings.HasPrefix(name, "device/arm64.SVCall"):
|
||||
return b.emitSV64Call(instr.Args)
|
||||
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
||||
return b.emitCSROperation(instr)
|
||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||
@@ -1803,6 +1852,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
low,
|
||||
}
|
||||
|
||||
b.createNilCheck(expr.X, value, "slice")
|
||||
b.createSliceBoundsCheck(llvmLen, low, high, max, lowType, highType, maxType)
|
||||
|
||||
// Truncate ints bigger than uintptr. This is after the bounds
|
||||
@@ -2044,17 +2094,17 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
|
||||
case token.QUO: // /
|
||||
return b.CreateFDiv(x, y, ""), nil
|
||||
case token.EQL: // ==
|
||||
return b.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatOEQ, x, y, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return b.CreateFCmp(llvm.FloatUNE, x, y, ""), nil
|
||||
case token.LSS: // <
|
||||
return b.CreateFCmp(llvm.FloatULT, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatOLT, x, y, ""), nil
|
||||
case token.LEQ: // <=
|
||||
return b.CreateFCmp(llvm.FloatULE, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatOLE, x, y, ""), nil
|
||||
case token.GTR: // >
|
||||
return b.CreateFCmp(llvm.FloatUGT, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatOGT, x, y, ""), nil
|
||||
case token.GEQ: // >=
|
||||
return b.CreateFCmp(llvm.FloatUGE, x, y, ""), nil
|
||||
return b.CreateFCmp(llvm.FloatOGE, x, y, ""), nil
|
||||
default:
|
||||
panic("binop on float: " + op.String())
|
||||
}
|
||||
@@ -2318,15 +2368,15 @@ func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
if !expr.IsNil() {
|
||||
value, _ := constant.Uint64Val(expr.Value)
|
||||
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||
return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
|
||||
}
|
||||
return llvm.ConstNull(b.i8ptrType)
|
||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||
n, _ := constant.Uint64Val(expr.Value)
|
||||
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
|
||||
return llvm.ConstInt(llvmType, n, false)
|
||||
} else if typ.Info()&types.IsInteger != 0 { // signed
|
||||
n, _ := constant.Int64Val(expr.Value)
|
||||
n, _ := constant.Int64Val(constant.ToInt(expr.Value))
|
||||
return llvm.ConstInt(llvmType, uint64(n), true)
|
||||
} else if typ.Info()&types.IsFloat != 0 {
|
||||
n, _ := constant.Float64Val(expr.Value)
|
||||
@@ -2578,7 +2628,17 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
|
||||
if typ.Info()&types.IsInteger != 0 {
|
||||
return b.CreateSub(llvm.ConstInt(x.Type(), 0, false), x, ""), nil
|
||||
} else if typ.Info()&types.IsFloat != 0 {
|
||||
return b.CreateFSub(llvm.ConstFloat(x.Type(), 0.0), x, ""), nil
|
||||
return b.CreateFNeg(x, ""), nil
|
||||
} else if typ.Info()&types.IsComplex != 0 {
|
||||
// Negate both components of the complex number.
|
||||
r := b.CreateExtractValue(x, 0, "r")
|
||||
i := b.CreateExtractValue(x, 1, "i")
|
||||
r = b.CreateFNeg(r, "")
|
||||
i = b.CreateFNeg(i, "")
|
||||
cplx := llvm.Undef(x.Type())
|
||||
cplx = b.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = b.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
} else {
|
||||
return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown basic type for negate: "+typ.String())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
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,
|
||||
}
|
||||
machine, err := NewTargetMachine(config)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create target machine:", err)
|
||||
}
|
||||
|
||||
tests := []string{
|
||||
"basic.go",
|
||||
"pointer.go",
|
||||
"slice.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, config)
|
||||
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
|
||||
}
|
||||
@@ -163,6 +163,44 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
|
||||
return b.CreateCall(target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||
// be one of:
|
||||
//
|
||||
// func SVCall0(num uintptr) uintptr
|
||||
// func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
// func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
//
|
||||
// The num parameter must be a constant. All other parameters may be any scalar
|
||||
// value supported by LLVM inline assembly.
|
||||
// Same as emitSVCall but for AArch64
|
||||
func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) {
|
||||
num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value)
|
||||
llvmArgs := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||
constraints := "={x0}"
|
||||
for i, arg := range args[1:] {
|
||||
arg = arg.(*ssa.MakeInterface).X
|
||||
if i == 0 {
|
||||
constraints += ",0"
|
||||
} else {
|
||||
constraints += ",{x" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
llvmValue := b.getValue(arg)
|
||||
llvmArgs = append(llvmArgs, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
// Implement the ARM64 calling convention by marking x1-x7 as
|
||||
// clobbered. x0 is used as an output register so doesn't have to be
|
||||
// marked as clobbered.
|
||||
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
|
||||
return b.CreateCall(target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin which emits CSR instructions. It can be one of:
|
||||
//
|
||||
// func (csr CSR) Get() uintptr
|
||||
|
||||
@@ -465,9 +465,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...)
|
||||
wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false)
|
||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
||||
if f.LLVMFn.LastParam().Name() == "parentHandle" {
|
||||
wrapper.LastParam().SetName("parentHandle")
|
||||
}
|
||||
wrapper.LastParam().SetName("parentHandle")
|
||||
|
||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
|
||||
@@ -26,7 +26,6 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
|
||||
packedType := ctx.StructType(valueTypes, false)
|
||||
|
||||
// Allocate memory for the packed data.
|
||||
var packedAlloc, packedHeapAlloc llvm.Value
|
||||
size := targetData.TypeAllocSize(packedType)
|
||||
if size == 0 {
|
||||
return llvm.ConstPointerNull(i8ptrType)
|
||||
@@ -39,9 +38,39 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
|
||||
// Try to keep this cast in SSA form.
|
||||
return builder.CreateIntToPtr(values[0], i8ptrType, "pack.int")
|
||||
}
|
||||
|
||||
// Because packedType is a struct and we have to cast it to a *i8, store
|
||||
// it in an alloca first for bitcasting (store+bitcast+load).
|
||||
packedAlloc, _, _ = CreateTemporaryAlloca(builder, mod, packedType, "")
|
||||
// it in a *i8 alloca first and load the *i8 value from there. This is
|
||||
// effectively a bitcast.
|
||||
packedAlloc, _, _ := CreateTemporaryAlloca(builder, mod, i8ptrType, "")
|
||||
|
||||
if size < targetData.TypeAllocSize(i8ptrType) {
|
||||
// The alloca is bigger than the value that will be stored in it.
|
||||
// To avoid having some bits undefined, zero the alloca first.
|
||||
// Hopefully this will get optimized away.
|
||||
builder.CreateStore(llvm.ConstNull(i8ptrType), packedAlloc)
|
||||
}
|
||||
|
||||
// Store all values in the alloca.
|
||||
packedAllocCast := builder.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "")
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := builder.CreateInBoundsGEP(packedAllocCast, indices, "")
|
||||
builder.CreateStore(value, gep)
|
||||
}
|
||||
|
||||
// Load value (the *i8) from the alloca.
|
||||
result := builder.CreateLoad(packedAlloc, "")
|
||||
|
||||
// End the lifetime of the alloca, to help the optimizer.
|
||||
packedPtr := builder.CreateBitCast(packedAlloc, i8ptrType, "")
|
||||
packedSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||
EmitLifetimeEnd(builder, mod, packedPtr, packedSize)
|
||||
|
||||
return result
|
||||
} else {
|
||||
// Check if the values are all constants.
|
||||
constant := true
|
||||
@@ -67,7 +96,7 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
|
||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||
sizeValue := llvm.ConstInt(uintptrType, size, false)
|
||||
alloc := mod.NamedFunction("runtime.alloc")
|
||||
packedHeapAlloc = builder.CreateCall(alloc, []llvm.Value{
|
||||
packedHeapAlloc := builder.CreateCall(alloc, []llvm.Value{
|
||||
sizeValue,
|
||||
llvm.Undef(i8ptrType), // unused context parameter
|
||||
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
||||
@@ -80,28 +109,19 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.
|
||||
llvm.ConstPointerNull(i8ptrType), // coroutine handle
|
||||
}, "")
|
||||
}
|
||||
packedAlloc = builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
||||
}
|
||||
// Store all values in the alloca or heap pointer.
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
||||
builder.CreateStore(value, gep)
|
||||
}
|
||||
packedAlloc := builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
||||
|
||||
if packedHeapAlloc.IsNil() {
|
||||
// Load value (as *i8) from the alloca.
|
||||
packedAlloc = builder.CreateBitCast(packedAlloc, llvm.PointerType(i8ptrType, 0), "")
|
||||
result := builder.CreateLoad(packedAlloc, "")
|
||||
packedPtr := builder.CreateBitCast(packedAlloc, i8ptrType, "")
|
||||
packedSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||
EmitLifetimeEnd(builder, mod, packedPtr, packedSize)
|
||||
return result
|
||||
} else {
|
||||
// Get the original heap allocation pointer, which already is an *i8.
|
||||
// Store all values in the heap pointer.
|
||||
for i, value := range values {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
||||
}
|
||||
gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
||||
builder.CreateStore(value, gep)
|
||||
}
|
||||
|
||||
// Return the original heap allocation pointer, which already is an *i8.
|
||||
return packedHeapAlloc
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
// Basic tests that don't need to be split into a separate file.
|
||||
|
||||
func addInt(x, y int) int {
|
||||
return x + y
|
||||
}
|
||||
|
||||
func equalInt(x, y int) bool {
|
||||
return x == y
|
||||
}
|
||||
|
||||
func floatEQ(x, y float32) bool {
|
||||
return x == y
|
||||
}
|
||||
|
||||
func floatNE(x, y float32) bool {
|
||||
return x != y
|
||||
}
|
||||
|
||||
func floatLower(x, y float32) bool {
|
||||
return x < y
|
||||
}
|
||||
|
||||
func floatLowerEqual(x, y float32) bool {
|
||||
return x <= y
|
||||
}
|
||||
|
||||
func floatGreater(x, y float32) bool {
|
||||
return x > y
|
||||
}
|
||||
|
||||
func floatGreaterEqual(x, y float32) bool {
|
||||
return x >= y
|
||||
}
|
||||
|
||||
func complexReal(x complex64) float32 {
|
||||
return real(x)
|
||||
}
|
||||
|
||||
func complexImag(x complex64) float32 {
|
||||
return imag(x)
|
||||
}
|
||||
|
||||
func complexAdd(x, y complex64) complex64 {
|
||||
return x + y
|
||||
}
|
||||
|
||||
func complexSub(x, y complex64) complex64 {
|
||||
return x - y
|
||||
}
|
||||
|
||||
func complexMul(x, y complex64) complex64 {
|
||||
return x * y
|
||||
}
|
||||
|
||||
// TODO: complexDiv (requires runtime call)
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
; ModuleID = 'basic.go'
|
||||
source_filename = "basic.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = add i32 %x, %y
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp eq i32 %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp oeq float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp une float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp olt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp ole float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp ogt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp oge float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret float %x.r
|
||||
}
|
||||
|
||||
define internal float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret float %x.i
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fadd float %x.r, %y.r
|
||||
%1 = fadd float %x.i, %y.i
|
||||
%2 = insertvalue { float, float } undef, float %0, 0
|
||||
%3 = insertvalue { float, float } %2, float %1, 1
|
||||
ret { float, float } %3
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fsub float %x.r, %y.r
|
||||
%1 = fsub float %x.i, %y.i
|
||||
%2 = insertvalue { float, float } undef, float %0, 0
|
||||
%3 = insertvalue { float, float } %2, float %1, 1
|
||||
ret { float, float } %3
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fmul float %x.r, %y.r
|
||||
%1 = fmul float %x.i, %y.i
|
||||
%2 = fsub float %0, %1
|
||||
%3 = fmul float %x.r, %y.i
|
||||
%4 = fmul float %x.i, %y.r
|
||||
%5 = fadd float %3, %4
|
||||
%6 = insertvalue { float, float } undef, float %2, 0
|
||||
%7 = insertvalue { float, float } %6, float %5, 1
|
||||
ret { float, float } %7
|
||||
}
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
// This file tests various operations on pointers, such as pointer arithmetic
|
||||
// and dereferencing pointers.
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Dereference pointers.
|
||||
|
||||
func pointerDerefZero(x *[0]int) [0]int {
|
||||
return *x // This is a no-op, there is nothing to load.
|
||||
}
|
||||
|
||||
// Unsafe pointer casts, they are sometimes a no-op.
|
||||
|
||||
func pointerCastFromUnsafe(x unsafe.Pointer) *int {
|
||||
return (*int)(x)
|
||||
}
|
||||
|
||||
func pointerCastToUnsafe(x *int) unsafe.Pointer {
|
||||
return unsafe.Pointer(x)
|
||||
}
|
||||
|
||||
func pointerCastToUnsafeNoop(x *byte) unsafe.Pointer {
|
||||
return unsafe.Pointer(x)
|
||||
}
|
||||
|
||||
// The compiler has support for a few special cast+add patterns that are
|
||||
// transformed into a single GEP.
|
||||
|
||||
func pointerUnsafeGEPFixedOffset(ptr *byte) *byte {
|
||||
return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + 10))
|
||||
}
|
||||
|
||||
func pointerUnsafeGEPByteOffset(ptr *byte, offset uintptr) *byte {
|
||||
return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + offset))
|
||||
}
|
||||
|
||||
func pointerUnsafeGEPIntOffset(ptr *int32, offset uintptr) *int32 {
|
||||
return (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + offset*4))
|
||||
}
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
; ModuleID = 'pointer.go'
|
||||
source_filename = "pointer.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret [0 x i32] zeroinitializer
|
||||
}
|
||||
|
||||
define internal i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = bitcast i8* %x to i32*
|
||||
ret i32* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = bitcast i32* %x to i8*
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i8* %x
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
||||
ret i32* %0
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func sliceLen(ints []int) int {
|
||||
return len(ints)
|
||||
}
|
||||
|
||||
func sliceCap(ints []int) int {
|
||||
return cap(ints)
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
; ModuleID = 'slice.go'
|
||||
source_filename = "slice.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 %ints.len
|
||||
}
|
||||
|
||||
define internal i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 %ints.cap
|
||||
}
|
||||
@@ -4,11 +4,11 @@ go 1.11
|
||||
|
||||
require (
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac
|
||||
github.com/chromedp/chromedp v0.5.3
|
||||
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b
|
||||
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||
go.bug.st/serial v1.0.0
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||
tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d
|
||||
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac h1:T7V5BXqnYd55Hj/g5uhDYumg9Fp3rMTS6bykYtTIFX4=
|
||||
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
|
||||
github.com/chromedp/chromedp v0.5.3 h1:F9LafxmYpsQhWQBdCs+6Sret1zzeeFyHS5LkRF//Ffg=
|
||||
github.com/chromedp/chromedp v0.5.3/go.mod h1:YLdPtndaHQ4rCpSpBG+IPpy9JvX0VD+7aaLxYgYj28w=
|
||||
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b h1:LF+GRwyzxrO3MUzPvejv+yBup0lNG+/QdIRrkxOPseA=
|
||||
github.com/chromedp/cdproto v0.0.0-20200709115526-d1f6fc58448b/go.mod h1:E6LPWRdIJc11h/di5p0rwvRmUYbhGpBEH7ZbPfzDIOE=
|
||||
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e h1:Hv0JVyHhbIXb9NiYQe4NsrfgrSofAp0q2FnhhJOXgi8=
|
||||
github.com/chromedp/chromedp v0.5.4-0.20200303084119-2bb39134ab9e/go.mod h1:vmQMRHFZrY3T+Jv51T0n87OK/i6bK+5P9a+Fg5jPwgQ=
|
||||
github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
|
||||
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -11,14 +12,15 @@ github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g=
|
||||
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/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs=
|
||||
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0=
|
||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
|
||||
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -46,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=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d h1:hcX7vpB067GWM/EH4sGGOti0PMgIx+0bbZwUXctOIvE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200503225853-345b2947b59d/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9 h1:l2kTQOhqEoeDTK3ckUnwReOQwMPUmURMIdjJbeAuDT4=
|
||||
tinygo.org/x/go-llvm v0.0.0-20201104183921-570e7a6841d9/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.15.0"
|
||||
const Version = "0.17.0-dev"
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
|
||||
+92
-37
@@ -6,50 +6,81 @@ possible and only run unknown expressions (e.g. external calls) at runtime. This
|
||||
is in practice a partial evaluator of the `runtime.initAll` function, which
|
||||
calls each package initializer.
|
||||
|
||||
It works by directly interpreting LLVM IR:
|
||||
This package is a rewrite of a previous partial evaluator that worked
|
||||
directly on LLVM IR and used the module and LLVM constants as intermediate
|
||||
values. This newer version instead uses a mostly Go intermediate form. It
|
||||
compiles functions and extracts relevant data first (compiler.go), then
|
||||
executes those functions (interpreter.go) in a memory space that can be
|
||||
rolled back per function (memory.go). This means that it is not necessary to
|
||||
scan functions to see whether they can be run at compile time, which was very
|
||||
error prone. Instead it just tries to execute everything and if it hits
|
||||
something it cannot interpret (such as a store to memory-mapped I/O) it rolls
|
||||
back the execution of that function and runs the function at runtime instead.
|
||||
All in all, this design provides several benefits:
|
||||
|
||||
* Almost all operations work directly on constants, and are implemented using
|
||||
the llvm.Const* set of functions that are evaluated directly.
|
||||
* External function calls and some other operations (inline assembly, volatile
|
||||
load, volatile store) are seen as having limited side effects. Limited in
|
||||
the sense that it is known at compile time which globals it affects, which
|
||||
then are marked 'dirty' (meaning, further operations on it must be done at
|
||||
runtime). These operations are emitted directly in the `runtime.initAll`
|
||||
function. Return values are also considered 'dirty'.
|
||||
* Such 'dirty' objects and local values must be executed at runtime instead of
|
||||
at compile time. This dirtyness propagates further through the IR, for
|
||||
example storing a dirty local value to a global also makes the global dirty,
|
||||
meaning that the global may not be read or written at compile time as it's
|
||||
contents at that point during interpretation is unknown.
|
||||
* There are some heuristics in place to avoid doing too much with dirty
|
||||
values. For example, a branch based on a dirty local marks the whole
|
||||
function itself as having side effect (as if it is an external function).
|
||||
However, all globals it touches are still taken into account and when a call
|
||||
is inserted in `runtime.initAll`, all globals it references are also marked
|
||||
dirty.
|
||||
* Heap allocation (`runtime.alloc`) is emulated by creating new objects. The
|
||||
value in the allocation is the initializer of the global, the zero value is
|
||||
the zero initializer.
|
||||
* Stack allocation (`alloca`) is often emulated using a fake alloca object,
|
||||
until the address of the alloca is taken in which case it is also created as
|
||||
a real `alloca` in `runtime.initAll` and marked dirty. This may be necessary
|
||||
when calling an external function with the given alloca as paramter.
|
||||
* Much better error handling. By being able to revert to runtime execution
|
||||
without the need for scanning functions, this version is able to
|
||||
automatically work around many bugs in the previous implementation.
|
||||
* More correct memory model. This is not inherent to the new design, but the
|
||||
new design also made the memory model easier to reason about.
|
||||
* Faster execution of initialization code. While it is not much faster for
|
||||
normal interpretation (maybe 25% or so) due to the compilation overhead,
|
||||
it should be a whole lot faster for loops as it doesn't have to call into
|
||||
LLVM (via CGo) for every operation.
|
||||
|
||||
As mentioned, this partial evaulator comes in three parts: a compiler, an
|
||||
interpreter, and a memory manager.
|
||||
|
||||
## Compiler
|
||||
|
||||
The main task of the compiler is that it extracts all necessary data from
|
||||
every instruction in a function so that when this instruction is interpreted,
|
||||
no additional CGo calls are necessary. This is not currently done for all
|
||||
instructions (`runtime.alloc` is a notable exception), but at least it does
|
||||
so for the vast majority of instructions.
|
||||
|
||||
## Interpreter
|
||||
|
||||
The interpreter runs an instruction just like it would if it were executed
|
||||
'for real'. The vast majority of instructions can be executed at compile
|
||||
time. As indicated above, some instructions need to be executed at runtime
|
||||
instead.
|
||||
|
||||
## Memory
|
||||
|
||||
Memory is represented as objects (the `object` type) that contains data that
|
||||
will eventually be stored in a global and values (the `value` interface) that
|
||||
can be worked with while running the interpreter. Values therefore are only
|
||||
used locally and are always passed by value (just like most LLVM constants)
|
||||
while objects represent the backing storage (like LLVM globals). Some values
|
||||
are pointer values, and point to an object.
|
||||
|
||||
Importantly, this partial evaluator can roll back the execution of a
|
||||
function. This is implemented by creating a new memory view per function
|
||||
activation, which makes sure that any change to a global (such as a store
|
||||
instruction) is stored in the memory view. It creates a copy of the object
|
||||
and stores that in the memory view to be modified. Once the function has
|
||||
executed successfully, all these modified objects are then copied into the
|
||||
parent function, up to the root function invocation which (on successful
|
||||
execution) writes the values back into the LLVM module. This way, function
|
||||
invocations can be rolled back without leaving a trace.
|
||||
|
||||
Pointer values point to memory objects, but not to a particular memory
|
||||
object. Every memory object is given an index, and pointers use that index to
|
||||
look up the current active object for the pointer to load from or to copy
|
||||
when storing to it.
|
||||
|
||||
Rolling back a function should roll back everyting, including the few
|
||||
instructions emitted at runtime. This is done by treating instructions much
|
||||
like memory objects and removing the created instructions when necessary.
|
||||
|
||||
## Why is this necessary?
|
||||
|
||||
A partial evaluator is hard to get right, so why go through all the trouble of
|
||||
writing one?
|
||||
|
||||
The main reason is that the previous attempt wasn't complete and wasn't sound.
|
||||
It simply tried to evaluate Go SSA directly, which was good but more difficult
|
||||
than necessary. An IR based interpreter needs to understand fewer instructions
|
||||
as the LLVM IR simply has less (complex) instructions than Go SSA. Also, LLVM
|
||||
provides some useful tools like easily getting all uses of a function or global,
|
||||
which Go SSA does not provide.
|
||||
|
||||
But why is it necessary at all? The answer is that globals with initializers are
|
||||
much easier to optimize by LLVM than initialization code. Also, there are a few
|
||||
other benefits:
|
||||
The answer is that globals with initializers are much easier to optimize by
|
||||
LLVM than initialization code. Also, there are a few other benefits:
|
||||
|
||||
* Dead globals are trivial to optimize away.
|
||||
* Constant globals are easier to detect. Remember that Go does not have global
|
||||
@@ -60,5 +91,29 @@ other benefits:
|
||||
* Constants are much more efficent on microcontrollers, as they can be
|
||||
allocated in flash instead of RAM.
|
||||
|
||||
The Go SSA package does not create constant initializers for globals.
|
||||
Instead, it emits initialization functions, so if you write the following:
|
||||
|
||||
```go
|
||||
var foo = []byte{1, 2, 3, 4}
|
||||
```
|
||||
|
||||
It would generate something like this:
|
||||
|
||||
```go
|
||||
var foo []byte
|
||||
|
||||
func init() {
|
||||
foo = make([]byte, 4)
|
||||
foo[0] = 1
|
||||
foo[1] = 2
|
||||
foo[2] = 3
|
||||
foo[3] = 4
|
||||
}
|
||||
```
|
||||
|
||||
This is of course hugely wasteful, it's much better to create `foo` as a
|
||||
global array instead of initializing it at runtime.
|
||||
|
||||
For more details, see [this section of the
|
||||
documentation](https://tinygo.org/compiler-internals/differences-from-go/).
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
package interp
|
||||
|
||||
// This file compiles the LLVM IR to a form that's easy to efficiently
|
||||
// interpret.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// A function is a compiled LLVM function, which means that interpreting it
|
||||
// avoids most CGo calls necessary. This is done in a separate step so the
|
||||
// result can be cached.
|
||||
// Functions are in SSA form, just like the LLVM version if it. The first block
|
||||
// (blocks[0]) is the entry block.
|
||||
type function struct {
|
||||
llvmFn llvm.Value
|
||||
name string // precalculated llvmFn.Name()
|
||||
params []llvm.Value // precalculated llvmFn.Params()
|
||||
blocks []*basicBlock
|
||||
locals map[llvm.Value]int
|
||||
}
|
||||
|
||||
// basicBlock represents a LLVM basic block and contains a slice of
|
||||
// instructions. The last instruction must be a terminator instruction.
|
||||
type basicBlock struct {
|
||||
instructions []instruction
|
||||
}
|
||||
|
||||
// instruction is a precompiled LLVM IR instruction. The operands can be either
|
||||
// an already known value (such as literalValue or pointerValue) but can also be
|
||||
// the special localValue, which means that the value is a function parameter or
|
||||
// is produced by another instruction in the function. In that case, the
|
||||
// interpreter will replace the operand with that local value.
|
||||
type instruction struct {
|
||||
opcode llvm.Opcode
|
||||
localIndex int
|
||||
operands []value
|
||||
llvmInst llvm.Value
|
||||
name string
|
||||
}
|
||||
|
||||
// String returns a nice human-readable version of this instruction.
|
||||
func (inst *instruction) String() string {
|
||||
operands := make([]string, len(inst.operands))
|
||||
for i, op := range inst.operands {
|
||||
operands[i] = op.String()
|
||||
}
|
||||
|
||||
name := instructionNameMap[inst.opcode]
|
||||
if name == "" {
|
||||
name = "<unknown op>"
|
||||
}
|
||||
return name + " " + strings.Join(operands, " ")
|
||||
}
|
||||
|
||||
// compileFunction compiles a given LLVM function to an easier to interpret
|
||||
// version of the function. As far as possible, all operands are preprocessed so
|
||||
// that the interpreter doesn't have to call into LLVM.
|
||||
func (r *runner) compileFunction(llvmFn llvm.Value) *function {
|
||||
fn := &function{
|
||||
llvmFn: llvmFn,
|
||||
name: llvmFn.Name(),
|
||||
params: llvmFn.Params(),
|
||||
locals: make(map[llvm.Value]int),
|
||||
}
|
||||
if llvmFn.IsDeclaration() {
|
||||
// Nothing to do.
|
||||
return fn
|
||||
}
|
||||
|
||||
for i, param := range fn.params {
|
||||
fn.locals[param] = i
|
||||
}
|
||||
|
||||
// Make a map of all the blocks, to quickly find the block number for a
|
||||
// given branch instruction.
|
||||
blockIndices := make(map[llvm.Value]int)
|
||||
for llvmBB := llvmFn.FirstBasicBlock(); !llvmBB.IsNil(); llvmBB = llvm.NextBasicBlock(llvmBB) {
|
||||
index := len(blockIndices)
|
||||
blockIndices[llvmBB.AsValue()] = index
|
||||
}
|
||||
|
||||
// Compile every block.
|
||||
for llvmBB := llvmFn.FirstBasicBlock(); !llvmBB.IsNil(); llvmBB = llvm.NextBasicBlock(llvmBB) {
|
||||
bb := &basicBlock{}
|
||||
fn.blocks = append(fn.blocks, bb)
|
||||
|
||||
// Compile every instruction in the block.
|
||||
for llvmInst := llvmBB.FirstInstruction(); !llvmInst.IsNil(); llvmInst = llvm.NextInstruction(llvmInst) {
|
||||
// Create instruction skeleton.
|
||||
opcode := llvmInst.InstructionOpcode()
|
||||
inst := instruction{
|
||||
opcode: opcode,
|
||||
localIndex: len(fn.locals),
|
||||
llvmInst: llvmInst,
|
||||
}
|
||||
fn.locals[llvmInst] = len(fn.locals)
|
||||
|
||||
// Add operands specific for this instruction.
|
||||
switch opcode {
|
||||
case llvm.Ret:
|
||||
// Return instruction, which can either be a `ret void` (no
|
||||
// return value) or return a value.
|
||||
numOperands := llvmInst.OperandsCount()
|
||||
if numOperands != 0 {
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
}
|
||||
}
|
||||
case llvm.Br:
|
||||
// Branch instruction. Can be either a conditional branch (with
|
||||
// 3 operands) or unconditional branch (with just one basic
|
||||
// block operand).
|
||||
numOperands := llvmInst.OperandsCount()
|
||||
switch numOperands {
|
||||
case 3:
|
||||
// Conditional jump to one of two blocks. Comparable to an
|
||||
// if/else in procedural languages.
|
||||
thenBB := llvmInst.Operand(2)
|
||||
elseBB := llvmInst.Operand(1)
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
literalValue{uint32(blockIndices[thenBB])},
|
||||
literalValue{uint32(blockIndices[elseBB])},
|
||||
}
|
||||
case 1:
|
||||
// Unconditional jump to a target basic block. Comparable to
|
||||
// a jump in C and Go.
|
||||
jumpBB := llvmInst.Operand(0)
|
||||
inst.operands = []value{
|
||||
literalValue{uint32(blockIndices[jumpBB])},
|
||||
}
|
||||
default:
|
||||
panic("unknown number of operands")
|
||||
}
|
||||
case llvm.PHI:
|
||||
inst.name = llvmInst.Name()
|
||||
incomingCount := inst.llvmInst.IncomingCount()
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
incomingBB := inst.llvmInst.IncomingBlock(i)
|
||||
incomingValue := inst.llvmInst.IncomingValue(i)
|
||||
inst.operands = append(inst.operands,
|
||||
literalValue{uint32(blockIndices[incomingBB.AsValue()])},
|
||||
r.getValue(incomingValue),
|
||||
)
|
||||
}
|
||||
case llvm.Select:
|
||||
// Select is a special instruction that is much like a ternary
|
||||
// operator. It produces operand 1 or 2 based on the boolean
|
||||
// that is operand 0.
|
||||
inst.name = llvmInst.Name()
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
r.getValue(llvmInst.Operand(1)),
|
||||
r.getValue(llvmInst.Operand(2)),
|
||||
}
|
||||
case llvm.Call:
|
||||
// Call is a regular function call but could also be a runtime
|
||||
// intrinsic. Some runtime intrinsics are treated specially by
|
||||
// the interpreter, such as runtime.alloc. We don't
|
||||
// differentiate between them here because these calls may also
|
||||
// need to be run at runtime, in which case they should all be
|
||||
// created in the same way.
|
||||
llvmCalledValue := llvmInst.CalledValue()
|
||||
if !llvmCalledValue.IsAFunction().IsNil() {
|
||||
name := llvmCalledValue.Name()
|
||||
if name == "llvm.dbg.value" || strings.HasPrefix(name, "llvm.lifetime.") {
|
||||
// These intrinsics should not be interpreted, they are not
|
||||
// relevant to the execution of this function.
|
||||
continue
|
||||
}
|
||||
}
|
||||
inst.name = llvmInst.Name()
|
||||
numOperands := llvmInst.OperandsCount()
|
||||
inst.operands = append(inst.operands, r.getValue(llvmCalledValue))
|
||||
for i := 0; i < numOperands-1; i++ {
|
||||
inst.operands = append(inst.operands, r.getValue(llvmInst.Operand(i)))
|
||||
}
|
||||
case llvm.Load:
|
||||
// Load instruction. The interpreter will load from the
|
||||
// appropriate memory view.
|
||||
// Also provide the memory size to be loaded, which is necessary
|
||||
// with a lack of type information.
|
||||
inst.name = llvmInst.Name()
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
literalValue{r.targetData.TypeAllocSize(llvmInst.Type())},
|
||||
}
|
||||
case llvm.Store:
|
||||
// Store instruction. The interpreter will create a new object
|
||||
// in the memory view of the function invocation and store to
|
||||
// that, to make it possible to roll back this store.
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
r.getValue(llvmInst.Operand(1)),
|
||||
}
|
||||
case llvm.Alloca:
|
||||
// Alloca allocates stack space for local variables.
|
||||
numElements := r.getValue(inst.llvmInst.Operand(0)).(literalValue).value.(uint32)
|
||||
elementSize := r.targetData.TypeAllocSize(inst.llvmInst.Type().ElementType())
|
||||
inst.operands = []value{
|
||||
literalValue{elementSize * uint64(numElements)},
|
||||
}
|
||||
case llvm.GetElementPtr:
|
||||
// GetElementPtr does pointer arithmetic.
|
||||
inst.name = llvmInst.Name()
|
||||
ptr := llvmInst.Operand(0)
|
||||
n := llvmInst.OperandsCount()
|
||||
elementType := ptr.Type().ElementType()
|
||||
// gep: [source ptr, dest value size, pairs of indices...]
|
||||
inst.operands = []value{
|
||||
r.getValue(ptr),
|
||||
literalValue{r.targetData.TypeAllocSize(llvmInst.Type().ElementType())},
|
||||
r.getValue(llvmInst.Operand(1)),
|
||||
literalValue{r.targetData.TypeAllocSize(elementType)},
|
||||
}
|
||||
for i := 2; i < n; i++ {
|
||||
operand := r.getValue(llvmInst.Operand(i))
|
||||
if elementType.TypeKind() == llvm.StructTypeKind {
|
||||
index := operand.(literalValue).value.(uint32)
|
||||
elementOffset := r.targetData.ElementOffset(elementType, int(index))
|
||||
// Encode operands in a special way. The elementOffset
|
||||
// is just the offset in bytes. The elementSize is a
|
||||
// negative number (when cast to a int64) by flipping
|
||||
// all the bits. This allows the interpreter to detect
|
||||
// this is a struct field and that it should not
|
||||
// multiply it with the elementOffset to get the offset.
|
||||
// It is important for the interpreter to know the
|
||||
// struct field index for when the GEP must be done at
|
||||
// runtime.
|
||||
inst.operands = append(inst.operands, literalValue{elementOffset}, literalValue{^uint64(index)})
|
||||
elementType = elementType.StructElementTypes()[index]
|
||||
} else {
|
||||
elementType = elementType.ElementType()
|
||||
elementSize := r.targetData.TypeAllocSize(elementType)
|
||||
elementSizeOperand := literalValue{elementSize}
|
||||
// Add operand * elementSizeOperand bytes to the pointer.
|
||||
inst.operands = append(inst.operands, operand, elementSizeOperand)
|
||||
}
|
||||
}
|
||||
case llvm.BitCast, llvm.IntToPtr, llvm.PtrToInt:
|
||||
// Bitcasts are ususally used to cast a pointer from one type to
|
||||
// another leaving the pointer itself intact.
|
||||
inst.name = llvmInst.Name()
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
}
|
||||
case llvm.ExtractValue:
|
||||
inst.name = llvmInst.Name()
|
||||
agg := llvmInst.Operand(0)
|
||||
var offset uint64
|
||||
indexingType := agg.Type()
|
||||
for _, index := range inst.llvmInst.Indices() {
|
||||
switch indexingType.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
offset += r.targetData.ElementOffset(indexingType, int(index))
|
||||
indexingType = indexingType.StructElementTypes()[index]
|
||||
default: // ArrayTypeKind
|
||||
indexingType = indexingType.ElementType()
|
||||
elementSize := r.targetData.TypeAllocSize(indexingType)
|
||||
offset += elementSize * uint64(index)
|
||||
}
|
||||
}
|
||||
size := r.targetData.TypeAllocSize(inst.llvmInst.Type())
|
||||
// extractvalue [agg, byteOffset, byteSize]
|
||||
inst.operands = []value{
|
||||
r.getValue(agg),
|
||||
literalValue{offset},
|
||||
literalValue{size},
|
||||
}
|
||||
case llvm.InsertValue:
|
||||
inst.name = llvmInst.Name()
|
||||
agg := llvmInst.Operand(0)
|
||||
var offset uint64
|
||||
indexingType := agg.Type()
|
||||
for _, index := range inst.llvmInst.Indices() {
|
||||
switch indexingType.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
offset += r.targetData.ElementOffset(indexingType, int(index))
|
||||
indexingType = indexingType.StructElementTypes()[index]
|
||||
default: // ArrayTypeKind
|
||||
indexingType = indexingType.ElementType()
|
||||
elementSize := r.targetData.TypeAllocSize(indexingType)
|
||||
offset += elementSize * uint64(index)
|
||||
}
|
||||
}
|
||||
// insertvalue [agg, elt, byteOffset]
|
||||
inst.operands = []value{
|
||||
r.getValue(agg),
|
||||
r.getValue(llvmInst.Operand(1)),
|
||||
literalValue{offset},
|
||||
}
|
||||
case llvm.ICmp:
|
||||
inst.name = llvmInst.Name()
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
r.getValue(llvmInst.Operand(1)),
|
||||
literalValue{uint8(llvmInst.IntPredicate())},
|
||||
}
|
||||
case llvm.FCmp:
|
||||
inst.name = llvmInst.Name()
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
r.getValue(llvmInst.Operand(1)),
|
||||
literalValue{uint8(llvmInst.FloatPredicate())},
|
||||
}
|
||||
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
|
||||
// Integer binary operations.
|
||||
inst.name = llvmInst.Name()
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
r.getValue(llvmInst.Operand(1)),
|
||||
}
|
||||
case llvm.SExt, llvm.ZExt, llvm.Trunc:
|
||||
// Extend or shrink an integer size.
|
||||
// No sign extension going on so easy to do.
|
||||
// zext: [value, bitwidth]
|
||||
// trunc: [value, bitwidth]
|
||||
inst.name = llvmInst.Name()
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
literalValue{uint64(llvmInst.Type().IntTypeWidth())},
|
||||
}
|
||||
case llvm.SIToFP, llvm.UIToFP:
|
||||
// Convert an integer to a floating point instruction.
|
||||
// opcode: [value, bitwidth]
|
||||
inst.name = llvmInst.Name()
|
||||
inst.operands = []value{
|
||||
r.getValue(llvmInst.Operand(0)),
|
||||
literalValue{uint64(r.targetData.TypeAllocSize(llvmInst.Type()) * 8)},
|
||||
}
|
||||
default:
|
||||
// Unknown instruction, which is already set in inst.opcode so
|
||||
// is detectable.
|
||||
// This error is handled when actually trying to interpret this
|
||||
// instruction (to not trigger on code that won't be executed).
|
||||
}
|
||||
bb.instructions = append(bb.instructions, inst)
|
||||
}
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
// instructionNameMap maps from instruction opcodes to instruction names. This
|
||||
// can be useful for debug logging.
|
||||
var instructionNameMap = [...]string{
|
||||
llvm.Ret: "ret",
|
||||
llvm.Br: "br",
|
||||
llvm.Switch: "switch",
|
||||
llvm.IndirectBr: "indirectbr",
|
||||
llvm.Invoke: "invoke",
|
||||
llvm.Unreachable: "unreachable",
|
||||
|
||||
// Standard Binary Operators
|
||||
llvm.Add: "add",
|
||||
llvm.FAdd: "fadd",
|
||||
llvm.Sub: "sub",
|
||||
llvm.FSub: "fsub",
|
||||
llvm.Mul: "mul",
|
||||
llvm.FMul: "fmul",
|
||||
llvm.UDiv: "udiv",
|
||||
llvm.SDiv: "sdiv",
|
||||
llvm.FDiv: "fdiv",
|
||||
llvm.URem: "urem",
|
||||
llvm.SRem: "srem",
|
||||
llvm.FRem: "frem",
|
||||
|
||||
// Logical Operators
|
||||
llvm.Shl: "shl",
|
||||
llvm.LShr: "lshr",
|
||||
llvm.AShr: "ashr",
|
||||
llvm.And: "and",
|
||||
llvm.Or: "or",
|
||||
llvm.Xor: "xor",
|
||||
|
||||
// Memory Operators
|
||||
llvm.Alloca: "alloca",
|
||||
llvm.Load: "load",
|
||||
llvm.Store: "store",
|
||||
llvm.GetElementPtr: "getelementptr",
|
||||
|
||||
// Cast Operators
|
||||
llvm.Trunc: "trunc",
|
||||
llvm.ZExt: "zext",
|
||||
llvm.SExt: "sext",
|
||||
llvm.FPToUI: "fptoui",
|
||||
llvm.FPToSI: "fptosi",
|
||||
llvm.UIToFP: "uitofp",
|
||||
llvm.SIToFP: "sitofp",
|
||||
llvm.FPTrunc: "fptrunc",
|
||||
llvm.FPExt: "fpext",
|
||||
llvm.PtrToInt: "ptrtoint",
|
||||
llvm.IntToPtr: "inttoptr",
|
||||
llvm.BitCast: "bitcast",
|
||||
|
||||
// Other Operators
|
||||
llvm.ICmp: "icmp",
|
||||
llvm.FCmp: "fcmp",
|
||||
llvm.PHI: "phi",
|
||||
llvm.Call: "call",
|
||||
llvm.Select: "select",
|
||||
llvm.VAArg: "vaarg",
|
||||
llvm.ExtractElement: "extractelement",
|
||||
llvm.InsertElement: "insertelement",
|
||||
llvm.ShuffleVector: "shufflevector",
|
||||
llvm.ExtractValue: "extractvalue",
|
||||
llvm.InsertValue: "insertvalue",
|
||||
}
|
||||
+16
-12
@@ -11,15 +11,19 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// errUnreachable is returned when an unreachable instruction is executed. This
|
||||
// error should not be visible outside of the interp package.
|
||||
var errUnreachable = &Error{Err: errors.New("interp: unreachable executed")}
|
||||
var errLiteralToPointer = errors.New("interp: trying to convert literal value to pointer")
|
||||
|
||||
// unsupportedInstructionError returns a new "unsupported instruction" error for
|
||||
// the given instruction. It includes source location information, when
|
||||
// available.
|
||||
func (e *evalPackage) unsupportedInstructionError(inst llvm.Value) *Error {
|
||||
return e.errorAt(inst, errors.New("interp: unsupported instruction"))
|
||||
// These errors are expected during normal execution and can be recovered from
|
||||
// by running the affected function at runtime instead of compile time.
|
||||
var (
|
||||
errExpectedPointer = errors.New("interp: trying to use an integer as a pointer (memory-mapped I/O?)")
|
||||
errUnsupportedInst = errors.New("interp: unsupported instruction")
|
||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||
)
|
||||
|
||||
func isRecoverableError(err error) bool {
|
||||
return err == errExpectedPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
||||
}
|
||||
|
||||
// ErrorLine is one line in a traceback. The position may be missing.
|
||||
@@ -46,13 +50,13 @@ func (e *Error) Error() string {
|
||||
// errorAt returns an error value for the currently interpreted package at the
|
||||
// location of the instruction. The location information may not be complete as
|
||||
// it depends on debug information in the IR.
|
||||
func (e *evalPackage) errorAt(inst llvm.Value, err error) *Error {
|
||||
pos := getPosition(inst)
|
||||
func (r *runner) errorAt(inst instruction, err error) *Error {
|
||||
pos := getPosition(inst.llvmInst)
|
||||
return &Error{
|
||||
ImportPath: e.packagePath,
|
||||
ImportPath: r.pkgName,
|
||||
Pos: pos,
|
||||
Err: err,
|
||||
Traceback: []ErrorLine{{pos, inst}},
|
||||
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-708
@@ -1,708 +0,0 @@
|
||||
package interp
|
||||
|
||||
// This file implements the core interpretation routines, interpreting single
|
||||
// functions.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
type frame struct {
|
||||
*evalPackage
|
||||
fn llvm.Value
|
||||
locals map[llvm.Value]Value
|
||||
}
|
||||
|
||||
// evalBasicBlock evaluates a single basic block, returning the return value (if
|
||||
// ending with a ret instruction), a list of outgoing basic blocks (if not
|
||||
// ending with a ret instruction), or an error on failure.
|
||||
// Most of it works at compile time. Some calls get translated into calls to be
|
||||
// executed at runtime: calls to functions with side effects, external calls,
|
||||
// and operations on the result of such instructions.
|
||||
func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (retval Value, outgoing []llvm.Value, err *Error) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if fr.Debug {
|
||||
print(indent)
|
||||
inst.Dump()
|
||||
println()
|
||||
}
|
||||
switch {
|
||||
case !inst.IsABinaryOperator().IsNil():
|
||||
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||
|
||||
switch inst.InstructionOpcode() {
|
||||
// Standard binary operators
|
||||
case llvm.Add:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAdd(lhs, rhs, "")}
|
||||
case llvm.FAdd:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFAdd(lhs, rhs, "")}
|
||||
case llvm.Sub:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSub(lhs, rhs, "")}
|
||||
case llvm.FSub:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFSub(lhs, rhs, "")}
|
||||
case llvm.Mul:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateMul(lhs, rhs, "")}
|
||||
case llvm.FMul:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFMul(lhs, rhs, "")}
|
||||
case llvm.UDiv:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateUDiv(lhs, rhs, "")}
|
||||
case llvm.SDiv:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSDiv(lhs, rhs, "")}
|
||||
case llvm.FDiv:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFDiv(lhs, rhs, "")}
|
||||
case llvm.URem:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateURem(lhs, rhs, "")}
|
||||
case llvm.SRem:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSRem(lhs, rhs, "")}
|
||||
case llvm.FRem:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFRem(lhs, rhs, "")}
|
||||
|
||||
// Logical operators
|
||||
case llvm.Shl:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateShl(lhs, rhs, "")}
|
||||
case llvm.LShr:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateLShr(lhs, rhs, "")}
|
||||
case llvm.AShr:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAShr(lhs, rhs, "")}
|
||||
case llvm.And:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAnd(lhs, rhs, "")}
|
||||
case llvm.Or:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateOr(lhs, rhs, "")}
|
||||
case llvm.Xor:
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateXor(lhs, rhs, "")}
|
||||
|
||||
default:
|
||||
return nil, nil, fr.unsupportedInstructionError(inst)
|
||||
}
|
||||
|
||||
// Memory operators
|
||||
case !inst.IsAAllocaInst().IsNil():
|
||||
allocType := inst.Type().ElementType()
|
||||
alloca := llvm.AddGlobal(fr.Mod, allocType, fr.packagePath+"$alloca")
|
||||
alloca.SetInitializer(llvm.ConstNull(allocType))
|
||||
alloca.SetLinkage(llvm.InternalLinkage)
|
||||
fr.locals[inst] = &LocalValue{
|
||||
Underlying: alloca,
|
||||
Eval: fr.Eval,
|
||||
}
|
||||
case !inst.IsALoadInst().IsNil():
|
||||
operand := fr.getLocal(inst.Operand(0)).(*LocalValue)
|
||||
var value llvm.Value
|
||||
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
|
||||
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
||||
} else {
|
||||
var err error
|
||||
value, err = operand.Load()
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
}
|
||||
if value.Type() != inst.Type() {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
|
||||
}
|
||||
fr.locals[inst] = fr.getValue(value)
|
||||
case !inst.IsAStoreInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
ptr := fr.getLocal(inst.Operand(1))
|
||||
if inst.IsVolatile() {
|
||||
fr.builder.CreateStore(value.Value(), ptr.Value())
|
||||
} else {
|
||||
err := ptr.Store(value.Value())
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
}
|
||||
case !inst.IsAGetElementPtrInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
llvmIndices := make([]llvm.Value, inst.OperandsCount()-1)
|
||||
for i := range llvmIndices {
|
||||
llvmIndices[i] = inst.Operand(i + 1)
|
||||
}
|
||||
indices := make([]uint32, len(llvmIndices))
|
||||
for i, llvmIndex := range llvmIndices {
|
||||
operand := fr.getLocal(llvmIndex)
|
||||
if !operand.IsConstant() {
|
||||
// Not a constant operation.
|
||||
// This should be detected by the scanner, but isn't at the
|
||||
// moment.
|
||||
return nil, nil, fr.errorAt(inst, errors.New("todo: non-const gep"))
|
||||
}
|
||||
indices[i] = uint32(operand.Value().ZExtValue())
|
||||
}
|
||||
result, err := value.GetElementPtr(indices)
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
if result.Type() != inst.Type() {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: gep: type does not match"))
|
||||
}
|
||||
fr.locals[inst] = result
|
||||
|
||||
// Cast operators
|
||||
case !inst.IsATruncInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateTrunc(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsAZExtInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateZExt(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsASExtInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSExt(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsAFPToUIInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPToUI(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsAFPToSIInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPToSI(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsAUIToFPInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateUIToFP(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsASIToFPInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSIToFP(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsAFPTruncInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPTrunc(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsAFPExtInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPExt(value.(*LocalValue).Value(), inst.Type(), "")}
|
||||
case !inst.IsAPtrToIntInst().IsNil():
|
||||
value := fr.getLocal(inst.Operand(0))
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreatePtrToInt(value.Value(), inst.Type(), "")}
|
||||
case !inst.IsABitCastInst().IsNil() && inst.Type().TypeKind() == llvm.PointerTypeKind:
|
||||
operand := inst.Operand(0)
|
||||
if !operand.IsACallInst().IsNil() {
|
||||
fn := operand.CalledValue()
|
||||
if !fn.IsAFunction().IsNil() && fn.Name() == "runtime.alloc" {
|
||||
continue // special case: bitcast of alloc
|
||||
}
|
||||
}
|
||||
if _, ok := fr.getLocal(operand).(*MapValue); ok {
|
||||
// Special case for runtime.trackPointer calls.
|
||||
// Note: this might not be entirely sound in some rare cases
|
||||
// where the map is stored in a dirty global.
|
||||
uses := getUses(inst)
|
||||
if len(uses) == 1 {
|
||||
use := uses[0]
|
||||
if !use.IsACallInst().IsNil() && !use.CalledValue().IsAFunction().IsNil() && use.CalledValue().Name() == "runtime.trackPointer" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// It is not possible in Go to bitcast a map value to a pointer.
|
||||
return nil, nil, fr.errorAt(inst, errors.New("unimplemented: bitcast of map"))
|
||||
}
|
||||
value := fr.getLocal(operand).(*LocalValue)
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
|
||||
|
||||
// Other operators
|
||||
case !inst.IsAICmpInst().IsNil():
|
||||
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||
predicate := inst.IntPredicate()
|
||||
if predicate == llvm.IntEQ {
|
||||
var lhsZero, rhsZero bool
|
||||
var ok1, ok2 bool
|
||||
if lhs.Type().TypeKind() == llvm.PointerTypeKind {
|
||||
// Unfortunately, the const propagation in the IR builder
|
||||
// doesn't handle pointer compares of inttoptr values. So we
|
||||
// implement it manually here.
|
||||
lhsZero, ok1 = isPointerNil(lhs)
|
||||
rhsZero, ok2 = isPointerNil(rhs)
|
||||
}
|
||||
if lhs.Type().TypeKind() == llvm.IntegerTypeKind {
|
||||
lhsZero, ok1 = isZero(lhs)
|
||||
rhsZero, ok2 = isZero(rhs)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
if lhsZero && rhsZero {
|
||||
// Both are zero, so this icmp is always evaluated to true.
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false)}
|
||||
continue
|
||||
}
|
||||
if lhsZero != rhsZero {
|
||||
// Only one of them is zero, so this comparison must return false.
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false)}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateICmp(predicate, lhs, rhs, "")}
|
||||
case !inst.IsAFCmpInst().IsNil():
|
||||
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||
predicate := inst.FloatPredicate()
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFCmp(predicate, lhs, rhs, "")}
|
||||
case !inst.IsAPHINode().IsNil():
|
||||
for i := 0; i < inst.IncomingCount(); i++ {
|
||||
if inst.IncomingBlock(i) == incoming {
|
||||
fr.locals[inst] = fr.getLocal(inst.IncomingValue(i))
|
||||
}
|
||||
}
|
||||
case !inst.IsACallInst().IsNil():
|
||||
callee := inst.CalledValue()
|
||||
switch {
|
||||
case callee.Name() == "runtime.alloc":
|
||||
// heap allocation
|
||||
users := getUses(inst)
|
||||
var resultInst = inst
|
||||
if len(users) == 1 && !users[0].IsABitCastInst().IsNil() {
|
||||
// happens when allocating something other than i8*
|
||||
resultInst = users[0]
|
||||
}
|
||||
size := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying.ZExtValue()
|
||||
allocType := resultInst.Type().ElementType()
|
||||
typeSize := fr.TargetData.TypeAllocSize(allocType)
|
||||
elementCount := 1
|
||||
if size != typeSize {
|
||||
// allocate an array
|
||||
if size%typeSize != 0 {
|
||||
return nil, nil, fr.unsupportedInstructionError(inst)
|
||||
}
|
||||
elementCount = int(size / typeSize)
|
||||
allocType = llvm.ArrayType(allocType, elementCount)
|
||||
}
|
||||
alloc := llvm.AddGlobal(fr.Mod, allocType, fr.packagePath+"$alloc")
|
||||
alloc.SetInitializer(llvm.ConstNull(allocType))
|
||||
alloc.SetLinkage(llvm.InternalLinkage)
|
||||
result := &LocalValue{
|
||||
Underlying: alloc,
|
||||
Eval: fr.Eval,
|
||||
}
|
||||
if elementCount == 1 {
|
||||
fr.locals[resultInst] = result
|
||||
} else {
|
||||
result, err := result.GetElementPtr([]uint32{0, 0})
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
fr.locals[resultInst] = result
|
||||
}
|
||||
case callee.Name() == "runtime.hashmapMake":
|
||||
// create a map
|
||||
keySize := inst.Operand(0).ZExtValue()
|
||||
valueSize := inst.Operand(1).ZExtValue()
|
||||
fr.locals[inst] = &MapValue{
|
||||
Eval: fr.Eval,
|
||||
PkgName: fr.packagePath,
|
||||
KeySize: int(keySize),
|
||||
ValueSize: int(valueSize),
|
||||
}
|
||||
case callee.Name() == "runtime.hashmapStringSet":
|
||||
// set a string key in the map
|
||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||
valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok || !keyBuf.IsConstant() || !keyLen.IsConstant() || !valPtr.IsConstant() {
|
||||
// The mapassign operation could not be done at compile
|
||||
// time. Do it at runtime instead.
|
||||
m := fr.getLocal(inst.Operand(0)).Value()
|
||||
fr.markDirty(m)
|
||||
llvmParams := []llvm.Value{
|
||||
m, // *runtime.hashmap
|
||||
fr.getLocal(inst.Operand(1)).Value(), // key.ptr
|
||||
fr.getLocal(inst.Operand(2)).Value(), // key.len
|
||||
fr.getLocal(inst.Operand(3)).Value(), // value (unsafe.Pointer)
|
||||
fr.getLocal(inst.Operand(4)).Value(), // context
|
||||
fr.getLocal(inst.Operand(5)).Value(), // parentHandle
|
||||
}
|
||||
fr.builder.CreateCall(callee, llvmParams, "")
|
||||
continue
|
||||
}
|
||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||
// into separate pointer and length parameters.
|
||||
err := m.PutString(keyBuf, keyLen, valPtr)
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
case callee.Name() == "runtime.hashmapBinarySet":
|
||||
// set a binary (int etc.) key in the map
|
||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||
valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok || !keyBuf.IsConstant() || !valPtr.IsConstant() {
|
||||
// The mapassign operation could not be done at compile
|
||||
// time. Do it at runtime instead.
|
||||
m := fr.getLocal(inst.Operand(0)).Value()
|
||||
fr.markDirty(m)
|
||||
llvmParams := []llvm.Value{
|
||||
m, // *runtime.hashmap
|
||||
fr.getLocal(inst.Operand(1)).Value(), // key
|
||||
fr.getLocal(inst.Operand(2)).Value(), // value
|
||||
fr.getLocal(inst.Operand(3)).Value(), // context
|
||||
fr.getLocal(inst.Operand(4)).Value(), // parentHandle
|
||||
}
|
||||
fr.builder.CreateCall(callee, llvmParams, "")
|
||||
continue
|
||||
}
|
||||
err := m.PutBinary(keyBuf, valPtr)
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
case callee.Name() == "runtime.stringConcat":
|
||||
// adding two strings together
|
||||
buf1Ptr := fr.getLocal(inst.Operand(0))
|
||||
buf1Len := fr.getLocal(inst.Operand(1))
|
||||
buf2Ptr := fr.getLocal(inst.Operand(2))
|
||||
buf2Len := fr.getLocal(inst.Operand(3))
|
||||
buf1, err := getStringBytes(buf1Ptr, buf1Len.Value())
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
buf2, err := getStringBytes(buf2Ptr, buf2Len.Value())
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
result := []byte(string(buf1) + string(buf2))
|
||||
vals := make([]llvm.Value, len(result))
|
||||
for i := range vals {
|
||||
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
|
||||
}
|
||||
globalType := llvm.ArrayType(fr.Mod.Context().Int8Type(), len(result))
|
||||
globalValue := llvm.ConstArray(fr.Mod.Context().Int8Type(), vals)
|
||||
global := llvm.AddGlobal(fr.Mod, globalType, fr.packagePath+"$stringconcat")
|
||||
global.SetInitializer(globalValue)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
stringType := fr.Mod.GetTypeByName("runtime._string")
|
||||
retPtr := llvm.ConstGEP(global, getLLVMIndices(fr.Mod.Context().Int32Type(), []uint32{0, 0}))
|
||||
retLen := llvm.ConstInt(stringType.StructElementTypes()[1], uint64(len(result)), false)
|
||||
ret := llvm.ConstNull(stringType)
|
||||
ret = llvm.ConstInsertValue(ret, retPtr, []uint32{0})
|
||||
ret = llvm.ConstInsertValue(ret, retLen, []uint32{1})
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, ret}
|
||||
case callee.Name() == "runtime.sliceCopy":
|
||||
elementSize := fr.getLocal(inst.Operand(4)).(*LocalValue).Value().ZExtValue()
|
||||
dstArray := fr.getLocal(inst.Operand(0)).(*LocalValue).stripPointerCasts()
|
||||
srcArray := fr.getLocal(inst.Operand(1)).(*LocalValue).stripPointerCasts()
|
||||
dstLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||
srcLen := fr.getLocal(inst.Operand(3)).(*LocalValue)
|
||||
if elementSize != 1 && dstArray.Type().ElementType().TypeKind() == llvm.ArrayTypeKind && srcArray.Type().ElementType().TypeKind() == llvm.ArrayTypeKind {
|
||||
// Slice data pointers are created by adding a global array
|
||||
// and getting the address of the first element using a GEP.
|
||||
// However, before the compiler can pass it to
|
||||
// runtime.sliceCopy, it has to perform a bitcast to a *i8,
|
||||
// to make it a unsafe.Pointer. Now, when the IR builder
|
||||
// sees a bitcast of a GEP with zero indices, it will make
|
||||
// a bitcast of the original array instead of the GEP,
|
||||
// which breaks our assumptions.
|
||||
// Re-add this GEP, in the hope that it it is then of the correct type...
|
||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0})
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
dstArray = dstArrayValue.(*LocalValue)
|
||||
srcArrayValue, err := srcArray.GetElementPtr([]uint32{0, 0})
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
srcArray = srcArrayValue.(*LocalValue)
|
||||
}
|
||||
if fr.Eval.TargetData.TypeAllocSize(dstArray.Type().ElementType()) != elementSize {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: slice dst element size does not match pointer type"))
|
||||
}
|
||||
if fr.Eval.TargetData.TypeAllocSize(srcArray.Type().ElementType()) != elementSize {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: slice src element size does not match pointer type"))
|
||||
}
|
||||
if dstArray.Type() != srcArray.Type() {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: slice element types don't match"))
|
||||
}
|
||||
length := dstLen.Value().SExtValue()
|
||||
if srcLength := srcLen.Value().SExtValue(); srcLength < length {
|
||||
length = srcLength
|
||||
}
|
||||
if length < 0 {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: trying to copy a slice with negative length?"))
|
||||
}
|
||||
for i := int64(0); i < length; i++ {
|
||||
// *dst = *src
|
||||
val, err := srcArray.Load()
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
err = dstArray.Store(val)
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
// dst++
|
||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
dstArray = dstArrayValue.(*LocalValue)
|
||||
// src++
|
||||
srcArrayValue, err := srcArray.GetElementPtr([]uint32{1})
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
srcArray = srcArrayValue.(*LocalValue)
|
||||
}
|
||||
case callee.Name() == "runtime.stringToBytes":
|
||||
// convert a string to a []byte
|
||||
bufPtr := fr.getLocal(inst.Operand(0))
|
||||
bufLen := fr.getLocal(inst.Operand(1))
|
||||
result, err := getStringBytes(bufPtr, bufLen.Value())
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err)
|
||||
}
|
||||
vals := make([]llvm.Value, len(result))
|
||||
for i := range vals {
|
||||
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
|
||||
}
|
||||
globalType := llvm.ArrayType(fr.Mod.Context().Int8Type(), len(result))
|
||||
globalValue := llvm.ConstArray(fr.Mod.Context().Int8Type(), vals)
|
||||
global := llvm.AddGlobal(fr.Mod, globalType, fr.packagePath+"$bytes")
|
||||
global.SetInitializer(globalValue)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
sliceType := inst.Type()
|
||||
retPtr := llvm.ConstGEP(global, getLLVMIndices(fr.Mod.Context().Int32Type(), []uint32{0, 0}))
|
||||
retLen := llvm.ConstInt(sliceType.StructElementTypes()[1], uint64(len(result)), false)
|
||||
ret := llvm.ConstNull(sliceType)
|
||||
ret = llvm.ConstInsertValue(ret, retPtr, []uint32{0}) // ptr
|
||||
ret = llvm.ConstInsertValue(ret, retLen, []uint32{1}) // len
|
||||
ret = llvm.ConstInsertValue(ret, retLen, []uint32{2}) // cap
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, ret}
|
||||
case callee.Name() == "runtime.typeAssert":
|
||||
actualTypeInt := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||
assertedType := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||
if actualTypeInt.IsAConstantExpr().IsNil() || actualTypeInt.Opcode() != llvm.PtrToInt {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: expected typecode in runtime.typeAssert to be a ptrtoint"))
|
||||
}
|
||||
actualType := actualTypeInt.Operand(0)
|
||||
if actualType.IsAConstant().IsNil() || assertedType.IsAConstant().IsNil() {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: unimplemented: type assert with non-constant interface value"))
|
||||
}
|
||||
assertOk := uint64(0)
|
||||
if llvm.ConstExtractValue(actualType.Initializer(), []uint32{0}) == assertedType {
|
||||
assertOk = 1
|
||||
}
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), assertOk, false)}
|
||||
case callee.Name() == "runtime.interfaceImplements":
|
||||
typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||
interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||
if typecode.IsAConstantExpr().IsNil() || typecode.Opcode() != llvm.PtrToInt {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: expected typecode to be a ptrtoint"))
|
||||
}
|
||||
typecode = typecode.Operand(0)
|
||||
if interfaceMethodSet.IsAConstantExpr().IsNil() || interfaceMethodSet.Opcode() != llvm.GetElementPtr {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: expected method set in runtime.interfaceImplements to be a constant gep"))
|
||||
}
|
||||
interfaceMethodSet = interfaceMethodSet.Operand(0).Initializer()
|
||||
methodSet := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1})
|
||||
if methodSet.IsAConstantExpr().IsNil() || methodSet.Opcode() != llvm.GetElementPtr {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: expected method set to be a constant gep"))
|
||||
}
|
||||
methodSet = methodSet.Operand(0).Initializer()
|
||||
|
||||
// Make a set of all the methods on the concrete type, for
|
||||
// easier checking in the next step.
|
||||
definedMethods := map[string]struct{}{}
|
||||
for i := 0; i < methodSet.Type().ArrayLength(); i++ {
|
||||
methodInfo := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)})
|
||||
name := llvm.ConstExtractValue(methodInfo, []uint32{0}).Name()
|
||||
definedMethods[name] = struct{}{}
|
||||
}
|
||||
// Check whether all interface methods are also in the list
|
||||
// of defined methods calculated above.
|
||||
implements := uint64(1) // i1 true
|
||||
for i := 0; i < interfaceMethodSet.Type().ArrayLength(); i++ {
|
||||
name := llvm.ConstExtractValue(interfaceMethodSet, []uint32{uint32(i)}).Name()
|
||||
if _, ok := definedMethods[name]; !ok {
|
||||
// There is a method on the interface that is not
|
||||
// implemented by the type.
|
||||
implements = 0 // i1 false
|
||||
break
|
||||
}
|
||||
}
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)}
|
||||
case callee.Name() == "runtime.nanotime":
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
||||
case callee.Name() == "llvm.dbg.value":
|
||||
// do nothing
|
||||
case strings.HasPrefix(callee.Name(), "llvm.lifetime."):
|
||||
// do nothing
|
||||
case callee.Name() == "runtime.trackPointer":
|
||||
// do nothing
|
||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||
// This are all print instructions, which necessarily have side
|
||||
// effects but no results.
|
||||
// TODO: print an error when executing runtime._panic (with the
|
||||
// exact error message it would print at runtime).
|
||||
var params []llvm.Value
|
||||
for i := 0; i < inst.OperandsCount()-1; i++ {
|
||||
operand := fr.getLocal(inst.Operand(i)).Value()
|
||||
fr.markDirty(operand)
|
||||
params = append(params, operand)
|
||||
}
|
||||
// TODO: accurate debug info, including call chain
|
||||
fr.builder.CreateCall(callee, params, inst.Name())
|
||||
case !callee.IsAFunction().IsNil() && callee.IsDeclaration():
|
||||
// external functions
|
||||
var params []llvm.Value
|
||||
for i := 0; i < inst.OperandsCount()-1; i++ {
|
||||
operand := fr.getLocal(inst.Operand(i)).Value()
|
||||
fr.markDirty(operand)
|
||||
params = append(params, operand)
|
||||
}
|
||||
// TODO: accurate debug info, including call chain
|
||||
result := fr.builder.CreateCall(callee, params, inst.Name())
|
||||
if inst.Type().TypeKind() != llvm.VoidTypeKind {
|
||||
fr.markDirty(result)
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, result}
|
||||
}
|
||||
case !callee.IsAFunction().IsNil():
|
||||
// regular function
|
||||
var params []Value
|
||||
dirtyParams := false
|
||||
for i := 0; i < inst.OperandsCount()-1; i++ {
|
||||
local := fr.getLocal(inst.Operand(i))
|
||||
if !local.IsConstant() {
|
||||
dirtyParams = true
|
||||
}
|
||||
params = append(params, local)
|
||||
}
|
||||
var ret Value
|
||||
scanResult, err := fr.hasSideEffects(callee)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if scanResult.severity == sideEffectLimited || dirtyParams && scanResult.severity != sideEffectAll {
|
||||
// Side effect is bounded. This means the operation invokes
|
||||
// side effects (like calling an external function) but it
|
||||
// is known at compile time which side effects it invokes.
|
||||
// This means the function can be called at runtime and the
|
||||
// affected globals can be marked dirty at compile time.
|
||||
llvmParams := make([]llvm.Value, len(params))
|
||||
for i, param := range params {
|
||||
llvmParams[i] = param.Value()
|
||||
}
|
||||
result := fr.builder.CreateCall(callee, llvmParams, inst.Name())
|
||||
ret = &LocalValue{fr.Eval, result}
|
||||
// mark all mentioned globals as dirty
|
||||
for global := range scanResult.mentionsGlobals {
|
||||
fr.markDirty(global)
|
||||
}
|
||||
} else {
|
||||
// Side effect is one of:
|
||||
// * None: no side effects, can be fully interpreted at
|
||||
// compile time.
|
||||
// * Unbounded: cannot call at runtime so we'll try to
|
||||
// interpret anyway and hope for the best.
|
||||
ret, err = fr.function(callee, params, indent+" ")
|
||||
if err != nil {
|
||||
// Record this function call in the backtrace.
|
||||
err.Traceback = append(err.Traceback, ErrorLine{
|
||||
Pos: getPosition(inst),
|
||||
Inst: inst,
|
||||
})
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
if inst.Type().TypeKind() != llvm.VoidTypeKind {
|
||||
fr.locals[inst] = ret
|
||||
}
|
||||
default:
|
||||
// function pointers, etc.
|
||||
return nil, nil, fr.unsupportedInstructionError(inst)
|
||||
}
|
||||
case !inst.IsAExtractValueInst().IsNil():
|
||||
agg := fr.getLocal(inst.Operand(0)).(*LocalValue) // must be constant
|
||||
indices := inst.Indices()
|
||||
if agg.Underlying.IsConstant() {
|
||||
newValue := llvm.ConstExtractValue(agg.Underlying, indices)
|
||||
fr.locals[inst] = fr.getValue(newValue)
|
||||
} else {
|
||||
if len(indices) != 1 {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: cannot handle extractvalue with not exactly 1 index"))
|
||||
}
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateExtractValue(agg.Underlying, int(indices[0]), inst.Name())}
|
||||
}
|
||||
case !inst.IsAInsertValueInst().IsNil():
|
||||
agg := fr.getLocal(inst.Operand(0)).(*LocalValue) // must be constant
|
||||
val := fr.getLocal(inst.Operand(1))
|
||||
indices := inst.Indices()
|
||||
if agg.IsConstant() && val.IsConstant() {
|
||||
newValue := llvm.ConstInsertValue(agg.Underlying, val.Value(), indices)
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, newValue}
|
||||
} else {
|
||||
if len(indices) != 1 {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: cannot handle insertvalue with not exactly 1 index"))
|
||||
}
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateInsertValue(agg.Underlying, val.Value(), int(indices[0]), inst.Name())}
|
||||
}
|
||||
case !inst.IsASelectInst().IsNil():
|
||||
// var result T
|
||||
// if cond {
|
||||
// result = x
|
||||
// } else {
|
||||
// result = y
|
||||
// }
|
||||
// return result
|
||||
cond := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||
x := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||
y := fr.getLocal(inst.Operand(2)).(*LocalValue).Underlying
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSelect(cond, x, y, "")}
|
||||
|
||||
case !inst.IsAReturnInst().IsNil() && inst.OperandsCount() == 0:
|
||||
return nil, nil, nil // ret void
|
||||
case !inst.IsAReturnInst().IsNil() && inst.OperandsCount() == 1:
|
||||
return fr.getLocal(inst.Operand(0)), nil, nil
|
||||
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 3:
|
||||
// conditional branch (if/then/else)
|
||||
cond := fr.getLocal(inst.Operand(0)).Value()
|
||||
if cond.Type() != fr.Mod.Context().Int1Type() {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("expected an i1 in a branch instruction"))
|
||||
}
|
||||
thenBB := inst.Operand(1)
|
||||
elseBB := inst.Operand(2)
|
||||
if !cond.IsAInstruction().IsNil() {
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: branch on a non-constant"))
|
||||
}
|
||||
if !cond.IsAConstantExpr().IsNil() {
|
||||
// This may happen when the instruction builder could not
|
||||
// const-fold some instructions.
|
||||
return nil, nil, fr.errorAt(inst, errors.New("interp: branch on a non-const-propagated constant expression"))
|
||||
}
|
||||
switch cond {
|
||||
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false): // false
|
||||
return nil, []llvm.Value{thenBB}, nil // then
|
||||
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false): // true
|
||||
return nil, []llvm.Value{elseBB}, nil // else
|
||||
default:
|
||||
return nil, nil, fr.errorAt(inst, errors.New("branch was not true or false"))
|
||||
}
|
||||
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
|
||||
// unconditional branch (goto)
|
||||
return nil, []llvm.Value{inst.Operand(0)}, nil
|
||||
case !inst.IsAUnreachableInst().IsNil():
|
||||
// Unreachable was reached (e.g. after a call to panic()).
|
||||
// Report this as an error, as it is not supposed to happen.
|
||||
// This is a sentinel error value.
|
||||
return nil, nil, errUnreachable
|
||||
|
||||
default:
|
||||
return nil, nil, fr.unsupportedInstructionError(inst)
|
||||
}
|
||||
}
|
||||
|
||||
panic("interp: reached end of basic block without terminator")
|
||||
}
|
||||
|
||||
// Get the Value for an operand, which is a constant value of some sort.
|
||||
func (fr *frame) getLocal(v llvm.Value) Value {
|
||||
if ret, ok := fr.locals[v]; ok {
|
||||
return ret
|
||||
} else if value := fr.getValue(v); value != nil {
|
||||
return value
|
||||
} else {
|
||||
// This should not happen under normal circumstances.
|
||||
panic("cannot find value")
|
||||
}
|
||||
}
|
||||
+100
-114
@@ -1,59 +1,77 @@
|
||||
// Package interp interprets Go package initializers as much as possible. This
|
||||
// avoid running them at runtime, improving code size and making other
|
||||
// optimizations possible.
|
||||
// Package interp is a partial evaluator of code run at package init time. See
|
||||
// the README in this package for details.
|
||||
package interp
|
||||
|
||||
// This file provides the overarching Eval object with associated (utility)
|
||||
// methods.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
type Eval struct {
|
||||
Mod llvm.Module
|
||||
TargetData llvm.TargetData
|
||||
Debug bool
|
||||
builder llvm.Builder
|
||||
dirtyGlobals map[llvm.Value]struct{}
|
||||
sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results
|
||||
// Enable extra checks, which should be disabled by default.
|
||||
// This may help track down bugs by adding a few more sanity checks.
|
||||
const checks = true
|
||||
|
||||
// runner contains all state related to one interp run.
|
||||
type runner struct {
|
||||
mod llvm.Module
|
||||
targetData llvm.TargetData
|
||||
builder llvm.Builder
|
||||
pointerSize uint32 // cached pointer size from the TargetData
|
||||
i8ptrType llvm.Type // often used type so created in advance
|
||||
maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result
|
||||
debug bool // log debug messages
|
||||
pkgName string // package name of the currently executing package
|
||||
functionCache map[llvm.Value]*function // cache of compiled functions
|
||||
objects []object // slice of objects in memory
|
||||
globals map[llvm.Value]int // map from global to index in objects slice
|
||||
start time.Time
|
||||
callsExecuted uint64
|
||||
}
|
||||
|
||||
// evalPackage encapsulates the Eval type for just a single package. The Eval
|
||||
// type keeps state across the whole program, the evalPackage type keeps extra
|
||||
// state for the currently interpreted package.
|
||||
type evalPackage struct {
|
||||
*Eval
|
||||
packagePath string
|
||||
}
|
||||
|
||||
// Run evaluates the function with the given name and then eliminates all
|
||||
// callers.
|
||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||
// Set debug to true if it should print output while running.
|
||||
func Run(mod llvm.Module, debug bool) error {
|
||||
if debug {
|
||||
println("\ncompile-time evaluation:")
|
||||
r := runner{
|
||||
mod: mod,
|
||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||
debug: debug,
|
||||
functionCache: make(map[llvm.Value]*function),
|
||||
objects: []object{{}},
|
||||
globals: make(map[llvm.Value]int),
|
||||
start: time.Now(),
|
||||
}
|
||||
r.pointerSize = uint32(r.targetData.PointerSize())
|
||||
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
|
||||
|
||||
name := "runtime.initAll"
|
||||
e := &Eval{
|
||||
Mod: mod,
|
||||
TargetData: llvm.NewTargetData(mod.DataLayout()),
|
||||
Debug: debug,
|
||||
dirtyGlobals: map[llvm.Value]struct{}{},
|
||||
}
|
||||
e.builder = mod.Context().NewBuilder()
|
||||
|
||||
initAll := mod.NamedFunction(name)
|
||||
initAll := mod.NamedFunction("runtime.initAll")
|
||||
bb := initAll.EntryBasicBlock()
|
||||
|
||||
// Create a builder, to insert instructions that could not be evaluated at
|
||||
// compile time.
|
||||
r.builder = mod.Context().NewBuilder()
|
||||
defer r.builder.Dispose()
|
||||
|
||||
// Create a dummy alloca in the entry block that we can set the insert point
|
||||
// to. This is necessary because otherwise we might be removing the
|
||||
// instruction (init call) that we are removing after successful
|
||||
// interpretation.
|
||||
e.builder.SetInsertPointBefore(bb.FirstInstruction())
|
||||
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
|
||||
e.builder.SetInsertPointBefore(dummy)
|
||||
r.builder.SetInsertPointBefore(bb.FirstInstruction())
|
||||
dummy := r.builder.CreateAlloca(r.mod.Context().Int8Type(), "dummy")
|
||||
r.builder.SetInsertPointBefore(dummy)
|
||||
defer dummy.EraseFromParentAsInstruction()
|
||||
|
||||
// Get a list if init calls. A runtime.initAll might look something like this:
|
||||
// func initAll() {
|
||||
// unsafe.init()
|
||||
// machine.init()
|
||||
// runtime.init()
|
||||
// }
|
||||
// This function gets a list of these call instructions.
|
||||
var initCalls []llvm.Value
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if inst == dummy {
|
||||
@@ -63,99 +81,67 @@ func Run(mod llvm.Module, debug bool) error {
|
||||
break // ret void
|
||||
}
|
||||
if inst.IsACallInst().IsNil() || inst.CalledValue().IsAFunction().IsNil() {
|
||||
return errorAt(inst, "interp: expected all instructions in "+name+" to be direct calls")
|
||||
return errorAt(inst, "interp: expected all instructions in "+initAll.Name()+" to be direct calls")
|
||||
}
|
||||
initCalls = append(initCalls, inst)
|
||||
}
|
||||
|
||||
// Do this in a separate step to avoid corrupting the iterator above.
|
||||
undefPtr := llvm.Undef(llvm.PointerType(mod.Context().Int8Type(), 0))
|
||||
// Run initializers for each package. Once the package initializer is
|
||||
// finished, the call to the package initializer can be removed.
|
||||
for _, call := range initCalls {
|
||||
initName := call.CalledValue().Name()
|
||||
if !strings.HasSuffix(initName, ".init") {
|
||||
return errorAt(call, "interp: expected all instructions in "+name+" to be *.init() calls")
|
||||
return errorAt(call, "interp: expected all instructions in "+initAll.Name()+" to be *.init() calls")
|
||||
}
|
||||
pkgName := initName[:len(initName)-5]
|
||||
r.pkgName = initName[:len(initName)-len(".init")]
|
||||
fn := call.CalledValue()
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
||||
}
|
||||
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||
if callErr != nil {
|
||||
if isRecoverableError(callErr.Err) {
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, "not interpretring", r.pkgName, "because of error:", callErr.Err)
|
||||
}
|
||||
mem.revert()
|
||||
continue
|
||||
}
|
||||
return callErr
|
||||
}
|
||||
call.EraseFromParentAsInstruction()
|
||||
evalPkg := evalPackage{
|
||||
Eval: e,
|
||||
packagePath: pkgName,
|
||||
for index, obj := range mem.objects {
|
||||
r.objects[index] = obj
|
||||
}
|
||||
_, err := evalPkg.function(fn, []Value{&LocalValue{e, undefPtr}, &LocalValue{e, undefPtr}}, "")
|
||||
if err == errUnreachable {
|
||||
break
|
||||
}
|
||||
r.pkgName = ""
|
||||
|
||||
// Update all global variables in the LLVM module.
|
||||
mem := memoryView{r: &r}
|
||||
for _, obj := range r.objects {
|
||||
if obj.llvmGlobal.IsNil() {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
if obj.buffer == nil {
|
||||
continue
|
||||
}
|
||||
initializer := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
|
||||
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
|
||||
panic("initializer type mismatch")
|
||||
}
|
||||
obj.llvmGlobal.SetInitializer(initializer)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// function interprets the given function. The params are the function params
|
||||
// and the indent is the string indentation to use when dumping all interpreted
|
||||
// instructions.
|
||||
func (e *evalPackage) function(fn llvm.Value, params []Value, indent string) (Value, *Error) {
|
||||
fr := frame{
|
||||
evalPackage: e,
|
||||
fn: fn,
|
||||
locals: make(map[llvm.Value]Value),
|
||||
}
|
||||
for i, param := range fn.Params() {
|
||||
fr.locals[param] = params[i]
|
||||
}
|
||||
|
||||
bb := fn.EntryBasicBlock()
|
||||
var lastBB llvm.BasicBlock
|
||||
for {
|
||||
retval, outgoing, err := fr.evalBasicBlock(bb, lastBB, indent)
|
||||
if outgoing == nil {
|
||||
// returned something (a value or void, or an error)
|
||||
return retval, err
|
||||
}
|
||||
if len(outgoing) > 1 {
|
||||
panic("unimplemented: multiple outgoing blocks")
|
||||
}
|
||||
next := outgoing[0]
|
||||
if next.IsABasicBlock().IsNil() {
|
||||
panic("did not switch to a basic block")
|
||||
}
|
||||
lastBB = bb
|
||||
bb = next.AsBasicBlock()
|
||||
}
|
||||
}
|
||||
|
||||
// getValue determines what kind of LLVM value it gets and returns the
|
||||
// appropriate Value type.
|
||||
func (e *Eval) getValue(v llvm.Value) Value {
|
||||
return &LocalValue{e, v}
|
||||
}
|
||||
|
||||
// markDirty marks the passed-in LLVM value dirty, recursively. For example,
|
||||
// when it encounters a constant GEP on a global, it marks the global dirty.
|
||||
func (e *Eval) markDirty(v llvm.Value) {
|
||||
if !v.IsAGlobalVariable().IsNil() {
|
||||
if v.IsGlobalConstant() {
|
||||
return
|
||||
}
|
||||
if _, ok := e.dirtyGlobals[v]; !ok {
|
||||
e.dirtyGlobals[v] = struct{}{}
|
||||
e.sideEffectFuncs = nil // re-calculate all side effects
|
||||
}
|
||||
} else if v.IsConstant() {
|
||||
if v.OperandsCount() >= 2 && !v.Operand(0).IsAGlobalVariable().IsNil() {
|
||||
// looks like a constant getelementptr of a global.
|
||||
// TODO: find a way to make sure it really is: v.Opcode() returns 0.
|
||||
e.markDirty(v.Operand(0))
|
||||
return
|
||||
}
|
||||
return // nothing to mark
|
||||
} else if !v.IsAGetElementPtrInst().IsNil() {
|
||||
panic("interp: todo: GEP")
|
||||
} else {
|
||||
// Not constant and not a global or GEP so doesn't have to be marked
|
||||
// non-constant.
|
||||
// getFunction returns the compiled version of the given LLVM function. It
|
||||
// compiles the function if necessary and caches the result.
|
||||
func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
||||
if fn, ok := r.functionCache[llvmFn]; ok {
|
||||
return fn
|
||||
}
|
||||
fn := r.compileFunction(llvmFn)
|
||||
r.functionCache[llvmFn] = fn
|
||||
return fn
|
||||
}
|
||||
|
||||
+35
-2
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -41,9 +42,29 @@ func runTest(t *testing.T, pathPrefix string) {
|
||||
// Perform the transform.
|
||||
err = Run(mod, false)
|
||||
if err != nil {
|
||||
if err, match := err.(*Error); match {
|
||||
println(err.Error())
|
||||
if !err.Inst.IsNil() {
|
||||
err.Inst.Dump()
|
||||
println()
|
||||
}
|
||||
if len(err.Traceback) > 0 {
|
||||
println("\ntraceback:")
|
||||
for _, line := range err.Traceback {
|
||||
println(line.Pos.String() + ":")
|
||||
line.Inst.Dump()
|
||||
println()
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// To be sure, verify that the module is still valid.
|
||||
if llvm.VerifyModule(mod, llvm.PrintMessageAction) != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// Run some cleanup passes to get easy-to-read outputs.
|
||||
pm := llvm.NewPassManager()
|
||||
defer pm.Dispose()
|
||||
@@ -66,6 +87,8 @@ func runTest(t *testing.T, pathPrefix 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.).
|
||||
@@ -75,8 +98,18 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
||||
if len(lines1) != len(lines2) {
|
||||
return false
|
||||
}
|
||||
for i, line := range lines1 {
|
||||
if line != lines2[i] {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,917 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent string) (value, memoryView, *Error) {
|
||||
mem := memoryView{r: r, parent: parentMem}
|
||||
locals := make([]value, len(fn.locals))
|
||||
r.callsExecuted++
|
||||
|
||||
if time.Since(r.start) > time.Minute {
|
||||
// Running for more than a minute. This should never happen.
|
||||
return nil, mem, r.errorAt(fn.blocks[0].instructions[0], fmt.Errorf("interp: running for more than a minute, timing out (executed calls: %d)", r.callsExecuted))
|
||||
}
|
||||
|
||||
// Parameters are considered a kind of local values.
|
||||
for i, param := range params {
|
||||
locals[i] = param
|
||||
}
|
||||
|
||||
// Start with the first basic block and the first instruction.
|
||||
// Branch instructions may modify both bb and instIndex when branching.
|
||||
bb := fn.blocks[0]
|
||||
currentBB := 0
|
||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||
var operands []value
|
||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||
inst := bb.instructions[instIndex]
|
||||
operands = operands[:0]
|
||||
isRuntimeInst := false
|
||||
if inst.opcode != llvm.PHI {
|
||||
for _, v := range inst.operands {
|
||||
if v, ok := v.(localValue); ok {
|
||||
if localVal := locals[fn.locals[v.value]]; localVal == nil {
|
||||
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
|
||||
} else {
|
||||
operands = append(operands, localVal)
|
||||
if _, ok := localVal.(localValue); ok {
|
||||
isRuntimeInst = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
operands = append(operands, v)
|
||||
}
|
||||
}
|
||||
if isRuntimeInst {
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
switch inst.opcode {
|
||||
case llvm.Ret:
|
||||
if len(operands) != 0 {
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"ret", operands[0])
|
||||
}
|
||||
// Return instruction has a value to return.
|
||||
return operands[0], mem, nil
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"ret")
|
||||
}
|
||||
// Return instruction doesn't return anything, it's just 'ret void'.
|
||||
return nil, mem, nil
|
||||
case llvm.Br:
|
||||
switch len(operands) {
|
||||
case 1:
|
||||
// Unconditional branch: [nextBB]
|
||||
lastBB = currentBB
|
||||
currentBB = int(operands[0].(literalValue).value.(uint32))
|
||||
bb = fn.blocks[currentBB]
|
||||
instIndex = -1 // start at 0 the next cycle
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"br", operands, "->", currentBB)
|
||||
}
|
||||
case 3:
|
||||
// Conditional branch: [cond, thenBB, elseBB]
|
||||
lastBB = currentBB
|
||||
switch operands[0].Uint() {
|
||||
case 1: // true -> thenBB
|
||||
currentBB = int(operands[1].(literalValue).value.(uint32))
|
||||
case 0: // false -> elseBB
|
||||
currentBB = int(operands[2].(literalValue).value.(uint32))
|
||||
default:
|
||||
panic("bool should be 0 or 1")
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"br", operands, "->", currentBB)
|
||||
}
|
||||
bb = fn.blocks[currentBB]
|
||||
instIndex = -1 // start at 0 the next cycle
|
||||
default:
|
||||
panic("unknown operands length")
|
||||
}
|
||||
break // continue with next block
|
||||
case llvm.PHI:
|
||||
var result value
|
||||
for i := 0; i < len(inst.operands); i += 2 {
|
||||
if int(inst.operands[i].(literalValue).value.(uint32)) == lastBB {
|
||||
incoming := inst.operands[i+1]
|
||||
if local, ok := incoming.(localValue); ok {
|
||||
result = locals[fn.locals[local.value]]
|
||||
} else {
|
||||
result = incoming
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"phi", inst.operands, "->", result)
|
||||
}
|
||||
if result == nil {
|
||||
panic("could not find PHI input")
|
||||
}
|
||||
locals[inst.localIndex] = result
|
||||
case llvm.Select:
|
||||
// Select is much like a ternary operator: it picks a result from
|
||||
// the second and third operand based on the boolean first operand.
|
||||
var result value
|
||||
switch operands[0].Uint() {
|
||||
case 1:
|
||||
result = operands[1]
|
||||
case 0:
|
||||
result = operands[2]
|
||||
default:
|
||||
panic("boolean must be 0 or 1")
|
||||
}
|
||||
locals[inst.localIndex] = result
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"select", operands, "->", result)
|
||||
}
|
||||
case llvm.Call:
|
||||
// A call instruction can either be a regular call or a runtime intrinsic.
|
||||
fnPtr, err := operands[0].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
callFn := r.getFunction(fnPtr.llvmValue(&mem))
|
||||
switch {
|
||||
case callFn.name == "runtime.trackPointer":
|
||||
// Allocas and such are created as globals, so don't need a
|
||||
// runtime.trackPointer.
|
||||
// Unless the object is allocated at runtime for example, in
|
||||
// which case this call won't even get to this point but will
|
||||
// already be emitted in initAll.
|
||||
continue
|
||||
case callFn.name == "(reflect.Type).Elem" || strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
|
||||
// These functions should be run at runtime. Specifically:
|
||||
// * (reflect.Type).Elem is a special function. It should
|
||||
// eventually be interpreted, but fall back to a runtime call
|
||||
// for now.
|
||||
// * Print and panic functions are best emitted directly without
|
||||
// interpreting them, otherwise we get a ton of putchar (etc.)
|
||||
// calls.
|
||||
// * runtime.hashmapGet tries to access the map value directly.
|
||||
// This is not possible as the map value is treated as a special
|
||||
// kind of object in this package.
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
|
||||
// The time package contains a call to runtime.nanotime.
|
||||
// This appears to be to work around a limitation in Windows
|
||||
// Server 2008:
|
||||
// > Monotonic times are reported as offsets from startNano.
|
||||
// > We initialize startNano to runtimeNano() - 1 so that on systems where
|
||||
// > monotonic time resolution is fairly low (e.g. Windows 2008
|
||||
// > which appears to have a default resolution of 15ms),
|
||||
// > we avoid ever reporting a monotonic time of 0.
|
||||
// > (Callers may want to use 0 as "time not set".)
|
||||
// Simply let runtime.nanotime return 0 in this case, which
|
||||
// should be fine and avoids a call to runtime.nanotime. It
|
||||
// means that monotonic time in the time package is counted from
|
||||
// time.Time{}.Sub(1), which should be fine.
|
||||
locals[inst.localIndex] = literalValue{uint64(0)}
|
||||
case callFn.name == "runtime.alloc":
|
||||
// Allocate heap memory. At compile time, this is instead done
|
||||
// by creating a global variable.
|
||||
|
||||
// Get the requested memory size to be allocated.
|
||||
size := operands[1].Uint()
|
||||
|
||||
// Create the object.
|
||||
alloc := object{
|
||||
globalName: r.pkgName + "$alloc",
|
||||
buffer: newRawValue(uint32(size)),
|
||||
size: uint32(size),
|
||||
}
|
||||
index := len(r.objects)
|
||||
r.objects = append(r.objects, alloc)
|
||||
|
||||
// And create a pointer to this object, for working with it (so
|
||||
// that stores to it copy it, etc).
|
||||
ptr := newPointerValue(r, index, 0)
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"runtime.alloc:", size, "->", ptr)
|
||||
}
|
||||
locals[inst.localIndex] = ptr
|
||||
case callFn.name == "runtime.sliceCopy":
|
||||
// sliceCopy implements the built-in copy function for slices.
|
||||
// It is implemented here so that it can be used even if the
|
||||
// runtime implementation is not available. Doing it this way
|
||||
// may also be faster.
|
||||
// Code:
|
||||
// func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int {
|
||||
// n := srcLen
|
||||
// if n > dstLen {
|
||||
// n = dstLen
|
||||
// }
|
||||
// memmove(dst, src, n*elemSize)
|
||||
// return int(n)
|
||||
// }
|
||||
dstLen := operands[3].Uint()
|
||||
srcLen := operands[4].Uint()
|
||||
elemSize := operands[5].Uint()
|
||||
n := srcLen
|
||||
if n > dstLen {
|
||||
n = dstLen
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"copy:", operands[1], operands[2], n)
|
||||
}
|
||||
if n != 0 {
|
||||
// Only try to copy bytes when there are any bytes to copy.
|
||||
// This is not just an optimization. If one of the slices
|
||||
// (or both) are nil, the asPointer method call will fail
|
||||
// even though copying a nil slice is allowed.
|
||||
dst, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
src, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
nBytes := uint32(n * elemSize)
|
||||
dstObj := mem.getWritable(dst.index())
|
||||
dstBuf := dstObj.buffer.asRawValue(r)
|
||||
srcBuf := mem.get(src.index()).buffer.asRawValue(r)
|
||||
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
||||
dstObj.buffer = dstBuf
|
||||
mem.put(dst.index(), dstObj)
|
||||
}
|
||||
switch inst.llvmInst.Type().IntTypeWidth() {
|
||||
case 16:
|
||||
locals[inst.localIndex] = literalValue{uint16(n)}
|
||||
case 32:
|
||||
locals[inst.localIndex] = literalValue{uint32(n)}
|
||||
case 64:
|
||||
locals[inst.localIndex] = literalValue{uint64(n)}
|
||||
default:
|
||||
panic("unknown integer type width")
|
||||
}
|
||||
case strings.HasPrefix(callFn.name, "llvm.memcpy.p0i8.p0i8.") || strings.HasPrefix(callFn.name, "llvm.memmove.p0i8.p0i8."):
|
||||
// Copy a block of memory from one pointer to another.
|
||||
dst, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
src, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
nBytes := uint32(operands[3].Uint())
|
||||
dstObj := mem.getWritable(dst.index())
|
||||
dstBuf := dstObj.buffer.asRawValue(r)
|
||||
srcBuf := mem.get(src.index()).buffer.asRawValue(r)
|
||||
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
||||
dstObj.buffer = dstBuf
|
||||
mem.put(dst.index(), dstObj)
|
||||
case callFn.name == "runtime.typeAssert":
|
||||
// This function must be implemented manually as it is normally
|
||||
// implemented by the interface lowering pass.
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"typeassert:", operands[1:])
|
||||
}
|
||||
typeInInterfacePtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
actualType, err := mem.load(typeInInterfacePtr, r.pointerSize).asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
assertedType, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
result := assertedType.asRawValue(r).equal(actualType.asRawValue(r))
|
||||
if result {
|
||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
}
|
||||
case callFn.name == "runtime.interfaceImplements":
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
||||
}
|
||||
|
||||
// Load various values for the interface implements check below.
|
||||
typeInInterfacePtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSetPtr, err := mem.load(typeInInterfacePtr.addOffset(r.pointerSize), r.pointerSize).asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
|
||||
interfaceMethodSetPtr, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
interfaceMethodSet := mem.get(interfaceMethodSetPtr.index()).llvmGlobal.Initializer()
|
||||
|
||||
// Make a set of all the methods on the concrete type, for
|
||||
// easier checking in the next step.
|
||||
concreteTypeMethods := map[string]struct{}{}
|
||||
for i := 0; i < methodSet.Type().ArrayLength(); i++ {
|
||||
methodInfo := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)})
|
||||
name := llvm.ConstExtractValue(methodInfo, []uint32{0}).Name()
|
||||
concreteTypeMethods[name] = struct{}{}
|
||||
}
|
||||
|
||||
// Check whether all interface methods are also in the list
|
||||
// of defined methods calculated above. This is the interface
|
||||
// assert itself.
|
||||
assertOk := uint8(1) // i1 true
|
||||
for i := 0; i < interfaceMethodSet.Type().ArrayLength(); i++ {
|
||||
name := llvm.ConstExtractValue(interfaceMethodSet, []uint32{uint32(i)}).Name()
|
||||
if _, ok := concreteTypeMethods[name]; !ok {
|
||||
// There is a method on the interface that is not
|
||||
// implemented by the type. The assertion will fail.
|
||||
assertOk = 0 // i1 false
|
||||
break
|
||||
}
|
||||
}
|
||||
// If assertOk is still 1, the assertion succeeded.
|
||||
locals[inst.localIndex] = literalValue{assertOk}
|
||||
case callFn.name == "runtime.hashmapMake":
|
||||
// Create a new map.
|
||||
hashmapPointerType := inst.llvmInst.Type()
|
||||
keySize := uint32(operands[1].Uint())
|
||||
valueSize := uint32(operands[2].Uint())
|
||||
m := newMapValue(r, hashmapPointerType, keySize, valueSize)
|
||||
alloc := object{
|
||||
llvmType: hashmapPointerType,
|
||||
globalName: r.pkgName + "$map",
|
||||
buffer: m,
|
||||
size: m.len(r),
|
||||
}
|
||||
index := len(r.objects)
|
||||
r.objects = append(r.objects, alloc)
|
||||
|
||||
// Create a pointer to this map. Maps are reference types, so
|
||||
// are implemented as pointers.
|
||||
ptr := newPointerValue(r, index, 0)
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapMake:", keySize, valueSize, "->", ptr)
|
||||
}
|
||||
locals[inst.localIndex] = ptr
|
||||
case callFn.name == "runtime.hashmapBinarySet":
|
||||
// Do a mapassign operation with a binary key (that is, without
|
||||
// a string key).
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapBinarySet:", operands[1:])
|
||||
}
|
||||
mapPtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
m := mem.getWritable(mapPtr.index()).buffer.(*mapValue)
|
||||
keyPtr, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
valuePtr, err := operands[3].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
err = m.putBinary(&mem, keyPtr, valuePtr)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
case callFn.name == "runtime.hashmapStringSet":
|
||||
// Do a mapassign operation with a string key.
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapBinarySet:", operands[1:])
|
||||
}
|
||||
mapPtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
m := mem.getWritable(mapPtr.index()).buffer.(*mapValue)
|
||||
stringPtr, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
stringLen := operands[3].Uint()
|
||||
valuePtr, err := operands[4].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
err = m.putString(&mem, stringPtr, stringLen, valuePtr)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
default:
|
||||
if len(callFn.blocks) == 0 {
|
||||
// Call to a function declaration without a definition
|
||||
// available.
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Call a function with a definition available. Run it as usual,
|
||||
// possibly trying to recover from it if it failed to execute.
|
||||
if r.debug {
|
||||
argStrings := make([]string, len(operands)-1)
|
||||
for i := range argStrings {
|
||||
argStrings[i] = operands[i+1].String()
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, indent+"call:", callFn.name+"("+strings.Join(argStrings, ", ")+")")
|
||||
}
|
||||
retval, callMem, callErr := r.run(callFn, operands[1:], &mem, indent+" ")
|
||||
if callErr != nil {
|
||||
if isRecoverableError(callErr.Err) {
|
||||
// This error can be recovered by doing the call at
|
||||
// runtime instead of at compile time. But we need to
|
||||
// revert any changes made by the call first.
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"!! revert because of error:", callErr.Err)
|
||||
}
|
||||
callMem.revert()
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Add to the traceback, so that error handling code can see
|
||||
// how this function got called.
|
||||
callErr.Traceback = append(callErr.Traceback, ErrorLine{
|
||||
Pos: getPosition(inst.llvmInst),
|
||||
Inst: inst.llvmInst,
|
||||
})
|
||||
return nil, mem, callErr
|
||||
}
|
||||
locals[inst.localIndex] = retval
|
||||
mem.extend(callMem)
|
||||
}
|
||||
case llvm.Load:
|
||||
// Load instruction, loading some data from the topmost memory view.
|
||||
ptr, err := operands[0].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
size := operands[1].(literalValue).value.(uint64)
|
||||
if mem.hasExternalStore(ptr) {
|
||||
// If there could be an external store (for example, because a
|
||||
// pointer to the object was passed to a function that could not
|
||||
// be interpreted at compile time) then the load must be done at
|
||||
// runtime.
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
result := mem.load(ptr, uint32(size))
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"load:", ptr, "->", result)
|
||||
}
|
||||
locals[inst.localIndex] = result
|
||||
case llvm.Store:
|
||||
// Store instruction. Create a new object in the memory view and
|
||||
// store to that, to make it possible to roll back this store.
|
||||
ptr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
if mem.hasExternalLoadOrStore(ptr) {
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
val := operands[0]
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"store:", val, ptr)
|
||||
}
|
||||
mem.store(val, ptr)
|
||||
case llvm.Alloca:
|
||||
// Alloca normally allocates some stack memory. In the interpreter,
|
||||
// it allocates a global instead.
|
||||
// This can likely be optimized, as all it really needs is an alloca
|
||||
// in the initAll function and creating a global is wasteful for
|
||||
// this purpose.
|
||||
|
||||
// Create the new object.
|
||||
size := operands[0].(literalValue).value.(uint64)
|
||||
alloca := object{
|
||||
llvmType: inst.llvmInst.Type(),
|
||||
globalName: r.pkgName + "$alloca",
|
||||
buffer: newRawValue(uint32(size)),
|
||||
size: uint32(size),
|
||||
}
|
||||
index := len(r.objects)
|
||||
r.objects = append(r.objects, alloca)
|
||||
|
||||
// Create a pointer to this object (an alloca produces a pointer).
|
||||
ptr := newPointerValue(r, index, 0)
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"alloca:", operands, "->", ptr)
|
||||
}
|
||||
locals[inst.localIndex] = ptr
|
||||
case llvm.GetElementPtr:
|
||||
// GetElementPtr does pointer arithmetic, changing the offset of the
|
||||
// pointer into the underlying object.
|
||||
var offset uint64
|
||||
var gepOperands []uint64
|
||||
for i := 2; i < len(operands); i += 2 {
|
||||
index := operands[i].Uint()
|
||||
elementSize := operands[i+1].Uint()
|
||||
if int64(elementSize) < 0 {
|
||||
// This is a struct field.
|
||||
// The field number is encoded by flipping all the bits.
|
||||
gepOperands = append(gepOperands, ^elementSize)
|
||||
offset += index
|
||||
} else {
|
||||
// This is a normal GEP, probably an array index.
|
||||
gepOperands = append(gepOperands, index)
|
||||
offset += elementSize * index
|
||||
}
|
||||
}
|
||||
ptr, err := operands[0].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
ptr = ptr.addOffset(uint32(offset))
|
||||
locals[inst.localIndex] = ptr
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"gep:", operands, "->", ptr)
|
||||
}
|
||||
case llvm.BitCast, llvm.IntToPtr, llvm.PtrToInt:
|
||||
// Various bitcast-like instructions that all keep the same bits
|
||||
// while changing the LLVM type.
|
||||
// Because interp doesn't preserve the type, these operations are
|
||||
// identity operations.
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", operands[0])
|
||||
}
|
||||
locals[inst.localIndex] = operands[0]
|
||||
case llvm.ExtractValue:
|
||||
agg := operands[0].asRawValue(r)
|
||||
offset := operands[1].(literalValue).value.(uint64)
|
||||
size := operands[2].(literalValue).value.(uint64)
|
||||
elt := rawValue{
|
||||
buf: agg.buf[offset : offset+size],
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"extractvalue:", operands, "->", elt)
|
||||
}
|
||||
locals[inst.localIndex] = elt
|
||||
case llvm.InsertValue:
|
||||
agg := operands[0].asRawValue(r)
|
||||
elt := operands[1].asRawValue(r)
|
||||
offset := int(operands[2].(literalValue).value.(uint64))
|
||||
newagg := newRawValue(uint32(len(agg.buf)))
|
||||
copy(newagg.buf, agg.buf)
|
||||
copy(newagg.buf[offset:], elt.buf)
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"insertvalue:", operands, "->", newagg)
|
||||
}
|
||||
locals[inst.localIndex] = newagg
|
||||
case llvm.ICmp:
|
||||
predicate := llvm.IntPredicate(operands[2].(literalValue).value.(uint8))
|
||||
var result bool
|
||||
lhs := operands[0]
|
||||
rhs := operands[1]
|
||||
switch predicate {
|
||||
case llvm.IntEQ, llvm.IntNE:
|
||||
lhsPointer, lhsErr := lhs.asPointer(r)
|
||||
rhsPointer, rhsErr := rhs.asPointer(r)
|
||||
if (lhsErr == nil) != (rhsErr == nil) {
|
||||
// Fast path: only one is a pointer, so they can't be equal.
|
||||
result = false
|
||||
} else if lhsErr == nil {
|
||||
// Both must be nil, so both are pointers.
|
||||
// Compare them directly.
|
||||
result = lhsPointer.equal(rhsPointer)
|
||||
} else {
|
||||
// Fall back to generic comparison.
|
||||
result = lhs.asRawValue(r).equal(rhs.asRawValue(r))
|
||||
}
|
||||
if predicate == llvm.IntNE {
|
||||
result = !result
|
||||
}
|
||||
case llvm.IntUGT:
|
||||
result = lhs.Uint() > rhs.Uint()
|
||||
case llvm.IntUGE:
|
||||
result = lhs.Uint() >= rhs.Uint()
|
||||
case llvm.IntULT:
|
||||
result = lhs.Uint() < rhs.Uint()
|
||||
case llvm.IntULE:
|
||||
result = lhs.Uint() <= rhs.Uint()
|
||||
case llvm.IntSGT:
|
||||
result = lhs.Int() > rhs.Int()
|
||||
case llvm.IntSGE:
|
||||
result = lhs.Int() >= rhs.Int()
|
||||
case llvm.IntSLT:
|
||||
result = lhs.Int() < rhs.Int()
|
||||
case llvm.IntSLE:
|
||||
result = lhs.Int() <= rhs.Int()
|
||||
default:
|
||||
return nil, mem, r.errorAt(inst, errors.New("interp: unsupported icmp"))
|
||||
}
|
||||
if result {
|
||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"icmp:", operands[0], intPredicateString(predicate), operands[1], "->", result)
|
||||
}
|
||||
case llvm.FCmp:
|
||||
predicate := llvm.FloatPredicate(operands[2].(literalValue).value.(uint8))
|
||||
var result bool
|
||||
var lhs, rhs float64
|
||||
switch operands[0].len(r) {
|
||||
case 8:
|
||||
lhs = math.Float64frombits(operands[0].Uint())
|
||||
rhs = math.Float64frombits(operands[1].Uint())
|
||||
case 4:
|
||||
lhs = float64(math.Float32frombits(uint32(operands[0].Uint())))
|
||||
rhs = float64(math.Float32frombits(uint32(operands[1].Uint())))
|
||||
default:
|
||||
panic("unknown float type")
|
||||
}
|
||||
switch predicate {
|
||||
case llvm.FloatOEQ:
|
||||
result = lhs == rhs
|
||||
case llvm.FloatUNE:
|
||||
result = lhs != rhs
|
||||
case llvm.FloatOGT:
|
||||
result = lhs > rhs
|
||||
case llvm.FloatOGE:
|
||||
result = lhs >= rhs
|
||||
case llvm.FloatOLT:
|
||||
result = lhs < rhs
|
||||
case llvm.FloatOLE:
|
||||
result = lhs <= rhs
|
||||
default:
|
||||
return nil, mem, r.errorAt(inst, errors.New("interp: unsupported fcmp"))
|
||||
}
|
||||
if result {
|
||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"fcmp:", operands[0], predicate, operands[1], "->", result)
|
||||
}
|
||||
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
|
||||
// Integer binary operations.
|
||||
lhs := operands[0]
|
||||
rhs := operands[1]
|
||||
lhsPtr, err := lhs.asPointer(r)
|
||||
if err == nil {
|
||||
// The lhs is a pointer. This sometimes happens for particular
|
||||
// pointer tricks.
|
||||
switch inst.opcode {
|
||||
case llvm.Add:
|
||||
// This likely means this is part of a
|
||||
// unsafe.Pointer(uintptr(ptr) + offset) pattern.
|
||||
lhsPtr = lhsPtr.addOffset(uint32(rhs.Uint()))
|
||||
locals[inst.localIndex] = lhsPtr
|
||||
continue
|
||||
case llvm.Xor:
|
||||
if rhs.Uint() == 0 {
|
||||
// Special workaround for strings.noescape, see
|
||||
// src/strings/builder.go in the Go source tree. This is
|
||||
// the identity operator, so we can return the input.
|
||||
locals[inst.localIndex] = lhs
|
||||
continue
|
||||
}
|
||||
default:
|
||||
// Catch-all for weird operations that should just be done
|
||||
// at runtime.
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
var result uint64
|
||||
switch inst.opcode {
|
||||
case llvm.Add:
|
||||
result = lhs.Uint() + rhs.Uint()
|
||||
case llvm.Sub:
|
||||
result = lhs.Uint() - rhs.Uint()
|
||||
case llvm.Mul:
|
||||
result = lhs.Uint() * rhs.Uint()
|
||||
case llvm.UDiv:
|
||||
result = lhs.Uint() / rhs.Uint()
|
||||
case llvm.SDiv:
|
||||
result = uint64(lhs.Int() / rhs.Int())
|
||||
case llvm.URem:
|
||||
result = lhs.Uint() % rhs.Uint()
|
||||
case llvm.SRem:
|
||||
result = uint64(lhs.Int() % rhs.Int())
|
||||
case llvm.Shl:
|
||||
result = lhs.Uint() << rhs.Uint()
|
||||
case llvm.LShr:
|
||||
result = lhs.Uint() >> rhs.Uint()
|
||||
case llvm.AShr:
|
||||
result = uint64(lhs.Int() >> rhs.Uint())
|
||||
case llvm.And:
|
||||
result = lhs.Uint() & rhs.Uint()
|
||||
case llvm.Or:
|
||||
result = lhs.Uint() | rhs.Uint()
|
||||
case llvm.Xor:
|
||||
result = lhs.Uint() ^ rhs.Uint()
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
switch lhs.len(r) {
|
||||
case 8:
|
||||
locals[inst.localIndex] = literalValue{result}
|
||||
case 4:
|
||||
locals[inst.localIndex] = literalValue{uint32(result)}
|
||||
case 2:
|
||||
locals[inst.localIndex] = literalValue{uint16(result)}
|
||||
case 1:
|
||||
locals[inst.localIndex] = literalValue{uint8(result)}
|
||||
default:
|
||||
panic("unknown integer size")
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", lhs, rhs, "->", result)
|
||||
}
|
||||
case llvm.SExt, llvm.ZExt, llvm.Trunc:
|
||||
// Change the size of an integer to a larger or smaller bit width.
|
||||
// We make use of the fact that the Uint() function already
|
||||
// zero-extends the value and that Int() already sign-extends the
|
||||
// value, so we only need to truncate it to the appropriate bit
|
||||
// width. This means we can implement sext, zext and trunc in the
|
||||
// same way, by first {zero,sign}extending all the way up to uint64
|
||||
// and then truncating it as necessary.
|
||||
var value uint64
|
||||
if inst.opcode == llvm.SExt {
|
||||
value = uint64(operands[0].Int())
|
||||
} else {
|
||||
value = operands[0].Uint()
|
||||
}
|
||||
bitwidth := operands[1].Uint()
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth)
|
||||
}
|
||||
switch bitwidth {
|
||||
case 64:
|
||||
locals[inst.localIndex] = literalValue{value}
|
||||
case 32:
|
||||
locals[inst.localIndex] = literalValue{uint32(value)}
|
||||
case 16:
|
||||
locals[inst.localIndex] = literalValue{uint16(value)}
|
||||
case 8:
|
||||
locals[inst.localIndex] = literalValue{uint8(value)}
|
||||
default:
|
||||
panic("unknown integer size in sext/zext/trunc")
|
||||
}
|
||||
case llvm.SIToFP, llvm.UIToFP:
|
||||
var value float64
|
||||
switch inst.opcode {
|
||||
case llvm.SIToFP:
|
||||
value = float64(operands[0].Int())
|
||||
case llvm.UIToFP:
|
||||
value = float64(operands[0].Uint())
|
||||
}
|
||||
bitwidth := operands[1].Uint()
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+instructionNameMap[inst.opcode]+":", value, bitwidth)
|
||||
}
|
||||
switch bitwidth {
|
||||
case 64:
|
||||
locals[inst.localIndex] = literalValue{math.Float64bits(value)}
|
||||
case 32:
|
||||
locals[inst.localIndex] = literalValue{math.Float32bits(float32(value))}
|
||||
default:
|
||||
panic("unknown integer size in sitofp/uitofp")
|
||||
}
|
||||
default:
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+inst.String())
|
||||
}
|
||||
return nil, mem, r.errorAt(inst, errUnsupportedInst)
|
||||
}
|
||||
}
|
||||
return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator"))
|
||||
}
|
||||
|
||||
func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, mem *memoryView, indent string) *Error {
|
||||
numOperands := inst.llvmInst.OperandsCount()
|
||||
operands := make([]llvm.Value, numOperands)
|
||||
for i := 0; i < numOperands; i++ {
|
||||
operand := inst.llvmInst.Operand(i)
|
||||
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
|
||||
operand = locals[fn.locals[operand]].toLLVMValue(operand.Type(), mem)
|
||||
}
|
||||
operands[i] = operand
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+inst.String())
|
||||
}
|
||||
var result llvm.Value
|
||||
switch inst.opcode {
|
||||
case llvm.Call:
|
||||
llvmFn := operands[len(operands)-1]
|
||||
args := operands[:len(operands)-1]
|
||||
for _, arg := range args {
|
||||
if arg.Type().TypeKind() == llvm.PointerTypeKind {
|
||||
mem.markExternalStore(arg)
|
||||
}
|
||||
}
|
||||
result = r.builder.CreateCall(llvmFn, args, inst.name)
|
||||
case llvm.Load:
|
||||
mem.markExternalLoad(operands[0])
|
||||
result = r.builder.CreateLoad(operands[0], inst.name)
|
||||
if inst.llvmInst.IsVolatile() {
|
||||
result.SetVolatile(true)
|
||||
}
|
||||
case llvm.Store:
|
||||
mem.markExternalStore(operands[1])
|
||||
result = r.builder.CreateStore(operands[0], operands[1])
|
||||
if inst.llvmInst.IsVolatile() {
|
||||
result.SetVolatile(true)
|
||||
}
|
||||
case llvm.BitCast:
|
||||
result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
|
||||
case llvm.ExtractValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
}
|
||||
result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name)
|
||||
case llvm.InsertValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
}
|
||||
result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
|
||||
case llvm.Add:
|
||||
result = r.builder.CreateAdd(operands[0], operands[1], inst.name)
|
||||
case llvm.Sub:
|
||||
result = r.builder.CreateSub(operands[0], operands[1], inst.name)
|
||||
case llvm.Mul:
|
||||
result = r.builder.CreateMul(operands[0], operands[1], inst.name)
|
||||
case llvm.UDiv:
|
||||
result = r.builder.CreateUDiv(operands[0], operands[1], inst.name)
|
||||
case llvm.SDiv:
|
||||
result = r.builder.CreateSDiv(operands[0], operands[1], inst.name)
|
||||
case llvm.URem:
|
||||
result = r.builder.CreateURem(operands[0], operands[1], inst.name)
|
||||
case llvm.SRem:
|
||||
result = r.builder.CreateSRem(operands[0], operands[1], inst.name)
|
||||
case llvm.ZExt:
|
||||
result = r.builder.CreateZExt(operands[0], inst.llvmInst.Type(), inst.name)
|
||||
default:
|
||||
return r.errorAt(inst, errUnsupportedRuntimeInst)
|
||||
}
|
||||
locals[inst.localIndex] = localValue{result}
|
||||
mem.instructions = append(mem.instructions, result)
|
||||
return nil
|
||||
}
|
||||
|
||||
func intPredicateString(predicate llvm.IntPredicate) string {
|
||||
switch predicate {
|
||||
case llvm.IntEQ:
|
||||
return "eq"
|
||||
case llvm.IntNE:
|
||||
return "ne"
|
||||
case llvm.IntUGT:
|
||||
return "ugt"
|
||||
case llvm.IntUGE:
|
||||
return "uge"
|
||||
case llvm.IntULT:
|
||||
return "ult"
|
||||
case llvm.IntULE:
|
||||
return "ule"
|
||||
case llvm.IntSGT:
|
||||
return "sgt"
|
||||
case llvm.IntSGE:
|
||||
return "sge"
|
||||
case llvm.IntSLT:
|
||||
return "slt"
|
||||
case llvm.IntSLE:
|
||||
return "sle"
|
||||
default:
|
||||
return "cmp?"
|
||||
}
|
||||
}
|
||||
+1430
File diff suppressed because it is too large
Load Diff
-259
@@ -1,259 +0,0 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
type sideEffectSeverity int
|
||||
|
||||
func (severity sideEffectSeverity) String() string {
|
||||
switch severity {
|
||||
case sideEffectInProgress:
|
||||
return "in progress"
|
||||
case sideEffectNone:
|
||||
return "none"
|
||||
case sideEffectLimited:
|
||||
return "limited"
|
||||
case sideEffectAll:
|
||||
return "all"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
sideEffectInProgress sideEffectSeverity = iota // computing side effects is in progress (for recursive functions)
|
||||
sideEffectNone // no side effects at all (pure)
|
||||
sideEffectLimited // has side effects, but the effects are known
|
||||
sideEffectAll // has unknown side effects
|
||||
)
|
||||
|
||||
// sideEffectResult contains the scan results after scanning a function for side
|
||||
// effects (recursively).
|
||||
type sideEffectResult struct {
|
||||
severity sideEffectSeverity
|
||||
mentionsGlobals map[llvm.Value]struct{}
|
||||
}
|
||||
|
||||
// hasSideEffects scans this function and all descendants, recursively. It
|
||||
// returns whether this function has side effects and if it does, which globals
|
||||
// it mentions anywhere in this function or any called functions.
|
||||
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, *Error) {
|
||||
name := fn.Name()
|
||||
switch {
|
||||
case name == "runtime.alloc":
|
||||
// Cannot be scanned but can be interpreted.
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case name == "runtime.nanotime":
|
||||
// Fixed value at compile time.
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case name == "runtime._panic":
|
||||
return &sideEffectResult{severity: sideEffectLimited}, nil
|
||||
case name == "runtime.typeAssert":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case name == "runtime.interfaceImplements":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case name == "runtime.sliceCopy":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case name == "runtime.trackPointer":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case name == "llvm.dbg.value":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case name == "(*sync/atomic.Value).Load" || name == "(*sync/atomic.Value).Store":
|
||||
// These functions do some unsafe pointer loading/storing but are
|
||||
// otherwise safe.
|
||||
return &sideEffectResult{severity: sideEffectLimited}, nil
|
||||
case strings.HasPrefix(name, "llvm.lifetime."):
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
}
|
||||
if fn.IsDeclaration() {
|
||||
return &sideEffectResult{severity: sideEffectLimited}, nil
|
||||
}
|
||||
if e.sideEffectFuncs == nil {
|
||||
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
|
||||
}
|
||||
if se, ok := e.sideEffectFuncs[fn]; ok {
|
||||
return se, nil
|
||||
}
|
||||
result := &sideEffectResult{
|
||||
severity: sideEffectInProgress,
|
||||
mentionsGlobals: map[llvm.Value]struct{}{},
|
||||
}
|
||||
e.sideEffectFuncs[fn] = result
|
||||
dirtyLocals := map[llvm.Value]struct{}{}
|
||||
for bb := fn.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if inst.IsAInstruction().IsNil() {
|
||||
// Should not happen in valid IR.
|
||||
panic("not an instruction")
|
||||
}
|
||||
|
||||
// Check for any globals mentioned anywhere in the function. Assume
|
||||
// any mentioned globals may be read from or written to when
|
||||
// executed, thus must be marked dirty with a call.
|
||||
for i := 0; i < inst.OperandsCount(); i++ {
|
||||
operand := inst.Operand(i)
|
||||
if !operand.IsAGlobalVariable().IsNil() {
|
||||
result.mentionsGlobals[operand] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
switch inst.InstructionOpcode() {
|
||||
case llvm.IndirectBr, llvm.Invoke:
|
||||
// Not emitted by the compiler.
|
||||
return nil, e.errorAt(inst, errors.New("unknown instructions"))
|
||||
case llvm.Call:
|
||||
child := inst.CalledValue()
|
||||
if !child.IsAInlineAsm().IsNil() {
|
||||
// Inline assembly. This most likely has side effects.
|
||||
// Assume they're only limited side effects, similar to
|
||||
// external function calls.
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
continue
|
||||
}
|
||||
if child.IsAFunction().IsNil() {
|
||||
// Indirect call?
|
||||
// In any case, we can't know anything here about what it
|
||||
// affects exactly so mark this function as invoking all
|
||||
// possible side effects.
|
||||
result.updateSeverity(sideEffectAll)
|
||||
continue
|
||||
}
|
||||
if child.IsDeclaration() {
|
||||
// External function call. Assume only limited side effects
|
||||
// (no affected globals, etc.).
|
||||
switch child.Name() {
|
||||
case "runtime.alloc":
|
||||
continue
|
||||
case "runtime.typeAssert":
|
||||
continue // implemented in interp
|
||||
case "runtime.interfaceImplements":
|
||||
continue // implemented in interp
|
||||
}
|
||||
if e.hasLocalSideEffects(dirtyLocals, inst) {
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
}
|
||||
continue
|
||||
}
|
||||
childSideEffects, err := e.hasSideEffects(child)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch childSideEffects.severity {
|
||||
case sideEffectInProgress, sideEffectNone:
|
||||
// no side effects or recursive function - continue scanning
|
||||
case sideEffectLimited:
|
||||
// The return value may be problematic.
|
||||
if e.hasLocalSideEffects(dirtyLocals, inst) {
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
}
|
||||
case sideEffectAll:
|
||||
result.updateSeverity(sideEffectAll)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
case llvm.Load:
|
||||
if inst.IsVolatile() {
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
}
|
||||
if _, ok := e.dirtyGlobals[inst.Operand(0)]; ok {
|
||||
if e.hasLocalSideEffects(dirtyLocals, inst) {
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
}
|
||||
}
|
||||
case llvm.Store:
|
||||
if inst.IsVolatile() {
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
}
|
||||
case llvm.IntToPtr:
|
||||
// Pointer casts are not yet supported.
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
default:
|
||||
// Ignore most instructions.
|
||||
// Check this list for completeness:
|
||||
// https://godoc.org/github.com/llvm-mirror/llvm/bindings/go/llvm#Opcode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.severity == sideEffectInProgress {
|
||||
// No side effect was reported for this function.
|
||||
result.severity = sideEffectNone
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// hasLocalSideEffects checks whether the given instruction flows into a branch
|
||||
// or return instruction, in which case the whole function must be marked as
|
||||
// having side effects and be called at runtime.
|
||||
func (e *Eval) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct{}, inst llvm.Value) bool {
|
||||
if _, ok := dirtyLocals[inst]; ok {
|
||||
// It is already known that this local is dirty.
|
||||
return true
|
||||
}
|
||||
|
||||
for use := inst.FirstUse(); !use.IsNil(); use = use.NextUse() {
|
||||
user := use.User()
|
||||
if user.IsAInstruction().IsNil() {
|
||||
// Should not happen in valid IR.
|
||||
panic("user not an instruction")
|
||||
}
|
||||
switch user.InstructionOpcode() {
|
||||
case llvm.Br, llvm.Switch:
|
||||
// A branch on a dirty value makes this function dirty: it cannot be
|
||||
// interpreted at compile time so has to be run at runtime. It is
|
||||
// marked as having side effects for this reason.
|
||||
return true
|
||||
case llvm.Ret:
|
||||
// This function returns a dirty value so it is itself marked as
|
||||
// dirty to make sure it is called at runtime.
|
||||
return true
|
||||
case llvm.Store:
|
||||
ptr := user.Operand(1)
|
||||
if !ptr.IsAGlobalVariable().IsNil() {
|
||||
// Store to a global variable.
|
||||
// Already handled in (*Eval).hasSideEffects.
|
||||
continue
|
||||
}
|
||||
// This store might affect all kinds of values. While it is
|
||||
// certainly possible to traverse through all of them, the easiest
|
||||
// option right now is to just assume the worst and say that this
|
||||
// function has side effects.
|
||||
// TODO: traverse through all stores and mark all relevant allocas /
|
||||
// globals dirty.
|
||||
return true
|
||||
default:
|
||||
// All instructions that take 0 or more operands (1 or more if it
|
||||
// was a use) and produce a result.
|
||||
// For a list:
|
||||
// https://godoc.org/github.com/llvm-mirror/llvm/bindings/go/llvm#Opcode
|
||||
dirtyLocals[user] = struct{}{}
|
||||
if e.hasLocalSideEffects(dirtyLocals, user) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No side effects found.
|
||||
return false
|
||||
}
|
||||
|
||||
// updateSeverity sets r.severity to the max of r.severity and severity,
|
||||
// conservatively assuming the worst severity.
|
||||
func (r *sideEffectResult) updateSeverity(severity sideEffectSeverity) {
|
||||
if severity > r.severity {
|
||||
r.severity = severity
|
||||
}
|
||||
}
|
||||
|
||||
// updateSeverity updates the severity with the severity of the child severity,
|
||||
// like in a function call. This means it also copies the mentioned globals.
|
||||
func (r *sideEffectResult) update(child *sideEffectResult) {
|
||||
r.updateSeverity(child.severity)
|
||||
for global := range child.mentionsGlobals {
|
||||
r.mentionsGlobals[global] = struct{}{}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
var scanTestTable = []struct {
|
||||
name string
|
||||
severity sideEffectSeverity
|
||||
mentionsGlobals []string
|
||||
}{
|
||||
{"returnsConst", sideEffectNone, nil},
|
||||
{"returnsArg", sideEffectNone, nil},
|
||||
{"externalCallOnly", sideEffectNone, nil},
|
||||
{"externalCallAndReturn", sideEffectLimited, nil},
|
||||
{"externalCallBranch", sideEffectLimited, nil},
|
||||
{"readCleanGlobal", sideEffectNone, []string{"cleanGlobalInt"}},
|
||||
{"readDirtyGlobal", sideEffectLimited, []string{"dirtyGlobalInt"}},
|
||||
{"callFunctionPointer", sideEffectAll, []string{"functionPointer"}},
|
||||
{"getDirtyPointer", sideEffectLimited, nil},
|
||||
{"storeToPointer", sideEffectLimited, nil},
|
||||
{"callTypeAssert", sideEffectNone, nil},
|
||||
{"callInterfaceImplements", sideEffectNone, nil},
|
||||
}
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Read the input IR.
|
||||
path := "testdata/scan.ll"
|
||||
ctx := llvm.NewContext()
|
||||
buf, err := llvm.NewMemoryBufferFromFile(path)
|
||||
os.Stat(path) // make sure this file is tracked by `go test` caching
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file %s: %v", path, err)
|
||||
}
|
||||
mod, err := ctx.ParseIR(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("could not load module:\n%v", err)
|
||||
}
|
||||
|
||||
// Check all to-be-tested functions.
|
||||
for _, tc := range scanTestTable {
|
||||
// Create an eval object, for testing.
|
||||
e := &Eval{
|
||||
Mod: mod,
|
||||
TargetData: llvm.NewTargetData(mod.DataLayout()),
|
||||
dirtyGlobals: map[llvm.Value]struct{}{},
|
||||
}
|
||||
|
||||
// Mark some globals dirty, for testing.
|
||||
e.markDirty(mod.NamedGlobal("dirtyGlobalInt"))
|
||||
|
||||
// Scan for side effects.
|
||||
fn := mod.NamedFunction(tc.name)
|
||||
if fn.IsNil() {
|
||||
t.Errorf("scan test: could not find tested function %s in the IR", tc.name)
|
||||
continue
|
||||
}
|
||||
evalPkg := &evalPackage{e, "testdata"}
|
||||
result, err := evalPkg.hasSideEffects(fn)
|
||||
if err != nil {
|
||||
t.Errorf("scan test: failed to scan %s for side effects: %v", fn.Name(), err)
|
||||
}
|
||||
|
||||
// Check whether the result is what we expect.
|
||||
if result.severity != tc.severity {
|
||||
t.Errorf("scan test: function %s should have severity %s but it has %s", tc.name, tc.severity, result.severity)
|
||||
}
|
||||
|
||||
// Check whether the mentioned globals match with what we'd expect.
|
||||
mentionsGlobalNames := make([]string, 0, len(result.mentionsGlobals))
|
||||
for global := range result.mentionsGlobals {
|
||||
mentionsGlobalNames = append(mentionsGlobalNames, global.Name())
|
||||
}
|
||||
sort.Strings(mentionsGlobalNames)
|
||||
globalsMismatch := false
|
||||
if len(result.mentionsGlobals) != len(tc.mentionsGlobals) {
|
||||
globalsMismatch = true
|
||||
} else {
|
||||
for i, globalName := range mentionsGlobalNames {
|
||||
if tc.mentionsGlobals[i] != globalName {
|
||||
globalsMismatch = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if globalsMismatch {
|
||||
t.Errorf("scan test: expected %s to mention globals %v, but it mentions globals %v", tc.name, tc.mentionsGlobals, mentionsGlobalNames)
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+29
@@ -4,6 +4,10 @@ target triple = "x86_64--linux"
|
||||
@main.v1 = internal global i64 0
|
||||
@main.nonConst1 = global [4 x i64] zeroinitializer
|
||||
@main.nonConst2 = global i64 0
|
||||
@main.someArray = global [8 x {i16, i32}] zeroinitializer
|
||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = global i16 0
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
@@ -47,6 +51,20 @@ entry:
|
||||
%value2 = load i64, i64* %gep2
|
||||
store i64 %value2, i64* @main.nonConst2
|
||||
|
||||
; Test that the following GEP works:
|
||||
; var someArray
|
||||
; modifyExternal(&someArray[3].field1)
|
||||
%gep3 = getelementptr [8 x {i16, i32}], [8 x {i16, i32}]* @main.someArray, i32 0, i32 3, i32 1
|
||||
call void @modifyExternal(i32* %gep3)
|
||||
|
||||
; Test that marking a value as external also marks all referenced values.
|
||||
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
|
||||
store i16 5, i16* @main.exposedValue1
|
||||
|
||||
; Test that this even propagates through functions.
|
||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||
store i16 7, i16* @main.exposedValue2
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -58,3 +76,14 @@ entry:
|
||||
}
|
||||
|
||||
declare i64 @someValue()
|
||||
|
||||
declare void @modifyExternal(i32*)
|
||||
|
||||
; This function will modify an external value. By passing this function as a
|
||||
; function pointer to an external function, @main.exposedValue2 should be
|
||||
; marked as external.
|
||||
define void @willModifyGlobal() {
|
||||
entry:
|
||||
store i16 8, i16* @main.exposedValue2
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
+17
@@ -3,6 +3,10 @@ target triple = "x86_64--linux"
|
||||
|
||||
@main.nonConst1 = local_unnamed_addr global [4 x i64] zeroinitializer
|
||||
@main.nonConst2 = local_unnamed_addr global i64 0
|
||||
@main.someArray = global [8 x { i16, i32 }] zeroinitializer
|
||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = local_unnamed_addr global i16 0
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
@@ -16,6 +20,11 @@ entry:
|
||||
store i64 %value1, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0)
|
||||
%value2 = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @main.nonConst1, i32 0, i32 0)
|
||||
store i64 %value2, i64* @main.nonConst2
|
||||
call void @modifyExternal(i32* getelementptr inbounds ([8 x { i16, i32 }], [8 x { i16, i32 }]* @main.someArray, i32 0, i32 3, i32 1))
|
||||
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
|
||||
store i16 5, i16* @main.exposedValue1
|
||||
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
|
||||
store i16 7, i16* @main.exposedValue2
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -27,3 +36,11 @@ entry:
|
||||
}
|
||||
|
||||
declare i64 @someValue() local_unnamed_addr
|
||||
|
||||
declare void @modifyExternal(i32*) local_unnamed_addr
|
||||
|
||||
define void @willModifyGlobal() {
|
||||
entry:
|
||||
store i16 8, i16* @main.exposedValue2
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
+2
-4
@@ -48,8 +48,7 @@ entry:
|
||||
define internal void @main.testNonConstantBinarySet() {
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global. This breaks the normal hashmapBinarySet
|
||||
; optimization, to test the fallback.
|
||||
; Create hashmap from global.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
|
||||
@@ -64,8 +63,7 @@ define internal void @main.testNonConstantBinarySet() {
|
||||
; operations (with string keys).
|
||||
define internal void @main.testNonConstantStringSet() {
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global. This breaks the normal hashmapStringSet
|
||||
; optimization, to test the fallback.
|
||||
; Create hashmap from global.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
|
||||
|
||||
Vendored
+8
-16
@@ -2,27 +2,19 @@ target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
|
||||
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.4"
|
||||
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.6"
|
||||
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.1"
|
||||
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.3"
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, [8 x i8] c"\01\00\00\00\00\00\00\00", [8 x %runtime._string] [%runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer] }
|
||||
@"main$map" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }, { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
|
||||
@"main$alloca.2" = internal global i8 1
|
||||
@"main$alloca.3" = internal global i8 2
|
||||
@"main$map.4" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 1, i8 1, i8 0 }
|
||||
@"main$alloca.5" = internal global i8 2
|
||||
@"main$map.6" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 8, i8 1, i8 0 }
|
||||
|
||||
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8*, i8*) local_unnamed_addr
|
||||
|
||||
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8*, i8*) local_unnamed_addr
|
||||
@"main$map" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
|
||||
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer } }
|
||||
@"main$map.1" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.2", i32 0, i32 0, i32 0), i32 1, i8 1, i8 1, i8 0 }
|
||||
@"main$mapbucket.2" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
|
||||
@"main$map.3" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.4", i32 0, i32 0, i32 0), i32 1, i8 8, i8 1, i8 0 }
|
||||
@"main$mapbucket.4" = internal unnamed_addr global { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"x\00\00\00\00\00\00\00", i8* null, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* @"main$map.4", i8* @"main$alloca.2", i8* @"main$alloca.3", i8* undef, i8* null)
|
||||
call void @runtime.hashmapStringSet(%runtime.hashmap* @"main$map.6", i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* @"main$alloca.5", i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
-78
@@ -1,78 +0,0 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i64 }
|
||||
|
||||
declare i1 @runtime.typeAssert(i64, %runtime.typecodeID*, i8*, i8*)
|
||||
declare i1 @runtime.interfaceImplements(i64, i8**)
|
||||
|
||||
define i64 @returnsConst() {
|
||||
ret i64 0
|
||||
}
|
||||
|
||||
define i64 @returnsArg(i64 %arg) {
|
||||
ret i64 %arg
|
||||
}
|
||||
|
||||
declare i64 @externalCall()
|
||||
|
||||
define i64 @externalCallOnly() {
|
||||
%result = call i64 @externalCall()
|
||||
ret i64 0
|
||||
}
|
||||
|
||||
define i64 @externalCallAndReturn() {
|
||||
%result = call i64 @externalCall()
|
||||
ret i64 %result
|
||||
}
|
||||
|
||||
define i64 @externalCallBranch() {
|
||||
%result = call i64 @externalCall()
|
||||
%zero = icmp eq i64 %result, 0
|
||||
br i1 %zero, label %if.then, label %if.done
|
||||
|
||||
if.then:
|
||||
ret i64 2
|
||||
|
||||
if.done:
|
||||
ret i64 4
|
||||
}
|
||||
|
||||
@cleanGlobalInt = global i64 5
|
||||
define i64 @readCleanGlobal() {
|
||||
%global = load i64, i64* @cleanGlobalInt
|
||||
ret i64 %global
|
||||
}
|
||||
|
||||
@dirtyGlobalInt = global i64 5
|
||||
define i64 @readDirtyGlobal() {
|
||||
%global = load i64, i64* @dirtyGlobalInt
|
||||
ret i64 %global
|
||||
}
|
||||
|
||||
declare i64* @getDirtyPointer()
|
||||
|
||||
define void @storeToPointer() {
|
||||
%ptr = call i64* @getDirtyPointer()
|
||||
store i64 3, i64* %ptr
|
||||
ret void
|
||||
}
|
||||
|
||||
@functionPointer = global i64()* null
|
||||
define i64 @callFunctionPointer() {
|
||||
%fp = load i64()*, i64()** @functionPointer
|
||||
%result = call i64 %fp()
|
||||
ret i64 %result
|
||||
}
|
||||
|
||||
define i1 @callTypeAssert() {
|
||||
; Note: parameters are not realistic.
|
||||
%ok = call i1 @runtime.typeAssert(i64 0, %runtime.typecodeID* null, i8* undef, i8* null)
|
||||
ret i1 %ok
|
||||
}
|
||||
|
||||
define i1 @callInterfaceImplements() {
|
||||
; Note: parameters are not realistic.
|
||||
%ok = call i1 @runtime.interfaceImplements(i64 0, i8** null)
|
||||
ret i1 %ok
|
||||
}
|
||||
Vendored
+4
-1
@@ -1,6 +1,8 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@"main$alloc.1" = internal unnamed_addr constant [6 x i8] c"\05\00{\00\00\04"
|
||||
|
||||
declare void @runtime.printuint8(i8) local_unnamed_addr
|
||||
|
||||
declare void @runtime.printint16(i16) local_unnamed_addr
|
||||
@@ -15,6 +17,7 @@ entry:
|
||||
call void @runtime.printuint8(i8 3)
|
||||
call void @runtime.printuint8(i8 3)
|
||||
call void @runtime.printint16(i16 5)
|
||||
call void @runtime.printint16(i16 5)
|
||||
%int16SliceDst.val = load i16, i16* bitcast ([6 x i8]* @"main$alloc.1" to i16*)
|
||||
call void @runtime.printint16(i16 %int16SliceDst.val)
|
||||
ret void
|
||||
}
|
||||
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Return a list of values (actually, instructions) where this value is used as
|
||||
// an operand.
|
||||
func getUses(value llvm.Value) []llvm.Value {
|
||||
var uses []llvm.Value
|
||||
use := value.FirstUse()
|
||||
for !use.IsNil() {
|
||||
uses = append(uses, use.User())
|
||||
use = use.NextUse()
|
||||
}
|
||||
return uses
|
||||
}
|
||||
|
||||
// getStringBytes loads the byte slice of a Go string represented as a
|
||||
// {ptr, len} pair.
|
||||
func getStringBytes(strPtr Value, strLen llvm.Value) ([]byte, error) {
|
||||
if !strLen.IsConstant() {
|
||||
return nil, errors.New("getStringBytes with a non-constant length")
|
||||
}
|
||||
buf := make([]byte, strLen.ZExtValue())
|
||||
for i := range buf {
|
||||
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := gep.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf[i] = byte(c.ZExtValue())
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
|
||||
// llvm.ConstGEP.
|
||||
func getLLVMIndices(int32Type llvm.Type, indices []uint32) []llvm.Value {
|
||||
llvmIndices := make([]llvm.Value, len(indices))
|
||||
for i, index := range indices {
|
||||
llvmIndices[i] = llvm.ConstInt(int32Type, uint64(index), false)
|
||||
}
|
||||
return llvmIndices
|
||||
}
|
||||
|
||||
// Return true if this type is a scalar value (integer or floating point), false
|
||||
// otherwise.
|
||||
func isScalar(t llvm.Type) bool {
|
||||
switch t.TypeKind() {
|
||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isPointerNil returns whether this is a nil pointer or not. The ok value
|
||||
// indicates whether the result is certain: if it is false the result boolean is
|
||||
// not valid.
|
||||
func isPointerNil(v llvm.Value) (result bool, ok bool) {
|
||||
if !v.IsAConstantExpr().IsNil() {
|
||||
switch v.Opcode() {
|
||||
case llvm.IntToPtr:
|
||||
// Whether a constant inttoptr is nil is easy to
|
||||
// determine.
|
||||
result, ok = isZero(v.Operand(0))
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
case llvm.BitCast, llvm.GetElementPtr:
|
||||
// These const instructions are just a kind of wrappers for the
|
||||
// underlying pointer.
|
||||
return isPointerNil(v.Operand(0))
|
||||
}
|
||||
}
|
||||
if !v.IsAConstantPointerNull().IsNil() {
|
||||
// A constant pointer null is always null, of course.
|
||||
return true, true
|
||||
}
|
||||
if !v.IsAGlobalValue().IsNil() {
|
||||
// A global value is never null.
|
||||
return false, true
|
||||
}
|
||||
return false, false // not valid
|
||||
}
|
||||
|
||||
// isZero returns whether the value in v is the integer zero, and whether that
|
||||
// can be known right now.
|
||||
func isZero(v llvm.Value) (result bool, ok bool) {
|
||||
if !v.IsAConstantExpr().IsNil() {
|
||||
switch v.Opcode() {
|
||||
case llvm.PtrToInt:
|
||||
return isPointerNil(v.Operand(0))
|
||||
}
|
||||
}
|
||||
if !v.IsAConstantInt().IsNil() {
|
||||
val := v.ZExtValue()
|
||||
return val == 0, true
|
||||
}
|
||||
return false, false // not valid
|
||||
}
|
||||
|
||||
// unwrap returns the underlying value, with GEPs removed. This can be useful to
|
||||
// get the underlying global of a GEP pointer.
|
||||
func unwrap(value llvm.Value) llvm.Value {
|
||||
for {
|
||||
if !value.IsAConstantExpr().IsNil() {
|
||||
switch value.Opcode() {
|
||||
case llvm.GetElementPtr:
|
||||
value = value.Operand(0)
|
||||
continue
|
||||
}
|
||||
} else if !value.IsAGetElementPtrInst().IsNil() {
|
||||
value = value.Operand(0)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -1,443 +0,0 @@
|
||||
package interp
|
||||
|
||||
// This file provides a litte bit of abstraction around LLVM values.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// A Value is a LLVM value with some extra methods attached for easier
|
||||
// interpretation.
|
||||
type Value interface {
|
||||
Value() llvm.Value // returns a LLVM value
|
||||
Type() llvm.Type // equal to Value().Type()
|
||||
IsConstant() bool // returns true if this value is a constant value
|
||||
Load() (llvm.Value, error) // dereference a pointer
|
||||
Store(llvm.Value) error // store to a pointer
|
||||
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
||||
String() string // string representation, for debugging
|
||||
}
|
||||
|
||||
// A type that simply wraps a LLVM constant value.
|
||||
type LocalValue struct {
|
||||
Eval *Eval
|
||||
Underlying llvm.Value
|
||||
}
|
||||
|
||||
// Value implements Value by returning the constant value itself.
|
||||
func (v *LocalValue) Value() llvm.Value {
|
||||
return v.Underlying
|
||||
}
|
||||
|
||||
func (v *LocalValue) Type() llvm.Type {
|
||||
return v.Underlying.Type()
|
||||
}
|
||||
|
||||
func (v *LocalValue) IsConstant() bool {
|
||||
if _, ok := v.Eval.dirtyGlobals[unwrap(v.Underlying)]; ok {
|
||||
return false
|
||||
}
|
||||
return v.Underlying.IsConstant()
|
||||
}
|
||||
|
||||
// Load loads a constant value if this is a constant pointer.
|
||||
func (v *LocalValue) Load() (llvm.Value, error) {
|
||||
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||
return v.Underlying.Initializer(), nil
|
||||
}
|
||||
switch v.Underlying.Opcode() {
|
||||
case llvm.GetElementPtr:
|
||||
indices := v.getConstGEPIndices()
|
||||
if indices[0] != 0 {
|
||||
return llvm.Value{}, errors.New("invalid GEP")
|
||||
}
|
||||
global := v.Eval.getValue(v.Underlying.Operand(0))
|
||||
agg, err := global.Load()
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstExtractValue(agg, indices[1:]), nil
|
||||
case llvm.BitCast:
|
||||
return llvm.Value{}, errors.New("interp: load from a bitcast")
|
||||
default:
|
||||
return llvm.Value{}, errors.New("interp: load from a constant")
|
||||
}
|
||||
}
|
||||
|
||||
// Store stores to the underlying value if the value type is a pointer type,
|
||||
// otherwise it returns an error.
|
||||
func (v *LocalValue) Store(value llvm.Value) error {
|
||||
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||
if !value.IsConstant() {
|
||||
v.MarkDirty()
|
||||
v.Eval.builder.CreateStore(value, v.Underlying)
|
||||
} else {
|
||||
v.Underlying.SetInitializer(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !value.IsConstant() {
|
||||
v.MarkDirty()
|
||||
v.Eval.builder.CreateStore(value, v.Underlying)
|
||||
return nil
|
||||
}
|
||||
switch v.Underlying.Opcode() {
|
||||
case llvm.GetElementPtr:
|
||||
indices := v.getConstGEPIndices()
|
||||
if indices[0] != 0 {
|
||||
return errors.New("invalid GEP")
|
||||
}
|
||||
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
|
||||
agg, err := global.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
agg = llvm.ConstInsertValue(agg, value, indices[1:])
|
||||
return global.Store(agg)
|
||||
default:
|
||||
return errors.New("interp: store on a constant")
|
||||
}
|
||||
}
|
||||
|
||||
// GetElementPtr returns a GEP when the underlying value is of pointer type.
|
||||
func (v *LocalValue) GetElementPtr(indices []uint32) (Value, error) {
|
||||
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||
int32Type := v.Underlying.Type().Context().Int32Type()
|
||||
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
|
||||
return &LocalValue{v.Eval, gep}, nil
|
||||
}
|
||||
if !v.Underlying.IsAConstantExpr().IsNil() {
|
||||
switch v.Underlying.Opcode() {
|
||||
case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast:
|
||||
int32Type := v.Underlying.Type().Context().Int32Type()
|
||||
llvmIndices := getLLVMIndices(int32Type, indices)
|
||||
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("interp: unknown GEP")
|
||||
}
|
||||
|
||||
// stripPointerCasts removes all const bitcasts from pointer values, if there
|
||||
// are any.
|
||||
func (v *LocalValue) stripPointerCasts() *LocalValue {
|
||||
value := v.Underlying
|
||||
for {
|
||||
if !value.IsAConstantExpr().IsNil() {
|
||||
switch value.Opcode() {
|
||||
case llvm.BitCast:
|
||||
value = value.Operand(0)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return &LocalValue{
|
||||
Eval: v.Eval,
|
||||
Underlying: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *LocalValue) String() string {
|
||||
isConstant := "false"
|
||||
if v.IsConstant() {
|
||||
isConstant = "true"
|
||||
}
|
||||
return "&LocalValue{Type: " + v.Type().String() + ", IsConstant: " + isConstant + "}"
|
||||
}
|
||||
|
||||
// getConstGEPIndices returns indices of this constant GEP, if this is a GEP
|
||||
// instruction. If it is not, the behavior is undefined.
|
||||
func (v *LocalValue) getConstGEPIndices() []uint32 {
|
||||
indices := make([]uint32, v.Underlying.OperandsCount()-1)
|
||||
for i := range indices {
|
||||
operand := v.Underlying.Operand(i + 1)
|
||||
indices[i] = uint32(operand.ZExtValue())
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
// MarkDirty marks this global as dirty, meaning that every load from and store
|
||||
// to this global (from now on) must be performed at runtime.
|
||||
func (v *LocalValue) MarkDirty() {
|
||||
underlying := unwrap(v.Underlying)
|
||||
if underlying.IsAGlobalVariable().IsNil() {
|
||||
panic("trying to mark a non-global as dirty")
|
||||
}
|
||||
if !v.IsConstant() {
|
||||
return // already dirty
|
||||
}
|
||||
v.Eval.dirtyGlobals[underlying] = struct{}{}
|
||||
}
|
||||
|
||||
// MapValue implements a Go map which is created at compile time and stored as a
|
||||
// global variable.
|
||||
type MapValue struct {
|
||||
Eval *Eval
|
||||
PkgName string
|
||||
Underlying llvm.Value
|
||||
Keys []Value
|
||||
Values []Value
|
||||
KeySize int
|
||||
ValueSize int
|
||||
KeyType llvm.Type
|
||||
ValueType llvm.Type
|
||||
}
|
||||
|
||||
func (v *MapValue) newBucket() llvm.Value {
|
||||
ctx := v.Eval.Mod.Context()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
bucketType := ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(ctx.Int8Type(), 8), // tophash
|
||||
i8ptrType, // next bucket
|
||||
llvm.ArrayType(v.KeyType, 8), // key type
|
||||
llvm.ArrayType(v.ValueType, 8), // value type
|
||||
}, false)
|
||||
bucketValue := llvm.ConstNull(bucketType)
|
||||
bucket := llvm.AddGlobal(v.Eval.Mod, bucketType, v.PkgName+"$mapbucket")
|
||||
bucket.SetInitializer(bucketValue)
|
||||
bucket.SetLinkage(llvm.InternalLinkage)
|
||||
bucket.SetUnnamedAddr(true)
|
||||
return bucket
|
||||
}
|
||||
|
||||
// Value returns a global variable which is a pointer to the actual hashmap.
|
||||
func (v *MapValue) Value() llvm.Value {
|
||||
if !v.Underlying.IsNil() {
|
||||
return v.Underlying
|
||||
}
|
||||
|
||||
ctx := v.Eval.Mod.Context()
|
||||
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
|
||||
|
||||
var firstBucketGlobal llvm.Value
|
||||
if len(v.Keys) == 0 {
|
||||
// there are no buckets
|
||||
firstBucketGlobal = llvm.ConstPointerNull(i8ptrType)
|
||||
} else {
|
||||
// create initial bucket
|
||||
firstBucketGlobal = v.newBucket()
|
||||
}
|
||||
|
||||
// Insert each key/value pair in the hashmap.
|
||||
bucketGlobal := firstBucketGlobal
|
||||
for i, key := range v.Keys {
|
||||
var keyBuf []byte
|
||||
llvmKey := key.Value()
|
||||
llvmValue := v.Values[i].Value()
|
||||
if key.Type().TypeKind() == llvm.StructTypeKind && key.Type().StructName() == "runtime._string" {
|
||||
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
|
||||
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
|
||||
keyPtrVal := v.Eval.getValue(keyPtr)
|
||||
var err error
|
||||
keyBuf, err = getStringBytes(keyPtrVal, keyLen)
|
||||
if err != nil {
|
||||
panic(err) // TODO
|
||||
}
|
||||
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
|
||||
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
|
||||
n := key.Value().ZExtValue()
|
||||
for i := range keyBuf {
|
||||
keyBuf[i] = byte(n)
|
||||
n >>= 8
|
||||
}
|
||||
} else if key.Type().TypeKind() == llvm.ArrayTypeKind &&
|
||||
key.Type().ElementType().TypeKind() == llvm.IntegerTypeKind &&
|
||||
key.Type().ElementType().IntTypeWidth() == 8 {
|
||||
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
|
||||
for i := range keyBuf {
|
||||
keyBuf[i] = byte(llvm.ConstExtractValue(llvmKey, []uint32{uint32(i)}).ZExtValue())
|
||||
}
|
||||
} else {
|
||||
panic("interp: map key type not implemented: " + key.Type().String())
|
||||
}
|
||||
hash := v.hash(keyBuf)
|
||||
|
||||
if i%8 == 0 && i != 0 {
|
||||
// Bucket is full, create a new one.
|
||||
newBucketGlobal := v.newBucket()
|
||||
zero := llvm.ConstInt(ctx.Int32Type(), 0, false)
|
||||
newBucketPtr := llvm.ConstInBoundsGEP(newBucketGlobal, []llvm.Value{zero})
|
||||
newBucketPtrCast := llvm.ConstBitCast(newBucketPtr, i8ptrType)
|
||||
// insert pointer into old bucket
|
||||
bucket := bucketGlobal.Initializer()
|
||||
bucket = llvm.ConstInsertValue(bucket, newBucketPtrCast, []uint32{1})
|
||||
bucketGlobal.SetInitializer(bucket)
|
||||
// switch to next bucket
|
||||
bucketGlobal = newBucketGlobal
|
||||
}
|
||||
|
||||
tophashValue := llvm.ConstInt(ctx.Int8Type(), uint64(v.topHash(hash)), false)
|
||||
bucket := bucketGlobal.Initializer()
|
||||
bucket = llvm.ConstInsertValue(bucket, tophashValue, []uint32{0, uint32(i % 8)})
|
||||
bucket = llvm.ConstInsertValue(bucket, llvmKey, []uint32{2, uint32(i % 8)})
|
||||
bucket = llvm.ConstInsertValue(bucket, llvmValue, []uint32{3, uint32(i % 8)})
|
||||
bucketGlobal.SetInitializer(bucket)
|
||||
}
|
||||
|
||||
// Create the hashmap itself.
|
||||
zero := llvm.ConstInt(ctx.Int32Type(), 0, false)
|
||||
bucketPtr := llvm.ConstInBoundsGEP(firstBucketGlobal, []llvm.Value{zero})
|
||||
hashmapType := v.Type()
|
||||
hashmap := llvm.ConstNamedStruct(hashmapType, []llvm.Value{
|
||||
llvm.ConstPointerNull(llvm.PointerType(hashmapType, 0)), // next
|
||||
llvm.ConstBitCast(bucketPtr, i8ptrType), // buckets
|
||||
llvm.ConstInt(hashmapType.StructElementTypes()[2], uint64(len(v.Keys)), false), // count
|
||||
llvm.ConstInt(ctx.Int8Type(), uint64(v.KeySize), false), // keySize
|
||||
llvm.ConstInt(ctx.Int8Type(), uint64(v.ValueSize), false), // valueSize
|
||||
llvm.ConstInt(ctx.Int8Type(), 0, false), // bucketBits
|
||||
})
|
||||
|
||||
// Create a pointer to this hashmap.
|
||||
hashmapPtr := llvm.AddGlobal(v.Eval.Mod, hashmap.Type(), v.PkgName+"$map")
|
||||
hashmapPtr.SetInitializer(hashmap)
|
||||
hashmapPtr.SetLinkage(llvm.InternalLinkage)
|
||||
hashmapPtr.SetUnnamedAddr(true)
|
||||
v.Underlying = llvm.ConstInBoundsGEP(hashmapPtr, []llvm.Value{zero})
|
||||
return v.Underlying
|
||||
}
|
||||
|
||||
// Type returns type runtime.hashmap, which is the actual hashmap type.
|
||||
func (v *MapValue) Type() llvm.Type {
|
||||
return v.Eval.Mod.GetTypeByName("runtime.hashmap")
|
||||
}
|
||||
|
||||
func (v *MapValue) IsConstant() bool {
|
||||
return true // TODO: dirty maps
|
||||
}
|
||||
|
||||
// Load panics: maps are of reference type so cannot be dereferenced.
|
||||
func (v *MapValue) Load() (llvm.Value, error) {
|
||||
panic("interp: load from a map")
|
||||
}
|
||||
|
||||
// Store returns an error: maps are of reference type so cannot be stored to.
|
||||
func (v *MapValue) Store(value llvm.Value) error {
|
||||
// This must be a bug, but it might be helpful to indicate the location
|
||||
// anyway.
|
||||
return errors.New("interp: store on a map")
|
||||
}
|
||||
|
||||
// GetElementPtr panics: maps are of reference type so their (interior)
|
||||
// addresses cannot be calculated.
|
||||
func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
|
||||
return nil, errors.New("interp: GEP on a map")
|
||||
}
|
||||
|
||||
// PutString does a map assign operation, assuming that the map is of type
|
||||
// map[string]T.
|
||||
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) error {
|
||||
if !v.Underlying.IsNil() {
|
||||
return errors.New("map already created")
|
||||
}
|
||||
|
||||
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
||||
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
||||
}
|
||||
value, err := valPtr.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v.ValueType.IsNil() {
|
||||
v.ValueType = value.Type()
|
||||
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
||||
return errors.New("interp: map store value type has the wrong size")
|
||||
}
|
||||
} else {
|
||||
if value.Type() != v.ValueType {
|
||||
return errors.New("interp: map store value type is inconsistent")
|
||||
}
|
||||
}
|
||||
|
||||
keyType := v.Eval.Mod.GetTypeByName("runtime._string")
|
||||
v.KeyType = keyType
|
||||
key := llvm.ConstNull(keyType)
|
||||
key = llvm.ConstInsertValue(key, keyBuf.Value(), []uint32{0})
|
||||
key = llvm.ConstInsertValue(key, keyLen.Value(), []uint32{1})
|
||||
|
||||
// TODO: avoid duplicate keys
|
||||
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
||||
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PutBinary does a map assign operation.
|
||||
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) error {
|
||||
if !v.Underlying.IsNil() {
|
||||
return errors.New("map already created")
|
||||
}
|
||||
|
||||
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
||||
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
||||
}
|
||||
value, err := valPtr.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v.ValueType.IsNil() {
|
||||
v.ValueType = value.Type()
|
||||
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
||||
return errors.New("interp: map store value type has the wrong size")
|
||||
}
|
||||
} else {
|
||||
if value.Type() != v.ValueType {
|
||||
return errors.New("interp: map store value type is inconsistent")
|
||||
}
|
||||
}
|
||||
|
||||
if !keyPtr.Underlying.IsAConstantExpr().IsNil() {
|
||||
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
}
|
||||
}
|
||||
key, err := keyPtr.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v.KeyType.IsNil() {
|
||||
v.KeyType = key.Type()
|
||||
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
|
||||
return errors.New("interp: map store key type has the wrong size")
|
||||
}
|
||||
} else {
|
||||
if key.Type() != v.KeyType {
|
||||
return errors.New("interp: map store key type is inconsistent")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: avoid duplicate keys
|
||||
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
||||
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get FNV-1a hash of this string.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||
func (v *MapValue) hash(data []byte) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
for _, c := range data {
|
||||
result ^= uint32(c)
|
||||
result *= 16777619 // FNV prime
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Get the topmost 8 bits of the hash, without using a special value (like 0).
|
||||
func (v *MapValue) topHash(hash uint32) uint8 {
|
||||
tophash := uint8(hash >> 24)
|
||||
if tophash < 1 {
|
||||
// 0 means empty slot, so make it bigger.
|
||||
tophash += 1
|
||||
}
|
||||
return tophash
|
||||
}
|
||||
|
||||
func (v *MapValue) String() string {
|
||||
return "&MapValue{KeySize: " + strconv.Itoa(v.KeySize) + ", ValueSize: " + strconv.Itoa(v.ValueSize) + "}"
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func NewProgram(lprogram *loader.Program) *Program {
|
||||
program := lprogram.LoadSSA()
|
||||
program.Build()
|
||||
|
||||
mainPkg := program.ImportedPackage(lprogram.MainPkg().ImportPath)
|
||||
mainPkg := program.Package(lprogram.MainPkg().Pkg)
|
||||
if mainPkg == nil {
|
||||
panic("could not find main package")
|
||||
}
|
||||
@@ -79,7 +79,7 @@ func NewProgram(lprogram *loader.Program) *Program {
|
||||
}
|
||||
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
p.AddPackage(program.ImportedPackage(pkg.ImportPath))
|
||||
p.AddPackage(program.Package(pkg.Pkg))
|
||||
}
|
||||
|
||||
return p
|
||||
|
||||
+1
-1
Submodule lib/nrfx updated: 3ab39a9d45...d779b49fc5
Submodule
+1
Submodule lib/stm32-svd added at 9ec9982485
+1
-1
@@ -189,7 +189,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
|
||||
// with the TinyGo version. This is the case on some targets.
|
||||
func needsSyscallPackage(buildTags []string) bool {
|
||||
for _, tag := range buildTags {
|
||||
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" {
|
||||
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "wasi" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+47
-8
@@ -43,6 +43,7 @@ type Program struct {
|
||||
type PackageJSON struct {
|
||||
Dir string
|
||||
ImportPath string
|
||||
Name string
|
||||
ForTest string
|
||||
|
||||
// Source files
|
||||
@@ -51,8 +52,7 @@ type PackageJSON struct {
|
||||
CFiles []string
|
||||
|
||||
// Dependency information
|
||||
Imports []string
|
||||
ImportMap map[string]string
|
||||
Imports []string
|
||||
|
||||
// Error information
|
||||
Error *struct {
|
||||
@@ -169,6 +169,41 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if config.TestConfig.CompileTestBinary {
|
||||
// When creating a test binary, `go list` will list two or three
|
||||
// packages used for testing the package. The first is the original
|
||||
// package as if it were built normally, the second is the same
|
||||
// package but with the *_test.go files included. A possible third
|
||||
// may be included for _test packages (such as math_test), used to
|
||||
// test the external API with no access to internal functions.
|
||||
// All packages that are necessary for testing (including the to be
|
||||
// tested package with *_test.go files, but excluding the original
|
||||
// unmodified package) have a suffix added to the import path, for
|
||||
// example the math package has import path "math [math.test]" and
|
||||
// test dependencies such as fmt will have an import path of the
|
||||
// form "fmt [math.test]".
|
||||
// The code below removes this suffix, and if this results in a
|
||||
// duplicate (which happens with the to-be-tested package without
|
||||
// *.test.go files) the previous package is removed from the list of
|
||||
// packages included in this build.
|
||||
// This is necessary because the change in import paths results in
|
||||
// breakage to //go:linkname. Additionally, the duplicated package
|
||||
// slows down the build and so is best removed.
|
||||
if pkg.ForTest != "" && strings.HasSuffix(pkg.ImportPath, " ["+pkg.ForTest+".test]") {
|
||||
newImportPath := pkg.ImportPath[:len(pkg.ImportPath)-len(" ["+pkg.ForTest+".test]")]
|
||||
if _, ok := p.Packages[newImportPath]; ok {
|
||||
// Delete the previous package (that this package overrides).
|
||||
delete(p.Packages, newImportPath)
|
||||
for i, pkg := range p.sorted {
|
||||
if pkg.ImportPath == newImportPath {
|
||||
p.sorted = append(p.sorted[:i], p.sorted[i+1:]...) // remove element from slice
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
pkg.ImportPath = newImportPath
|
||||
}
|
||||
}
|
||||
p.sorted = append(p.sorted, pkg)
|
||||
p.Packages[pkg.ImportPath] = pkg
|
||||
}
|
||||
@@ -301,7 +336,16 @@ func (p *Package) Check() error {
|
||||
// Do typechecking of the package.
|
||||
checker.Importer = p
|
||||
|
||||
typesPkg, err := checker.Check(p.ImportPath, p.program.fset, p.Files, &p.info)
|
||||
packageName := p.ImportPath
|
||||
if p.Name == "main" {
|
||||
// The main package normally has a different import path, such as
|
||||
// "command-line-arguments" or "./testdata/cgo". Therefore, use the name
|
||||
// "main" in such a case: this package isn't imported from anywhere.
|
||||
// This is safe as it isn't possible to import a package with the name
|
||||
// "main".
|
||||
packageName = "main"
|
||||
}
|
||||
typesPkg, err := checker.Check(packageName, p.program.fset, p.Files, &p.info)
|
||||
if err != nil {
|
||||
if err, ok := err.(Errors); ok {
|
||||
return err
|
||||
@@ -367,11 +411,6 @@ func (p *Package) Import(to string) (*types.Package, error) {
|
||||
if to == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
if replace, ok := p.ImportMap[to]; ok {
|
||||
// This import path should be replaced by another import path, according
|
||||
// to `go list`.
|
||||
to = replace
|
||||
}
|
||||
if imported, ok := p.program.Packages[to]; ok {
|
||||
return imported.Pkg, nil
|
||||
} else {
|
||||
|
||||
@@ -16,3 +16,11 @@ func (p *Program) LoadSSA() *ssa.Program {
|
||||
|
||||
return prog
|
||||
}
|
||||
|
||||
// LoadSSA constructs the SSA form of this package.
|
||||
//
|
||||
// The program must already be parsed and type-checked with the .Parse() method.
|
||||
func (p *Package) LoadSSA() *ssa.Package {
|
||||
prog := ssa.NewProgram(p.program.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
||||
return prog.CreatePackage(p.Pkg, p.Files, &p.info, true)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -74,7 +73,12 @@ func copyFile(src, dst string) error {
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
destination, err := os.Create(dst)
|
||||
st, err := source.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destination, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, st.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -84,6 +88,14 @@ func copyFile(src, dst string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// executeCommand is a simple wrapper to exec.Cmd
|
||||
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
|
||||
if options.PrintCommands {
|
||||
fmt.Printf("%s %s\n ", name, strings.Join(arg, " "))
|
||||
}
|
||||
return exec.Command(name, arg...)
|
||||
}
|
||||
|
||||
// Build compiles and links the given package and writes it to outpath.
|
||||
func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
@@ -120,30 +132,71 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
}
|
||||
|
||||
// Test runs the tests in the given package.
|
||||
func Test(pkgName string, options *compileopts.Options) error {
|
||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) error {
|
||||
options.TestConfig.CompileTestBinary = true
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||
cmd := exec.Command(result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = result.MainDir
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
// Propagate the exit code
|
||||
if err, ok := err.(*exec.ExitError); ok {
|
||||
if status, ok := err.Sys().(syscall.WaitStatus); ok {
|
||||
os.Exit(status.ExitStatus())
|
||||
}
|
||||
os.Exit(1)
|
||||
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
if testCompileOnly || outpath != "" {
|
||||
// Write test binary to the specified file name.
|
||||
if outpath == "" {
|
||||
// No -o path was given, so create one now.
|
||||
// This matches the behavior of go test.
|
||||
outpath = filepath.Base(result.MainDir) + ".test"
|
||||
}
|
||||
copyFile(result.Binary, outpath)
|
||||
}
|
||||
if testCompileOnly {
|
||||
// Do not run the test.
|
||||
return nil
|
||||
}
|
||||
if len(config.Target.Emulator) == 0 {
|
||||
// Run directly.
|
||||
cmd := executeCommand(config.Options, result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = result.MainDir
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
// Propagate the exit code
|
||||
if err, ok := err.(*exec.ExitError); ok {
|
||||
if status, ok := err.Sys().(syscall.WaitStatus); ok {
|
||||
os.Exit(status.ExitStatus())
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary)
|
||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
buf := &bytes.Buffer{}
|
||||
w := io.MultiWriter(os.Stdout, buf)
|
||||
cmd.Stdout = w
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if err, ok := err.(*exec.ExitError); !ok || !err.Exited() {
|
||||
// Workaround for QEMU which always exits with an error.
|
||||
return &commandError{"failed to run emulator with", result.Binary, err}
|
||||
}
|
||||
}
|
||||
testOutput := string(buf.Bytes())
|
||||
if testOutput == "PASS\n" || strings.HasSuffix(testOutput, "\nPASS\n") {
|
||||
// Test passed.
|
||||
return nil
|
||||
} else {
|
||||
// Test failed, either by ending with the word "FAIL" or with a
|
||||
// panic of some sort.
|
||||
os.Exit(1)
|
||||
return nil // unreachable
|
||||
}
|
||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -230,9 +283,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
if len(command) < 2 {
|
||||
return errors.New("invalid flash command")
|
||||
}
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
cmd = executeCommand(config.Options, command[0], command[1:]...)
|
||||
default:
|
||||
cmd = exec.Command("/bin/sh", "-c", flashCmd)
|
||||
cmd = executeCommand(config.Options, "/bin/sh", "-c", flashCmd)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
@@ -246,13 +299,13 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
case "msd":
|
||||
switch fileExt {
|
||||
case ".uf2":
|
||||
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary)
|
||||
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
case ".hex":
|
||||
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary)
|
||||
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary, config.Options)
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
@@ -266,7 +319,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return err
|
||||
}
|
||||
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
|
||||
cmd := exec.Command("openocd", args...)
|
||||
cmd := executeCommand(config.Options, "openocd", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
@@ -302,16 +355,22 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
switch gdbInterface {
|
||||
case "msd", "command", "":
|
||||
if len(config.Target.Emulator) != 0 {
|
||||
// Assume QEMU as an emulator.
|
||||
if config.Target.Emulator[0] == "mgba" {
|
||||
gdbInterface = "mgba"
|
||||
} else {
|
||||
} else if config.Target.Emulator[0] == "simavr" {
|
||||
gdbInterface = "simavr"
|
||||
} else if strings.HasPrefix(config.Target.Emulator[0], "qemu-system-") {
|
||||
gdbInterface = "qemu"
|
||||
} else {
|
||||
// Assume QEMU as an emulator.
|
||||
gdbInterface = "qemu-user"
|
||||
}
|
||||
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
||||
gdbInterface = "openocd"
|
||||
} else if config.Target.JLinkDevice != "" {
|
||||
gdbInterface = "jlink"
|
||||
} else {
|
||||
gdbInterface = "native"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +388,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
daemon = exec.Command("openocd", args...)
|
||||
daemon = executeCommand(config.Options, "openocd", args...)
|
||||
if ocdOutput {
|
||||
// Make it clear which output is from the daemon.
|
||||
w := &ColorWriter{
|
||||
@@ -344,7 +403,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt")
|
||||
|
||||
// We need a separate debugging daemon for on-chip debugging.
|
||||
daemon = exec.Command("JLinkGDBServer", "-device", config.Target.JLinkDevice)
|
||||
daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice)
|
||||
if ocdOutput {
|
||||
// Make it clear which output is from the daemon.
|
||||
w := &ColorWriter{
|
||||
@@ -360,7 +419,15 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
|
||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "qemu-user":
|
||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary)
|
||||
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "mgba":
|
||||
@@ -368,7 +435,15 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary, "-g")
|
||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "simavr":
|
||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], "-g", result.Binary)
|
||||
daemon = executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
case "msd":
|
||||
@@ -409,7 +484,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
for _, cmd := range gdbCommands {
|
||||
params = append(params, "-ex", cmd)
|
||||
}
|
||||
cmd := exec.Command(config.Target.GDB, params...)
|
||||
cmd := executeCommand(config.Options, config.Target.GDB, params...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -434,7 +509,7 @@ func Run(pkgName string, options *compileopts.Options) error {
|
||||
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||
if len(config.Target.Emulator) == 0 {
|
||||
// Run directly.
|
||||
cmd := exec.Command(result.Binary)
|
||||
cmd := executeCommand(config.Options, result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
@@ -449,7 +524,7 @@ func Run(pkgName string, options *compileopts.Options) error {
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary)
|
||||
cmd := exec.Command(config.Target.Emulator[0], args...)
|
||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
@@ -492,7 +567,7 @@ func touchSerialPortAt1200bps(port string) (err error) {
|
||||
|
||||
const maxMSDRetries = 10
|
||||
|
||||
func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
func flashUF2UsingMSD(volume, tmppath string, options *compileopts.Options) error {
|
||||
// find standard UF2 info path
|
||||
var infoPath string
|
||||
switch runtime.GOOS {
|
||||
@@ -501,7 +576,7 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
case "darwin":
|
||||
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume)
|
||||
path, err := windowsFindUSBDrive(volume, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -516,7 +591,7 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
|
||||
}
|
||||
|
||||
func flashHexUsingMSD(volume, tmppath string) error {
|
||||
func flashHexUsingMSD(volume, tmppath string, options *compileopts.Options) error {
|
||||
// find expected volume path
|
||||
var destPath string
|
||||
switch runtime.GOOS {
|
||||
@@ -525,7 +600,7 @@ func flashHexUsingMSD(volume, tmppath string) error {
|
||||
case "darwin":
|
||||
destPath = "/Volumes/" + volume
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume)
|
||||
path, err := windowsFindUSBDrive(volume, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -559,8 +634,8 @@ func locateDevice(volume, path string) (string, error) {
|
||||
return d[0], nil
|
||||
}
|
||||
|
||||
func windowsFindUSBDrive(volume string) (string, error) {
|
||||
cmd := exec.Command("wmic",
|
||||
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) {
|
||||
cmd := executeCommand(options, "wmic",
|
||||
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
|
||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||
|
||||
@@ -582,30 +657,6 @@ func windowsFindUSBDrive(volume string) (string, error) {
|
||||
return "", errors.New("unable to locate a USB device to be flashed")
|
||||
}
|
||||
|
||||
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
|
||||
// number.
|
||||
func parseSize(s string) (int64, error) {
|
||||
s = strings.ToLower(strings.TrimSpace(s))
|
||||
if len(s) == 0 {
|
||||
return 0, errors.New("no size provided")
|
||||
}
|
||||
multiply := int64(1)
|
||||
switch s[len(s)-1] {
|
||||
case 'k':
|
||||
multiply = 1 << 10
|
||||
case 'm':
|
||||
multiply = 1 << 20
|
||||
case 'g':
|
||||
multiply = 1 << 30
|
||||
}
|
||||
if multiply != 1 {
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
n, err := strconv.ParseInt(s, 0, 64)
|
||||
n *= multiply
|
||||
return n, err
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
func getDefaultPort() (port string, err error) {
|
||||
var portPath string
|
||||
@@ -753,7 +804,6 @@ func main() {
|
||||
}
|
||||
command := os.Args[1]
|
||||
|
||||
outpath := flag.String("o", "", "output filename")
|
||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||
gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)")
|
||||
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
||||
@@ -765,20 +815,28 @@ func main() {
|
||||
target := flag.String("target", "", "LLVM target | .json file with TargetSpec")
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||
printCommands := flag.Bool("x", false, "Print commands")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
port := flag.String("port", "", "flash port")
|
||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
wasmAbi := flag.String("wasm-abi", "js", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||
heapSize := flag.String("heap-size", "1M", "default heap size in bytes (only supported by WebAssembly)")
|
||||
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||
|
||||
var flagJSON, flagDeps *bool
|
||||
if command == "list" {
|
||||
if command == "help" || command == "list" {
|
||||
flagJSON = flag.Bool("json", false, "print data in JSON format")
|
||||
flagDeps = flag.Bool("deps", false, "")
|
||||
}
|
||||
var outpath string
|
||||
if command == "help" || command == "build" || command == "build-library" || command == "test" {
|
||||
flag.StringVar(&outpath, "o", "", "output filename")
|
||||
}
|
||||
var testCompileOnlyFlag *bool
|
||||
if command == "help" || command == "test" {
|
||||
testCompileOnlyFlag = flag.Bool("c", false, "compile the test binary but do not run it")
|
||||
}
|
||||
|
||||
// Early command processing, before commands are interpreted by the Go flag
|
||||
// library.
|
||||
@@ -805,6 +863,7 @@ func main() {
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintCommands: *printCommands,
|
||||
Tags: *tags,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
@@ -818,16 +877,9 @@ func main() {
|
||||
options.LDFlags = strings.Split(*ldFlags, " ")
|
||||
}
|
||||
|
||||
var err error
|
||||
if options.HeapSize, err = parseSize(*heapSize); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Could not read heap size:", *heapSize)
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Setenv("CC", "clang -target="+*target)
|
||||
|
||||
err = options.Verify()
|
||||
err := options.Verify()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
usage()
|
||||
@@ -836,7 +888,7 @@ func main() {
|
||||
|
||||
switch command {
|
||||
case "build":
|
||||
if *outpath == "" {
|
||||
if outpath == "" {
|
||||
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
@@ -849,15 +901,15 @@ func main() {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
if options.Target == "" && filepath.Ext(*outpath) == ".wasm" {
|
||||
if options.Target == "" && filepath.Ext(outpath) == ".wasm" {
|
||||
options.Target = "wasm"
|
||||
}
|
||||
|
||||
err := Build(pkgName, *outpath, options)
|
||||
err := Build(pkgName, outpath, options)
|
||||
handleCompilerError(err)
|
||||
case "build-library":
|
||||
// Note: this command is only meant to be used while making a release!
|
||||
if *outpath == "" {
|
||||
if outpath == "" {
|
||||
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
@@ -882,13 +934,8 @@ func main() {
|
||||
}
|
||||
path, err := lib.Load(*target)
|
||||
handleCompilerError(err)
|
||||
copyFile(path, *outpath)
|
||||
copyFile(path, outpath)
|
||||
case "flash", "gdb":
|
||||
if *outpath != "" {
|
||||
fmt.Fprintln(os.Stderr, "Output cannot be specified with the flash command.")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
pkgName := filepath.ToSlash(flag.Arg(0))
|
||||
if command == "flash" {
|
||||
err := Flash(pkgName, *port, options)
|
||||
@@ -920,7 +967,7 @@ func main() {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err := Test(pkgName, options)
|
||||
err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
|
||||
handleCompilerError(err)
|
||||
case "targets":
|
||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
||||
|
||||
+13
-5
@@ -78,6 +78,9 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
t.Run("X86Linux", func(t *testing.T) {
|
||||
runPlatTests("i386--linux-gnu", matches, t)
|
||||
})
|
||||
t.Run("ARMLinux", func(t *testing.T) {
|
||||
runPlatTests("arm--linux-gnueabihf", matches, t)
|
||||
})
|
||||
@@ -98,6 +101,10 @@ func TestCompiler(t *testing.T) {
|
||||
runPlatTests("wasm", matches, t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("WASI", func(t *testing.T) {
|
||||
runPlatTests("wasi", matches, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +116,6 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runTest(path, target, t)
|
||||
})
|
||||
}
|
||||
@@ -159,8 +165,9 @@ func runTest(path, target string, t *testing.T) {
|
||||
VerifyIR: true,
|
||||
Debug: true,
|
||||
PrintSizes: "",
|
||||
WasmAbi: "js",
|
||||
WasmAbi: "",
|
||||
}
|
||||
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
err = runBuild("./"+path, binary, config)
|
||||
if err != nil {
|
||||
@@ -181,10 +188,11 @@ func runTest(path, target string, t *testing.T) {
|
||||
t.Fatal("failed to load target spec:", err)
|
||||
}
|
||||
if len(spec.Emulator) == 0 {
|
||||
t.Fatal("no emulator available for target:", target)
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
stdout := &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
|
||||
@@ -76,32 +76,8 @@ const (
|
||||
SCS_BASE = 0xE000E000
|
||||
SYST_BASE = SCS_BASE + 0x0010
|
||||
NVIC_BASE = SCS_BASE + 0x0100
|
||||
SCB_BASE = SCS_BASE + 0x0D00
|
||||
)
|
||||
|
||||
const (
|
||||
SCB_AIRCR_VECTKEY_Pos = 16
|
||||
SCB_AIRCR_SYSRESETREQ_Pos = 2
|
||||
SCB_AIRCR_SYSRESETREQ_Msk = 1 << SCB_AIRCR_SYSRESETREQ_Pos
|
||||
)
|
||||
|
||||
// System Control Block (SCB)
|
||||
//
|
||||
// SCB_Type provides the definitions for the System Control Block Registers.
|
||||
type SCB_Type struct {
|
||||
CPUID volatile.Register32 // CPUID Base Register
|
||||
ICSR volatile.Register32 // Interrupt Control and State Register
|
||||
VTOR volatile.Register32 // Vector Table Offset Register
|
||||
AIRCR volatile.Register32 // Application Interrupt and Reset Control Register
|
||||
SCR volatile.Register32 // System Control Register
|
||||
CCR volatile.Register32 // Configuration Control Register
|
||||
_ volatile.Register32 // RESERVED1;
|
||||
SHP [2]volatile.Register32 // System Handlers Priority Registers. [0] is RESERVED
|
||||
SHCSR volatile.Register32 // System Handler Control and State Register
|
||||
}
|
||||
|
||||
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
|
||||
|
||||
// Nested Vectored Interrupt Controller (NVIC).
|
||||
//
|
||||
// Source:
|
||||
@@ -213,17 +189,6 @@ func EnableInterrupts(mask uintptr) {
|
||||
})
|
||||
}
|
||||
|
||||
// SystemReset performs a hard system reset.
|
||||
func SystemReset() {
|
||||
// SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) |
|
||||
// SCB_AIRCR_SYSRESETREQ_Msk);
|
||||
SCB.AIRCR.Set((0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk)
|
||||
|
||||
for {
|
||||
Asm("wfi")
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the system timer to generate periodic tick events.
|
||||
// This will cause SysTick_Handler to fire once per tick.
|
||||
// The cyclecount parameter is a counter value which can range from 0 to
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// Cortex-M System Control Block-related definitions.
|
||||
|
||||
// +build cortexm
|
||||
|
||||
package arm
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const SCB_BASE = SCS_BASE + 0x0D00
|
||||
|
||||
// System Control Block (SCB)
|
||||
//
|
||||
// SCB_Type provides the definitions for the System Control Block Registers.
|
||||
type SCB_Type struct {
|
||||
CPUID volatile.Register32 // 0xD00: CPUID Base Register
|
||||
ICSR volatile.Register32 // 0xD04: Interrupt Control and State Register
|
||||
VTOR volatile.Register32 // 0xD08: Vector Table Offset Register
|
||||
AIRCR volatile.Register32 // 0xD0C: Application Interrupt and Reset Control Register
|
||||
SCR volatile.Register32 // 0xD10: System Control Register
|
||||
CCR volatile.Register32 // 0xD14: Configuration and Control Register
|
||||
SHPR1 volatile.Register32 // 0xD18: System Handler Priority Register 1 (Cortex-M3/M33/M4/M7 only)
|
||||
SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2
|
||||
SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3
|
||||
// the following are only applicable for Cortex-M3/M33/M4/M7
|
||||
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
|
||||
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
|
||||
HFSR volatile.Register32 // 0xD2C: HardFault Status Register
|
||||
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
|
||||
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
|
||||
BFAR volatile.Register32 // 0xD38: BusFault Address Register
|
||||
}
|
||||
|
||||
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
|
||||
|
||||
// SystemReset performs a hard system reset.
|
||||
func SystemReset() {
|
||||
SCB.AIRCR.Set((0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk)
|
||||
for {
|
||||
Asm("wfi")
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// CPUID: CPUID Base Register
|
||||
SCB_CPUID_REVISION_Pos = 0x0 // Position of REVISION field.
|
||||
SCB_CPUID_REVISION_Msk = 0xf // Bit mask of REVISION field.
|
||||
SCB_CPUID_PARTNO_Pos = 0x4 // Position of PARTNO field.
|
||||
SCB_CPUID_PARTNO_Msk = 0xfff0 // Bit mask of PARTNO field.
|
||||
SCB_CPUID_ARCHITECTURE_Pos = 0x10 // Position of ARCHITECTURE field.
|
||||
SCB_CPUID_ARCHITECTURE_Msk = 0xf0000 // Bit mask of ARCHITECTURE field.
|
||||
SCB_CPUID_VARIANT_Pos = 0x14 // Position of VARIANT field.
|
||||
SCB_CPUID_VARIANT_Msk = 0xf00000 // Bit mask of VARIANT field.
|
||||
SCB_CPUID_IMPLEMENTER_Pos = 0x18 // Position of IMPLEMENTER field.
|
||||
SCB_CPUID_IMPLEMENTER_Msk = 0xff000000 // Bit mask of IMPLEMENTER field.
|
||||
|
||||
// ICSR: Interrupt Control and State Register
|
||||
SCB_ICSR_VECTACTIVE_Pos = 0x0 // Position of VECTACTIVE field.
|
||||
SCB_ICSR_VECTACTIVE_Msk = 0x1ff // Bit mask of VECTACTIVE field.
|
||||
SCB_ICSR_RETTOBASE_Pos = 0xb // Position of RETTOBASE field.
|
||||
SCB_ICSR_RETTOBASE_Msk = 0x800 // Bit mask of RETTOBASE field.
|
||||
SCB_ICSR_RETTOBASE = 0x800 // Bit RETTOBASE.
|
||||
SCB_ICSR_RETTOBASE_RETTOBASE_0 = 0x0 // there are preempted active exceptions to execute
|
||||
SCB_ICSR_RETTOBASE_RETTOBASE_1 = 0x1 // there are no active exceptions, or the currently-executing exception is the only active exception
|
||||
SCB_ICSR_VECTPENDING_Pos = 0xc // Position of VECTPENDING field.
|
||||
SCB_ICSR_VECTPENDING_Msk = 0x1ff000 // Bit mask of VECTPENDING field.
|
||||
SCB_ICSR_ISRPENDING_Pos = 0x16 // Position of ISRPENDING field.
|
||||
SCB_ICSR_ISRPENDING_Msk = 0x400000 // Bit mask of ISRPENDING field.
|
||||
SCB_ICSR_ISRPENDING = 0x400000 // Bit ISRPENDING.
|
||||
SCB_ICSR_ISRPENDING_ISRPENDING_0 = 0x0 // No external interrupt pending.
|
||||
SCB_ICSR_ISRPENDING_ISRPENDING_1 = 0x1 // External interrupt pending.
|
||||
SCB_ICSR_PENDSTCLR_Pos = 0x19 // Position of PENDSTCLR field.
|
||||
SCB_ICSR_PENDSTCLR_Msk = 0x2000000 // Bit mask of PENDSTCLR field.
|
||||
SCB_ICSR_PENDSTCLR = 0x2000000 // Bit PENDSTCLR.
|
||||
SCB_ICSR_PENDSTCLR_PENDSTCLR_0 = 0x0 // no effect
|
||||
SCB_ICSR_PENDSTCLR_PENDSTCLR_1 = 0x1 // removes the pending state from the SysTick exception
|
||||
SCB_ICSR_PENDSTSET_Pos = 0x1a // Position of PENDSTSET field.
|
||||
SCB_ICSR_PENDSTSET_Msk = 0x4000000 // Bit mask of PENDSTSET field.
|
||||
SCB_ICSR_PENDSTSET = 0x4000000 // Bit PENDSTSET.
|
||||
SCB_ICSR_PENDSTSET_PENDSTSET_0 = 0x0 // write: no effect; read: SysTick exception is not pending
|
||||
SCB_ICSR_PENDSTSET_PENDSTSET_1 = 0x1 // write: changes SysTick exception state to pending; read: SysTick exception is pending
|
||||
SCB_ICSR_PENDSVCLR_Pos = 0x1b // Position of PENDSVCLR field.
|
||||
SCB_ICSR_PENDSVCLR_Msk = 0x8000000 // Bit mask of PENDSVCLR field.
|
||||
SCB_ICSR_PENDSVCLR = 0x8000000 // Bit PENDSVCLR.
|
||||
SCB_ICSR_PENDSVCLR_PENDSVCLR_0 = 0x0 // no effect
|
||||
SCB_ICSR_PENDSVCLR_PENDSVCLR_1 = 0x1 // removes the pending state from the PendSV exception
|
||||
SCB_ICSR_PENDSVSET_Pos = 0x1c // Position of PENDSVSET field.
|
||||
SCB_ICSR_PENDSVSET_Msk = 0x10000000 // Bit mask of PENDSVSET field.
|
||||
SCB_ICSR_PENDSVSET = 0x10000000 // Bit PENDSVSET.
|
||||
SCB_ICSR_PENDSVSET_PENDSVSET_0 = 0x0 // write: no effect; read: PendSV exception is not pending
|
||||
SCB_ICSR_PENDSVSET_PENDSVSET_1 = 0x1 // write: changes PendSV exception state to pending; read: PendSV exception is pending
|
||||
SCB_ICSR_NMIPENDSET_Pos = 0x1f // Position of NMIPENDSET field.
|
||||
SCB_ICSR_NMIPENDSET_Msk = 0x80000000 // Bit mask of NMIPENDSET field.
|
||||
SCB_ICSR_NMIPENDSET = 0x80000000 // Bit NMIPENDSET.
|
||||
SCB_ICSR_NMIPENDSET_NMIPENDSET_0 = 0x0 // write: no effect; read: NMI exception is not pending
|
||||
SCB_ICSR_NMIPENDSET_NMIPENDSET_1 = 0x1 // write: changes NMI exception state to pending; read: NMI exception is pending
|
||||
|
||||
// VTOR: Vector Table Offset Register
|
||||
SCB_VTOR_TBLOFF_Pos = 0x7 // Position of TBLOFF field.
|
||||
SCB_VTOR_TBLOFF_Msk = 0xffffff80 // Bit mask of TBLOFF field.
|
||||
|
||||
// AIRCR: Application Interrupt and Reset Control Register
|
||||
SCB_AIRCR_VECTRESET_Pos = 0x0 // Position of VECTRESET field.
|
||||
SCB_AIRCR_VECTRESET_Msk = 0x1 // Bit mask of VECTRESET field.
|
||||
SCB_AIRCR_VECTRESET = 0x1 // Bit VECTRESET.
|
||||
SCB_AIRCR_VECTRESET_VECTRESET_0 = 0x0 // No change
|
||||
SCB_AIRCR_VECTRESET_VECTRESET_1 = 0x1 // Causes a local system reset
|
||||
SCB_AIRCR_VECTCLRACTIVE_Pos = 0x1 // Position of VECTCLRACTIVE field.
|
||||
SCB_AIRCR_VECTCLRACTIVE_Msk = 0x2 // Bit mask of VECTCLRACTIVE field.
|
||||
SCB_AIRCR_VECTCLRACTIVE = 0x2 // Bit VECTCLRACTIVE.
|
||||
SCB_AIRCR_VECTCLRACTIVE_VECTCLRACTIVE_0 = 0x0 // No change
|
||||
SCB_AIRCR_VECTCLRACTIVE_VECTCLRACTIVE_1 = 0x1 // Clears all active state information for fixed and configurable exceptions
|
||||
SCB_AIRCR_SYSRESETREQ_Pos = 0x2 // Position of SYSRESETREQ field.
|
||||
SCB_AIRCR_SYSRESETREQ_Msk = 0x4 // Bit mask of SYSRESETREQ field.
|
||||
SCB_AIRCR_SYSRESETREQ = 0x4 // Bit SYSRESETREQ.
|
||||
SCB_AIRCR_SYSRESETREQ_SYSRESETREQ_0 = 0x0 // no system reset request
|
||||
SCB_AIRCR_SYSRESETREQ_SYSRESETREQ_1 = 0x1 // asserts a signal to the outer system that requests a reset
|
||||
SCB_AIRCR_PRIGROUP_Pos = 0x8 // Position of PRIGROUP field.
|
||||
SCB_AIRCR_PRIGROUP_Msk = 0x700 // Bit mask of PRIGROUP field.
|
||||
SCB_AIRCR_ENDIANNESS_Pos = 0xf // Position of ENDIANNESS field.
|
||||
SCB_AIRCR_ENDIANNESS_Msk = 0x8000 // Bit mask of ENDIANNESS field.
|
||||
SCB_AIRCR_ENDIANNESS = 0x8000 // Bit ENDIANNESS.
|
||||
SCB_AIRCR_ENDIANNESS_ENDIANNESS_0 = 0x0 // Little-endian
|
||||
SCB_AIRCR_ENDIANNESS_ENDIANNESS_1 = 0x1 // Big-endian
|
||||
SCB_AIRCR_VECTKEY_Pos = 0x10 // Position of VECTKEY field.
|
||||
SCB_AIRCR_VECTKEY_Msk = 0xffff0000 // Bit mask of VECTKEY field.
|
||||
|
||||
// SCR: System Control Register
|
||||
SCB_SCR_SLEEPONEXIT_Pos = 0x1 // Position of SLEEPONEXIT field.
|
||||
SCB_SCR_SLEEPONEXIT_Msk = 0x2 // Bit mask of SLEEPONEXIT field.
|
||||
SCB_SCR_SLEEPONEXIT = 0x2 // Bit SLEEPONEXIT.
|
||||
SCB_SCR_SLEEPONEXIT_SLEEPONEXIT_0 = 0x0 // o not sleep when returning to Thread mode
|
||||
SCB_SCR_SLEEPONEXIT_SLEEPONEXIT_1 = 0x1 // enter sleep, or deep sleep, on return from an ISR
|
||||
SCB_SCR_SLEEPDEEP_Pos = 0x2 // Position of SLEEPDEEP field.
|
||||
SCB_SCR_SLEEPDEEP_Msk = 0x4 // Bit mask of SLEEPDEEP field.
|
||||
SCB_SCR_SLEEPDEEP = 0x4 // Bit SLEEPDEEP.
|
||||
SCB_SCR_SLEEPDEEP_SLEEPDEEP_0 = 0x0 // sleep
|
||||
SCB_SCR_SLEEPDEEP_SLEEPDEEP_1 = 0x1 // deep sleep
|
||||
SCB_SCR_SEVONPEND_Pos = 0x4 // Position of SEVONPEND field.
|
||||
SCB_SCR_SEVONPEND_Msk = 0x10 // Bit mask of SEVONPEND field.
|
||||
SCB_SCR_SEVONPEND = 0x10 // Bit SEVONPEND.
|
||||
SCB_SCR_SEVONPEND_SEVONPEND_0 = 0x0 // only enabled interrupts or events can wakeup the processor, disabled interrupts are excluded
|
||||
SCB_SCR_SEVONPEND_SEVONPEND_1 = 0x1 // enabled events and all interrupts, including disabled interrupts, can wakeup the processor
|
||||
|
||||
// CCR: Configuration and Control Register
|
||||
SCB_CCR_NONBASETHRDENA_Pos = 0x0 // Position of NONBASETHRDENA field.
|
||||
SCB_CCR_NONBASETHRDENA_Msk = 0x1 // Bit mask of NONBASETHRDENA field.
|
||||
SCB_CCR_NONBASETHRDENA = 0x1 // Bit NONBASETHRDENA.
|
||||
SCB_CCR_NONBASETHRDENA_NONBASETHRDENA_0 = 0x0 // processor can enter Thread mode only when no exception is active
|
||||
SCB_CCR_NONBASETHRDENA_NONBASETHRDENA_1 = 0x1 // processor can enter Thread mode from any level under the control of an EXC_RETURN value
|
||||
SCB_CCR_USERSETMPEND_Pos = 0x1 // Position of USERSETMPEND field.
|
||||
SCB_CCR_USERSETMPEND_Msk = 0x2 // Bit mask of USERSETMPEND field.
|
||||
SCB_CCR_USERSETMPEND = 0x2 // Bit USERSETMPEND.
|
||||
SCB_CCR_USERSETMPEND_USERSETMPEND_0 = 0x0 // disable
|
||||
SCB_CCR_USERSETMPEND_USERSETMPEND_1 = 0x1 // enable
|
||||
SCB_CCR_UNALIGN_TRP_Pos = 0x3 // Position of UNALIGN_TRP field.
|
||||
SCB_CCR_UNALIGN_TRP_Msk = 0x8 // Bit mask of UNALIGN_TRP field.
|
||||
SCB_CCR_UNALIGN_TRP = 0x8 // Bit UNALIGN_TRP.
|
||||
SCB_CCR_UNALIGN_TRP_UNALIGN_TRP_0 = 0x0 // do not trap unaligned halfword and word accesses
|
||||
SCB_CCR_UNALIGN_TRP_UNALIGN_TRP_1 = 0x1 // trap unaligned halfword and word accesses
|
||||
SCB_CCR_DIV_0_TRP_Pos = 0x4 // Position of DIV_0_TRP field.
|
||||
SCB_CCR_DIV_0_TRP_Msk = 0x10 // Bit mask of DIV_0_TRP field.
|
||||
SCB_CCR_DIV_0_TRP = 0x10 // Bit DIV_0_TRP.
|
||||
SCB_CCR_DIV_0_TRP_DIV_0_TRP_0 = 0x0 // do not trap divide by 0
|
||||
SCB_CCR_DIV_0_TRP_DIV_0_TRP_1 = 0x1 // trap divide by 0
|
||||
SCB_CCR_BFHFNMIGN_Pos = 0x8 // Position of BFHFNMIGN field.
|
||||
SCB_CCR_BFHFNMIGN_Msk = 0x100 // Bit mask of BFHFNMIGN field.
|
||||
SCB_CCR_BFHFNMIGN = 0x100 // Bit BFHFNMIGN.
|
||||
SCB_CCR_BFHFNMIGN_BFHFNMIGN_0 = 0x0 // data bus faults caused by load and store instructions cause a lock-up
|
||||
SCB_CCR_BFHFNMIGN_BFHFNMIGN_1 = 0x1 // handlers running at priority -1 and -2 ignore data bus faults caused by load and store instructions
|
||||
SCB_CCR_STKALIGN_Pos = 0x9 // Position of STKALIGN field.
|
||||
SCB_CCR_STKALIGN_Msk = 0x200 // Bit mask of STKALIGN field.
|
||||
SCB_CCR_STKALIGN = 0x200 // Bit STKALIGN.
|
||||
SCB_CCR_STKALIGN_STKALIGN_0 = 0x0 // 4-byte aligned
|
||||
SCB_CCR_STKALIGN_STKALIGN_1 = 0x1 // 8-byte aligned
|
||||
SCB_CCR_DC_Pos = 0x10 // Position of DC field.
|
||||
SCB_CCR_DC_Msk = 0x10000 // Bit mask of DC field.
|
||||
SCB_CCR_DC = 0x10000 // Bit DC.
|
||||
SCB_CCR_DC_DC_0 = 0x0 // L1 data cache disabled
|
||||
SCB_CCR_DC_DC_1 = 0x1 // L1 data cache enabled
|
||||
SCB_CCR_IC_Pos = 0x11 // Position of IC field.
|
||||
SCB_CCR_IC_Msk = 0x20000 // Bit mask of IC field.
|
||||
SCB_CCR_IC = 0x20000 // Bit IC.
|
||||
SCB_CCR_IC_IC_0 = 0x0 // L1 instruction cache disabled
|
||||
SCB_CCR_IC_IC_1 = 0x1 // L1 instruction cache enabled
|
||||
SCB_CCR_BP_Pos = 0x12 // Position of BP field.
|
||||
SCB_CCR_BP_Msk = 0x40000 // Bit mask of BP field.
|
||||
SCB_CCR_BP = 0x40000 // Bit BP.
|
||||
|
||||
// SHPR1: System Handler Priority Register 1
|
||||
SCB_SHPR1_PRI_4_Pos = 0x0 // Position of PRI_4 field.
|
||||
SCB_SHPR1_PRI_4_Msk = 0xff // Bit mask of PRI_4 field.
|
||||
SCB_SHPR1_PRI_5_Pos = 0x8 // Position of PRI_5 field.
|
||||
SCB_SHPR1_PRI_5_Msk = 0xff00 // Bit mask of PRI_5 field.
|
||||
SCB_SHPR1_PRI_6_Pos = 0x10 // Position of PRI_6 field.
|
||||
SCB_SHPR1_PRI_6_Msk = 0xff0000 // Bit mask of PRI_6 field.
|
||||
|
||||
// SHPR2: System Handler Priority Register 2
|
||||
SCB_SHPR2_PRI_11_Pos = 0x18 // Position of PRI_11 field.
|
||||
SCB_SHPR2_PRI_11_Msk = 0xff000000 // Bit mask of PRI_11 field.
|
||||
|
||||
// SHPR3: System Handler Priority Register 3
|
||||
SCB_SHPR3_PRI_14_Pos = 0x10 // Position of PRI_14 field.
|
||||
SCB_SHPR3_PRI_14_Msk = 0xff0000 // Bit mask of PRI_14 field.
|
||||
SCB_SHPR3_PRI_15_Pos = 0x18 // Position of PRI_15 field.
|
||||
SCB_SHPR3_PRI_15_Msk = 0xff000000 // Bit mask of PRI_15 field.
|
||||
|
||||
// SHCSR: System Handler Control and State Register
|
||||
SCB_SHCSR_MEMFAULTACT_Pos = 0x0 // Position of MEMFAULTACT field.
|
||||
SCB_SHCSR_MEMFAULTACT_Msk = 0x1 // Bit mask of MEMFAULTACT field.
|
||||
SCB_SHCSR_MEMFAULTACT = 0x1 // Bit MEMFAULTACT.
|
||||
SCB_SHCSR_MEMFAULTACT_MEMFAULTACT_0 = 0x0 // exception is not active
|
||||
SCB_SHCSR_MEMFAULTACT_MEMFAULTACT_1 = 0x1 // exception is active
|
||||
SCB_SHCSR_BUSFAULTACT_Pos = 0x1 // Position of BUSFAULTACT field.
|
||||
SCB_SHCSR_BUSFAULTACT_Msk = 0x2 // Bit mask of BUSFAULTACT field.
|
||||
SCB_SHCSR_BUSFAULTACT = 0x2 // Bit BUSFAULTACT.
|
||||
SCB_SHCSR_BUSFAULTACT_BUSFAULTACT_0 = 0x0 // exception is not active
|
||||
SCB_SHCSR_BUSFAULTACT_BUSFAULTACT_1 = 0x1 // exception is active
|
||||
SCB_SHCSR_USGFAULTACT_Pos = 0x3 // Position of USGFAULTACT field.
|
||||
SCB_SHCSR_USGFAULTACT_Msk = 0x8 // Bit mask of USGFAULTACT field.
|
||||
SCB_SHCSR_USGFAULTACT = 0x8 // Bit USGFAULTACT.
|
||||
SCB_SHCSR_USGFAULTACT_USGFAULTACT_0 = 0x0 // exception is not active
|
||||
SCB_SHCSR_USGFAULTACT_USGFAULTACT_1 = 0x1 // exception is active
|
||||
SCB_SHCSR_SVCALLACT_Pos = 0x7 // Position of SVCALLACT field.
|
||||
SCB_SHCSR_SVCALLACT_Msk = 0x80 // Bit mask of SVCALLACT field.
|
||||
SCB_SHCSR_SVCALLACT = 0x80 // Bit SVCALLACT.
|
||||
SCB_SHCSR_SVCALLACT_SVCALLACT_0 = 0x0 // exception is not active
|
||||
SCB_SHCSR_SVCALLACT_SVCALLACT_1 = 0x1 // exception is active
|
||||
SCB_SHCSR_MONITORACT_Pos = 0x8 // Position of MONITORACT field.
|
||||
SCB_SHCSR_MONITORACT_Msk = 0x100 // Bit mask of MONITORACT field.
|
||||
SCB_SHCSR_MONITORACT = 0x100 // Bit MONITORACT.
|
||||
SCB_SHCSR_MONITORACT_MONITORACT_0 = 0x0 // exception is not active
|
||||
SCB_SHCSR_MONITORACT_MONITORACT_1 = 0x1 // exception is active
|
||||
SCB_SHCSR_PENDSVACT_Pos = 0xa // Position of PENDSVACT field.
|
||||
SCB_SHCSR_PENDSVACT_Msk = 0x400 // Bit mask of PENDSVACT field.
|
||||
SCB_SHCSR_PENDSVACT = 0x400 // Bit PENDSVACT.
|
||||
SCB_SHCSR_PENDSVACT_PENDSVACT_0 = 0x0 // exception is not active
|
||||
SCB_SHCSR_PENDSVACT_PENDSVACT_1 = 0x1 // exception is active
|
||||
SCB_SHCSR_SYSTICKACT_Pos = 0xb // Position of SYSTICKACT field.
|
||||
SCB_SHCSR_SYSTICKACT_Msk = 0x800 // Bit mask of SYSTICKACT field.
|
||||
SCB_SHCSR_SYSTICKACT = 0x800 // Bit SYSTICKACT.
|
||||
SCB_SHCSR_SYSTICKACT_SYSTICKACT_0 = 0x0 // exception is not active
|
||||
SCB_SHCSR_SYSTICKACT_SYSTICKACT_1 = 0x1 // exception is active
|
||||
SCB_SHCSR_USGFAULTPENDED_Pos = 0xc // Position of USGFAULTPENDED field.
|
||||
SCB_SHCSR_USGFAULTPENDED_Msk = 0x1000 // Bit mask of USGFAULTPENDED field.
|
||||
SCB_SHCSR_USGFAULTPENDED = 0x1000 // Bit USGFAULTPENDED.
|
||||
SCB_SHCSR_USGFAULTPENDED_USGFAULTPENDED_0 = 0x0 // exception is not pending
|
||||
SCB_SHCSR_USGFAULTPENDED_USGFAULTPENDED_1 = 0x1 // exception is pending
|
||||
SCB_SHCSR_MEMFAULTPENDED_Pos = 0xd // Position of MEMFAULTPENDED field.
|
||||
SCB_SHCSR_MEMFAULTPENDED_Msk = 0x2000 // Bit mask of MEMFAULTPENDED field.
|
||||
SCB_SHCSR_MEMFAULTPENDED = 0x2000 // Bit MEMFAULTPENDED.
|
||||
SCB_SHCSR_MEMFAULTPENDED_MEMFAULTPENDED_0 = 0x0 // exception is not pending
|
||||
SCB_SHCSR_MEMFAULTPENDED_MEMFAULTPENDED_1 = 0x1 // exception is pending
|
||||
SCB_SHCSR_BUSFAULTPENDED_Pos = 0xe // Position of BUSFAULTPENDED field.
|
||||
SCB_SHCSR_BUSFAULTPENDED_Msk = 0x4000 // Bit mask of BUSFAULTPENDED field.
|
||||
SCB_SHCSR_BUSFAULTPENDED = 0x4000 // Bit BUSFAULTPENDED.
|
||||
SCB_SHCSR_BUSFAULTPENDED_BUSFAULTPENDED_0 = 0x0 // exception is not pending
|
||||
SCB_SHCSR_BUSFAULTPENDED_BUSFAULTPENDED_1 = 0x1 // exception is pending
|
||||
SCB_SHCSR_SVCALLPENDED_Pos = 0xf // Position of SVCALLPENDED field.
|
||||
SCB_SHCSR_SVCALLPENDED_Msk = 0x8000 // Bit mask of SVCALLPENDED field.
|
||||
SCB_SHCSR_SVCALLPENDED = 0x8000 // Bit SVCALLPENDED.
|
||||
SCB_SHCSR_SVCALLPENDED_SVCALLPENDED_0 = 0x0 // exception is not pending
|
||||
SCB_SHCSR_SVCALLPENDED_SVCALLPENDED_1 = 0x1 // exception is pending
|
||||
SCB_SHCSR_MEMFAULTENA_Pos = 0x10 // Position of MEMFAULTENA field.
|
||||
SCB_SHCSR_MEMFAULTENA_Msk = 0x10000 // Bit mask of MEMFAULTENA field.
|
||||
SCB_SHCSR_MEMFAULTENA = 0x10000 // Bit MEMFAULTENA.
|
||||
SCB_SHCSR_MEMFAULTENA_MEMFAULTENA_0 = 0x0 // disable the exception
|
||||
SCB_SHCSR_MEMFAULTENA_MEMFAULTENA_1 = 0x1 // enable the exception
|
||||
SCB_SHCSR_BUSFAULTENA_Pos = 0x11 // Position of BUSFAULTENA field.
|
||||
SCB_SHCSR_BUSFAULTENA_Msk = 0x20000 // Bit mask of BUSFAULTENA field.
|
||||
SCB_SHCSR_BUSFAULTENA = 0x20000 // Bit BUSFAULTENA.
|
||||
SCB_SHCSR_BUSFAULTENA_BUSFAULTENA_0 = 0x0 // disable the exception
|
||||
SCB_SHCSR_BUSFAULTENA_BUSFAULTENA_1 = 0x1 // enable the exception
|
||||
SCB_SHCSR_USGFAULTENA_Pos = 0x12 // Position of USGFAULTENA field.
|
||||
SCB_SHCSR_USGFAULTENA_Msk = 0x40000 // Bit mask of USGFAULTENA field.
|
||||
SCB_SHCSR_USGFAULTENA = 0x40000 // Bit USGFAULTENA.
|
||||
SCB_SHCSR_USGFAULTENA_USGFAULTENA_0 = 0x0 // disable the exception
|
||||
SCB_SHCSR_USGFAULTENA_USGFAULTENA_1 = 0x1 // enable the exception
|
||||
|
||||
// CFSR: Configurable Fault Status Register
|
||||
SCB_CFSR_IACCVIOL_Pos = 0x0 // Position of IACCVIOL field.
|
||||
SCB_CFSR_IACCVIOL_Msk = 0x1 // Bit mask of IACCVIOL field.
|
||||
SCB_CFSR_IACCVIOL = 0x1 // Bit IACCVIOL.
|
||||
SCB_CFSR_IACCVIOL_IACCVIOL_0 = 0x0 // no instruction access violation fault
|
||||
SCB_CFSR_IACCVIOL_IACCVIOL_1 = 0x1 // the processor attempted an instruction fetch from a location that does not permit execution
|
||||
SCB_CFSR_DACCVIOL_Pos = 0x1 // Position of DACCVIOL field.
|
||||
SCB_CFSR_DACCVIOL_Msk = 0x2 // Bit mask of DACCVIOL field.
|
||||
SCB_CFSR_DACCVIOL = 0x2 // Bit DACCVIOL.
|
||||
SCB_CFSR_DACCVIOL_DACCVIOL_0 = 0x0 // no data access violation fault
|
||||
SCB_CFSR_DACCVIOL_DACCVIOL_1 = 0x1 // the processor attempted a load or store at a location that does not permit the operation
|
||||
SCB_CFSR_MUNSTKERR_Pos = 0x3 // Position of MUNSTKERR field.
|
||||
SCB_CFSR_MUNSTKERR_Msk = 0x8 // Bit mask of MUNSTKERR field.
|
||||
SCB_CFSR_MUNSTKERR = 0x8 // Bit MUNSTKERR.
|
||||
SCB_CFSR_MUNSTKERR_MUNSTKERR_0 = 0x0 // no unstacking fault
|
||||
SCB_CFSR_MUNSTKERR_MUNSTKERR_1 = 0x1 // unstack for an exception return has caused one or more access violations
|
||||
SCB_CFSR_MSTKERR_Pos = 0x4 // Position of MSTKERR field.
|
||||
SCB_CFSR_MSTKERR_Msk = 0x10 // Bit mask of MSTKERR field.
|
||||
SCB_CFSR_MSTKERR = 0x10 // Bit MSTKERR.
|
||||
SCB_CFSR_MSTKERR_MSTKERR_0 = 0x0 // no stacking fault
|
||||
SCB_CFSR_MSTKERR_MSTKERR_1 = 0x1 // stacking for an exception entry has caused one or more access violations
|
||||
SCB_CFSR_MLSPERR_Pos = 0x5 // Position of MLSPERR field.
|
||||
SCB_CFSR_MLSPERR_Msk = 0x20 // Bit mask of MLSPERR field.
|
||||
SCB_CFSR_MLSPERR = 0x20 // Bit MLSPERR.
|
||||
SCB_CFSR_MLSPERR_MLSPERR_0 = 0x0 // No MemManage fault occurred during floating-point lazy state preservation
|
||||
SCB_CFSR_MLSPERR_MLSPERR_1 = 0x1 // A MemManage fault occurred during floating-point lazy state preservation
|
||||
SCB_CFSR_MMARVALID_Pos = 0x7 // Position of MMARVALID field.
|
||||
SCB_CFSR_MMARVALID_Msk = 0x80 // Bit mask of MMARVALID field.
|
||||
SCB_CFSR_MMARVALID = 0x80 // Bit MMARVALID.
|
||||
SCB_CFSR_MMARVALID_MMARVALID_0 = 0x0 // value in MMAR is not a valid fault address
|
||||
SCB_CFSR_MMARVALID_MMARVALID_1 = 0x1 // MMAR holds a valid fault address
|
||||
SCB_CFSR_IBUSERR_Pos = 0x8 // Position of IBUSERR field.
|
||||
SCB_CFSR_IBUSERR_Msk = 0x100 // Bit mask of IBUSERR field.
|
||||
SCB_CFSR_IBUSERR = 0x100 // Bit IBUSERR.
|
||||
SCB_CFSR_IBUSERR_IBUSERR_0 = 0x0 // no instruction bus error
|
||||
SCB_CFSR_IBUSERR_IBUSERR_1 = 0x1 // instruction bus error
|
||||
SCB_CFSR_PRECISERR_Pos = 0x9 // Position of PRECISERR field.
|
||||
SCB_CFSR_PRECISERR_Msk = 0x200 // Bit mask of PRECISERR field.
|
||||
SCB_CFSR_PRECISERR = 0x200 // Bit PRECISERR.
|
||||
SCB_CFSR_PRECISERR_PRECISERR_0 = 0x0 // no precise data bus error
|
||||
SCB_CFSR_PRECISERR_PRECISERR_1 = 0x1 // a data bus error has occurred, and the PC value stacked for the exception return points to the instruction that caused the fault
|
||||
SCB_CFSR_IMPRECISERR_Pos = 0xa // Position of IMPRECISERR field.
|
||||
SCB_CFSR_IMPRECISERR_Msk = 0x400 // Bit mask of IMPRECISERR field.
|
||||
SCB_CFSR_IMPRECISERR = 0x400 // Bit IMPRECISERR.
|
||||
SCB_CFSR_IMPRECISERR_IMPRECISERR_0 = 0x0 // no imprecise data bus error
|
||||
SCB_CFSR_IMPRECISERR_IMPRECISERR_1 = 0x1 // a data bus error has occurred, but the return address in the stack frame is not related to the instruction that caused the error
|
||||
SCB_CFSR_UNSTKERR_Pos = 0xb // Position of UNSTKERR field.
|
||||
SCB_CFSR_UNSTKERR_Msk = 0x800 // Bit mask of UNSTKERR field.
|
||||
SCB_CFSR_UNSTKERR = 0x800 // Bit UNSTKERR.
|
||||
SCB_CFSR_UNSTKERR_UNSTKERR_0 = 0x0 // no unstacking fault
|
||||
SCB_CFSR_UNSTKERR_UNSTKERR_1 = 0x1 // unstack for an exception return has caused one or more BusFaults
|
||||
SCB_CFSR_STKERR_Pos = 0xc // Position of STKERR field.
|
||||
SCB_CFSR_STKERR_Msk = 0x1000 // Bit mask of STKERR field.
|
||||
SCB_CFSR_STKERR = 0x1000 // Bit STKERR.
|
||||
SCB_CFSR_STKERR_STKERR_0 = 0x0 // no stacking fault
|
||||
SCB_CFSR_STKERR_STKERR_1 = 0x1 // stacking for an exception entry has caused one or more BusFaults
|
||||
SCB_CFSR_LSPERR_Pos = 0xd // Position of LSPERR field.
|
||||
SCB_CFSR_LSPERR_Msk = 0x2000 // Bit mask of LSPERR field.
|
||||
SCB_CFSR_LSPERR = 0x2000 // Bit LSPERR.
|
||||
SCB_CFSR_LSPERR_LSPERR_0 = 0x0 // No bus fault occurred during floating-point lazy state preservation
|
||||
SCB_CFSR_LSPERR_LSPERR_1 = 0x1 // A bus fault occurred during floating-point lazy state preservation
|
||||
SCB_CFSR_BFARVALID_Pos = 0xf // Position of BFARVALID field.
|
||||
SCB_CFSR_BFARVALID_Msk = 0x8000 // Bit mask of BFARVALID field.
|
||||
SCB_CFSR_BFARVALID = 0x8000 // Bit BFARVALID.
|
||||
SCB_CFSR_BFARVALID_BFARVALID_0 = 0x0 // value in BFAR is not a valid fault address
|
||||
SCB_CFSR_BFARVALID_BFARVALID_1 = 0x1 // BFAR holds a valid fault address
|
||||
SCB_CFSR_UNDEFINSTR_Pos = 0x10 // Position of UNDEFINSTR field.
|
||||
SCB_CFSR_UNDEFINSTR_Msk = 0x10000 // Bit mask of UNDEFINSTR field.
|
||||
SCB_CFSR_UNDEFINSTR = 0x10000 // Bit UNDEFINSTR.
|
||||
SCB_CFSR_UNDEFINSTR_UNDEFINSTR_0 = 0x0 // no undefined instruction UsageFault
|
||||
SCB_CFSR_UNDEFINSTR_UNDEFINSTR_1 = 0x1 // the processor has attempted to execute an undefined instruction
|
||||
SCB_CFSR_INVSTATE_Pos = 0x11 // Position of INVSTATE field.
|
||||
SCB_CFSR_INVSTATE_Msk = 0x20000 // Bit mask of INVSTATE field.
|
||||
SCB_CFSR_INVSTATE = 0x20000 // Bit INVSTATE.
|
||||
SCB_CFSR_INVSTATE_INVSTATE_0 = 0x0 // no invalid state UsageFault
|
||||
SCB_CFSR_INVSTATE_INVSTATE_1 = 0x1 // the processor has attempted to execute an instruction that makes illegal use of the EPSR
|
||||
SCB_CFSR_INVPC_Pos = 0x12 // Position of INVPC field.
|
||||
SCB_CFSR_INVPC_Msk = 0x40000 // Bit mask of INVPC field.
|
||||
SCB_CFSR_INVPC = 0x40000 // Bit INVPC.
|
||||
SCB_CFSR_INVPC_INVPC_0 = 0x0 // no invalid PC load UsageFault
|
||||
SCB_CFSR_INVPC_INVPC_1 = 0x1 // the processor has attempted an illegal load of EXC_RETURN to the PC
|
||||
SCB_CFSR_NOCP_Pos = 0x13 // Position of NOCP field.
|
||||
SCB_CFSR_NOCP_Msk = 0x80000 // Bit mask of NOCP field.
|
||||
SCB_CFSR_NOCP = 0x80000 // Bit NOCP.
|
||||
SCB_CFSR_NOCP_NOCP_0 = 0x0 // no UsageFault caused by attempting to access a coprocessor
|
||||
SCB_CFSR_NOCP_NOCP_1 = 0x1 // the processor has attempted to access a coprocessor
|
||||
SCB_CFSR_UNALIGNED_Pos = 0x18 // Position of UNALIGNED field.
|
||||
SCB_CFSR_UNALIGNED_Msk = 0x1000000 // Bit mask of UNALIGNED field.
|
||||
SCB_CFSR_UNALIGNED = 0x1000000 // Bit UNALIGNED.
|
||||
SCB_CFSR_UNALIGNED_UNALIGNED_0 = 0x0 // no unaligned access fault, or unaligned access trapping not enabled
|
||||
SCB_CFSR_UNALIGNED_UNALIGNED_1 = 0x1 // the processor has made an unaligned memory access
|
||||
SCB_CFSR_DIVBYZERO_Pos = 0x19 // Position of DIVBYZERO field.
|
||||
SCB_CFSR_DIVBYZERO_Msk = 0x2000000 // Bit mask of DIVBYZERO field.
|
||||
SCB_CFSR_DIVBYZERO = 0x2000000 // Bit DIVBYZERO.
|
||||
SCB_CFSR_DIVBYZERO_DIVBYZERO_0 = 0x0 // no divide by zero fault, or divide by zero trapping not enabled
|
||||
SCB_CFSR_DIVBYZERO_DIVBYZERO_1 = 0x1 // the processor has executed an SDIV or UDIV instruction with a divisor of 0
|
||||
|
||||
// HFSR: HardFault Status register
|
||||
SCB_HFSR_VECTTBL_Pos = 0x1 // Position of VECTTBL field.
|
||||
SCB_HFSR_VECTTBL_Msk = 0x2 // Bit mask of VECTTBL field.
|
||||
SCB_HFSR_VECTTBL = 0x2 // Bit VECTTBL.
|
||||
SCB_HFSR_VECTTBL_VECTTBL_0 = 0x0 // no BusFault on vector table read
|
||||
SCB_HFSR_VECTTBL_VECTTBL_1 = 0x1 // BusFault on vector table read
|
||||
SCB_HFSR_FORCED_Pos = 0x1e // Position of FORCED field.
|
||||
SCB_HFSR_FORCED_Msk = 0x40000000 // Bit mask of FORCED field.
|
||||
SCB_HFSR_FORCED = 0x40000000 // Bit FORCED.
|
||||
SCB_HFSR_FORCED_FORCED_0 = 0x0 // no forced HardFault
|
||||
SCB_HFSR_FORCED_FORCED_1 = 0x1 // forced HardFault
|
||||
SCB_HFSR_DEBUGEVT_Pos = 0x1f // Position of DEBUGEVT field.
|
||||
SCB_HFSR_DEBUGEVT_Msk = 0x80000000 // Bit mask of DEBUGEVT field.
|
||||
SCB_HFSR_DEBUGEVT = 0x80000000 // Bit DEBUGEVT.
|
||||
SCB_HFSR_DEBUGEVT_DEBUGEVT_0 = 0x0 // No Debug event has occurred.
|
||||
SCB_HFSR_DEBUGEVT_DEBUGEVT_1 = 0x1 // Debug event has occurred. The Debug Fault Status Register has been updated.
|
||||
|
||||
// DFSR: Debug Fault Status Register
|
||||
SCB_DFSR_HALTED_Pos = 0x0 // Position of HALTED field.
|
||||
SCB_DFSR_HALTED_Msk = 0x1 // Bit mask of HALTED field.
|
||||
SCB_DFSR_HALTED = 0x1 // Bit HALTED.
|
||||
SCB_DFSR_HALTED_HALTED_0 = 0x0 // No active halt request debug event
|
||||
SCB_DFSR_HALTED_HALTED_1 = 0x1 // Halt request debug event active
|
||||
SCB_DFSR_BKPT_Pos = 0x1 // Position of BKPT field.
|
||||
SCB_DFSR_BKPT_Msk = 0x2 // Bit mask of BKPT field.
|
||||
SCB_DFSR_BKPT = 0x2 // Bit BKPT.
|
||||
SCB_DFSR_BKPT_BKPT_0 = 0x0 // No current breakpoint debug event
|
||||
SCB_DFSR_BKPT_BKPT_1 = 0x1 // At least one current breakpoint debug event
|
||||
SCB_DFSR_DWTTRAP_Pos = 0x2 // Position of DWTTRAP field.
|
||||
SCB_DFSR_DWTTRAP_Msk = 0x4 // Bit mask of DWTTRAP field.
|
||||
SCB_DFSR_DWTTRAP = 0x4 // Bit DWTTRAP.
|
||||
SCB_DFSR_DWTTRAP_DWTTRAP_0 = 0x0 // No current debug events generated by the DWT
|
||||
SCB_DFSR_DWTTRAP_DWTTRAP_1 = 0x1 // At least one current debug event generated by the DWT
|
||||
SCB_DFSR_VCATCH_Pos = 0x3 // Position of VCATCH field.
|
||||
SCB_DFSR_VCATCH_Msk = 0x8 // Bit mask of VCATCH field.
|
||||
SCB_DFSR_VCATCH = 0x8 // Bit VCATCH.
|
||||
SCB_DFSR_VCATCH_VCATCH_0 = 0x0 // No Vector catch triggered
|
||||
SCB_DFSR_VCATCH_VCATCH_1 = 0x1 // Vector catch triggered
|
||||
SCB_DFSR_EXTERNAL_Pos = 0x4 // Position of EXTERNAL field.
|
||||
SCB_DFSR_EXTERNAL_Msk = 0x10 // Bit mask of EXTERNAL field.
|
||||
SCB_DFSR_EXTERNAL = 0x10 // Bit EXTERNAL.
|
||||
SCB_DFSR_EXTERNAL_EXTERNAL_0 = 0x0 // No external debug request debug event
|
||||
SCB_DFSR_EXTERNAL_EXTERNAL_1 = 0x1 // External debug request debug event
|
||||
|
||||
// MMFAR: MemManage Fault Address Register
|
||||
SCB_MMFAR_ADDRESS_Pos = 0x0 // Position of ADDRESS field.
|
||||
SCB_MMFAR_ADDRESS_Msk = 0xffffffff // Bit mask of ADDRESS field.
|
||||
|
||||
// BFAR: BusFault Address Register
|
||||
SCB_BFAR_ADDRESS_Pos = 0x0 // Position of ADDRESS field.
|
||||
SCB_BFAR_ADDRESS_Msk = 0xffffffff // Bit mask of ADDRESS field.
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
package arm64
|
||||
|
||||
// Run the given assembly code. The code will be marked as having side effects,
|
||||
// as it doesn't produce output and thus would normally be eliminated by the
|
||||
// optimizer.
|
||||
func Asm(asm string)
|
||||
|
||||
// Run the given inline assembly. The code will be marked as having side
|
||||
// effects, as it would otherwise be optimized away. The inline assembly string
|
||||
// recognizes template values in the form {name}, like so:
|
||||
//
|
||||
// arm.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
//
|
||||
// You can use {} in the asm string (which expands to a register) to set the
|
||||
// return value.
|
||||
func AsmFull(asm string, regs map[string]interface{}) uintptr
|
||||
|
||||
// Run the following system call (SVCall) with 0 arguments.
|
||||
func SVCall0(num uintptr) uintptr
|
||||
|
||||
// Run the following system call (SVCall) with 1 argument.
|
||||
func SVCall1(num uintptr, a1 interface{}) uintptr
|
||||
|
||||
// Run the following system call (SVCall) with 2 arguments.
|
||||
func SVCall2(num uintptr, a1, a2 interface{}) uintptr
|
||||
|
||||
// Run the following system call (SVCall) with 3 arguments.
|
||||
func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
||||
|
||||
// Run the following system call (SVCall) with 4 arguments.
|
||||
func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||
@@ -24,7 +24,7 @@ call_start_cpu0:
|
||||
wsr.ps a2
|
||||
rsync
|
||||
|
||||
// Set WINDOWBASE to 1 << WINDOWSTART.
|
||||
// Set WINDOWSTART to 1 << WINDOWBASE.
|
||||
rsr.windowbase a2
|
||||
ssl a2
|
||||
movi a2, 1
|
||||
@@ -42,8 +42,13 @@ call_start_cpu0:
|
||||
wsr.ps a2
|
||||
rsync
|
||||
|
||||
// Enable the FPU (coprocessor 0 so the lowest bit).
|
||||
movi a2, 1
|
||||
wsr.cpenable a2
|
||||
rsync
|
||||
|
||||
// Jump to the runtime start function written in Go.
|
||||
j main
|
||||
call4 main
|
||||
|
||||
.section .text.tinygo_scanCurrentStack
|
||||
.global tinygo_scanCurrentStack
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// Hardfault aliases for definitions that have inconsistent naming (which are
|
||||
// auto-generated by gen-device-svd.go) among devices in package nxp.
|
||||
|
||||
// +build nxp,!mimxrt1062
|
||||
|
||||
package nxp
|
||||
|
||||
const (
|
||||
HardFault_CFSR_IACCVIOL = SystemControl_CFSR_IACCVIOL
|
||||
HardFault_CFSR_DACCVIOL = SystemControl_CFSR_DACCVIOL
|
||||
HardFault_CFSR_MUNSTKERR = SystemControl_CFSR_MUNSTKERR
|
||||
HardFault_CFSR_MSTKERR = SystemControl_CFSR_MSTKERR
|
||||
HardFault_CFSR_MLSPERR = SystemControl_CFSR_MLSPERR
|
||||
HardFault_CFSR_IBUSERR = SystemControl_CFSR_IBUSERR
|
||||
HardFault_CFSR_PRECISERR = SystemControl_CFSR_PRECISERR
|
||||
HardFault_CFSR_IMPRECISERR = SystemControl_CFSR_IMPRECISERR
|
||||
HardFault_CFSR_UNSTKERR = SystemControl_CFSR_UNSTKERR
|
||||
HardFault_CFSR_STKERR = SystemControl_CFSR_STKERR
|
||||
HardFault_CFSR_LSPERR = SystemControl_CFSR_LSPERR
|
||||
HardFault_CFSR_UNDEFINSTR = SystemControl_CFSR_UNDEFINSTR
|
||||
HardFault_CFSR_INVSTATE = SystemControl_CFSR_INVSTATE
|
||||
HardFault_CFSR_INVPC = SystemControl_CFSR_INVPC
|
||||
HardFault_CFSR_NOCP = SystemControl_CFSR_NOCP
|
||||
HardFault_CFSR_UNALIGNED = SystemControl_CFSR_UNALIGNED
|
||||
HardFault_CFSR_DIVBYZERO = SystemControl_CFSR_DIVBYZERO
|
||||
HardFault_CFSR_MMARVALID = SystemControl_CFSR_MMARVALID
|
||||
HardFault_CFSR_BFARVALID = SystemControl_CFSR_BFARVALID
|
||||
)
|
||||
@@ -0,0 +1,529 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// Type definitions, fields, and constants associated with various clocks and
|
||||
// peripherals of the NXP MIMXRT1062.
|
||||
|
||||
// +build nxp,mimxrt1062
|
||||
|
||||
package nxp
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Clock represents an individual peripheral clock that may be enabled/disabled
|
||||
// at runtime. Clocks also have a method `Mux` for selecting the clock source
|
||||
// and a method `Div` for selecting the hardware divisor. Note that many
|
||||
// peripherals have an independent prescalar configuration applied to the output
|
||||
// of this divisor.
|
||||
type (
|
||||
Clock uint32
|
||||
ClockMode uint8
|
||||
)
|
||||
|
||||
// Enable activates or deactivates the clock gate of receiver Clock c.
|
||||
func (c Clock) Enable(enable bool) {
|
||||
if enable {
|
||||
c.setGate(clockNeededRunWait)
|
||||
} else {
|
||||
c.setGate(clockNotNeeded)
|
||||
}
|
||||
}
|
||||
|
||||
// Mux selects a clock source for the mux of the receiver Clock c.
|
||||
func (c Clock) Mux(mux uint32) { c.setCcm(mux) }
|
||||
|
||||
// Div configures the prescalar divisor of the receiver Clock c.
|
||||
func (c Clock) Div(div uint32) { c.setCcm(div) }
|
||||
|
||||
const (
|
||||
ClockModeRun ClockMode = 0 // Remain in run mode
|
||||
ClockModeWait ClockMode = 1 // Transfer to wait mode
|
||||
ClockModeStop ClockMode = 2 // Transfer to stop mode
|
||||
)
|
||||
|
||||
// Set configures the run mode of the MCU.
|
||||
func (m ClockMode) Set() {
|
||||
CCM.CLPCR.Set((CCM.CLPCR.Get() & ^uint32(CCM_CLPCR_LPM_Msk)) |
|
||||
((uint32(m) << CCM_CLPCR_LPM_Pos) & CCM_CLPCR_LPM_Msk))
|
||||
}
|
||||
|
||||
// Named oscillators
|
||||
const (
|
||||
ClockCpu Clock = 0x0 // CPU clock
|
||||
ClockAhb Clock = 0x1 // AHB clock
|
||||
ClockSemc Clock = 0x2 // SEMC clock
|
||||
ClockIpg Clock = 0x3 // IPG clock
|
||||
ClockPer Clock = 0x4 // PER clock
|
||||
ClockOsc Clock = 0x5 // OSC clock selected by PMU_LOWPWR_CTRL[OSC_SEL]
|
||||
ClockRtc Clock = 0x6 // RTC clock (RTCCLK)
|
||||
ClockArmPll Clock = 0x7 // ARMPLLCLK
|
||||
ClockUsb1Pll Clock = 0x8 // USB1PLLCLK
|
||||
ClockUsb1PllPfd0 Clock = 0x9 // USB1PLLPDF0CLK
|
||||
ClockUsb1PllPfd1 Clock = 0xA // USB1PLLPFD1CLK
|
||||
ClockUsb1PllPfd2 Clock = 0xB // USB1PLLPFD2CLK
|
||||
ClockUsb1PllPfd3 Clock = 0xC // USB1PLLPFD3CLK
|
||||
ClockUsb2Pll Clock = 0xD // USB2PLLCLK
|
||||
ClockSysPll Clock = 0xE // SYSPLLCLK
|
||||
ClockSysPllPfd0 Clock = 0xF // SYSPLLPDF0CLK
|
||||
ClockSysPllPfd1 Clock = 0x10 // SYSPLLPFD1CLK
|
||||
ClockSysPllPfd2 Clock = 0x11 // SYSPLLPFD2CLK
|
||||
ClockSysPllPfd3 Clock = 0x12 // SYSPLLPFD3CLK
|
||||
ClockEnetPll0 Clock = 0x13 // Enet PLLCLK ref_enetpll0
|
||||
ClockEnetPll1 Clock = 0x14 // Enet PLLCLK ref_enetpll1
|
||||
ClockEnetPll2 Clock = 0x15 // Enet PLLCLK ref_enetpll2
|
||||
ClockAudioPll Clock = 0x16 // Audio PLLCLK
|
||||
ClockVideoPll Clock = 0x17 // Video PLLCLK
|
||||
)
|
||||
|
||||
// Named clocks of integrated peripherals
|
||||
const (
|
||||
ClockIpAipsTz1 Clock = (0 << 8) | CCM_CCGR0_CG0_Pos // CCGR0, CG0
|
||||
ClockIpAipsTz2 Clock = (0 << 8) | CCM_CCGR0_CG1_Pos // CCGR0, CG1
|
||||
ClockIpMqs Clock = (0 << 8) | CCM_CCGR0_CG2_Pos // CCGR0, CG2
|
||||
ClockIpFlexSpiExsc Clock = (0 << 8) | CCM_CCGR0_CG3_Pos // CCGR0, CG3
|
||||
ClockIpSimMMain Clock = (0 << 8) | CCM_CCGR0_CG4_Pos // CCGR0, CG4
|
||||
ClockIpDcp Clock = (0 << 8) | CCM_CCGR0_CG5_Pos // CCGR0, CG5
|
||||
ClockIpLpuart3 Clock = (0 << 8) | CCM_CCGR0_CG6_Pos // CCGR0, CG6
|
||||
ClockIpCan1 Clock = (0 << 8) | CCM_CCGR0_CG7_Pos // CCGR0, CG7
|
||||
ClockIpCan1S Clock = (0 << 8) | CCM_CCGR0_CG8_Pos // CCGR0, CG8
|
||||
ClockIpCan2 Clock = (0 << 8) | CCM_CCGR0_CG9_Pos // CCGR0, CG9
|
||||
ClockIpCan2S Clock = (0 << 8) | CCM_CCGR0_CG10_Pos // CCGR0, CG10
|
||||
ClockIpTrace Clock = (0 << 8) | CCM_CCGR0_CG11_Pos // CCGR0, CG11
|
||||
ClockIpGpt2 Clock = (0 << 8) | CCM_CCGR0_CG12_Pos // CCGR0, CG12
|
||||
ClockIpGpt2S Clock = (0 << 8) | CCM_CCGR0_CG13_Pos // CCGR0, CG13
|
||||
ClockIpLpuart2 Clock = (0 << 8) | CCM_CCGR0_CG14_Pos // CCGR0, CG14
|
||||
ClockIpGpio2 Clock = (0 << 8) | CCM_CCGR0_CG15_Pos // CCGR0, CG15
|
||||
|
||||
ClockIpLpspi1 Clock = (1 << 8) | CCM_CCGR1_CG0_Pos // CCGR1, CG0
|
||||
ClockIpLpspi2 Clock = (1 << 8) | CCM_CCGR1_CG1_Pos // CCGR1, CG1
|
||||
ClockIpLpspi3 Clock = (1 << 8) | CCM_CCGR1_CG2_Pos // CCGR1, CG2
|
||||
ClockIpLpspi4 Clock = (1 << 8) | CCM_CCGR1_CG3_Pos // CCGR1, CG3
|
||||
ClockIpAdc2 Clock = (1 << 8) | CCM_CCGR1_CG4_Pos // CCGR1, CG4
|
||||
ClockIpEnet Clock = (1 << 8) | CCM_CCGR1_CG5_Pos // CCGR1, CG5
|
||||
ClockIpPit Clock = (1 << 8) | CCM_CCGR1_CG6_Pos // CCGR1, CG6
|
||||
ClockIpAoi2 Clock = (1 << 8) | CCM_CCGR1_CG7_Pos // CCGR1, CG7
|
||||
ClockIpAdc1 Clock = (1 << 8) | CCM_CCGR1_CG8_Pos // CCGR1, CG8
|
||||
ClockIpSemcExsc Clock = (1 << 8) | CCM_CCGR1_CG9_Pos // CCGR1, CG9
|
||||
ClockIpGpt1 Clock = (1 << 8) | CCM_CCGR1_CG10_Pos // CCGR1, CG10
|
||||
ClockIpGpt1S Clock = (1 << 8) | CCM_CCGR1_CG11_Pos // CCGR1, CG11
|
||||
ClockIpLpuart4 Clock = (1 << 8) | CCM_CCGR1_CG12_Pos // CCGR1, CG12
|
||||
ClockIpGpio1 Clock = (1 << 8) | CCM_CCGR1_CG13_Pos // CCGR1, CG13
|
||||
ClockIpCsu Clock = (1 << 8) | CCM_CCGR1_CG14_Pos // CCGR1, CG14
|
||||
ClockIpGpio5 Clock = (1 << 8) | CCM_CCGR1_CG15_Pos // CCGR1, CG15
|
||||
|
||||
ClockIpOcramExsc Clock = (2 << 8) | CCM_CCGR2_CG0_Pos // CCGR2, CG0
|
||||
ClockIpCsi Clock = (2 << 8) | CCM_CCGR2_CG1_Pos // CCGR2, CG1
|
||||
ClockIpIomuxcSnvs Clock = (2 << 8) | CCM_CCGR2_CG2_Pos // CCGR2, CG2
|
||||
ClockIpLpi2c1 Clock = (2 << 8) | CCM_CCGR2_CG3_Pos // CCGR2, CG3
|
||||
ClockIpLpi2c2 Clock = (2 << 8) | CCM_CCGR2_CG4_Pos // CCGR2, CG4
|
||||
ClockIpLpi2c3 Clock = (2 << 8) | CCM_CCGR2_CG5_Pos // CCGR2, CG5
|
||||
ClockIpOcotp Clock = (2 << 8) | CCM_CCGR2_CG6_Pos // CCGR2, CG6
|
||||
ClockIpXbar3 Clock = (2 << 8) | CCM_CCGR2_CG7_Pos // CCGR2, CG7
|
||||
ClockIpIpmux1 Clock = (2 << 8) | CCM_CCGR2_CG8_Pos // CCGR2, CG8
|
||||
ClockIpIpmux2 Clock = (2 << 8) | CCM_CCGR2_CG9_Pos // CCGR2, CG9
|
||||
ClockIpIpmux3 Clock = (2 << 8) | CCM_CCGR2_CG10_Pos // CCGR2, CG10
|
||||
ClockIpXbar1 Clock = (2 << 8) | CCM_CCGR2_CG11_Pos // CCGR2, CG11
|
||||
ClockIpXbar2 Clock = (2 << 8) | CCM_CCGR2_CG12_Pos // CCGR2, CG12
|
||||
ClockIpGpio3 Clock = (2 << 8) | CCM_CCGR2_CG13_Pos // CCGR2, CG13
|
||||
ClockIpLcd Clock = (2 << 8) | CCM_CCGR2_CG14_Pos // CCGR2, CG14
|
||||
ClockIpPxp Clock = (2 << 8) | CCM_CCGR2_CG15_Pos // CCGR2, CG15
|
||||
|
||||
ClockIpFlexio2 Clock = (3 << 8) | CCM_CCGR3_CG0_Pos // CCGR3, CG0
|
||||
ClockIpLpuart5 Clock = (3 << 8) | CCM_CCGR3_CG1_Pos // CCGR3, CG1
|
||||
ClockIpSemc Clock = (3 << 8) | CCM_CCGR3_CG2_Pos // CCGR3, CG2
|
||||
ClockIpLpuart6 Clock = (3 << 8) | CCM_CCGR3_CG3_Pos // CCGR3, CG3
|
||||
ClockIpAoi1 Clock = (3 << 8) | CCM_CCGR3_CG4_Pos // CCGR3, CG4
|
||||
ClockIpLcdPixel Clock = (3 << 8) | CCM_CCGR3_CG5_Pos // CCGR3, CG5
|
||||
ClockIpGpio4 Clock = (3 << 8) | CCM_CCGR3_CG6_Pos // CCGR3, CG6
|
||||
ClockIpEwm0 Clock = (3 << 8) | CCM_CCGR3_CG7_Pos // CCGR3, CG7
|
||||
ClockIpWdog1 Clock = (3 << 8) | CCM_CCGR3_CG8_Pos // CCGR3, CG8
|
||||
ClockIpFlexRam Clock = (3 << 8) | CCM_CCGR3_CG9_Pos // CCGR3, CG9
|
||||
ClockIpAcmp1 Clock = (3 << 8) | CCM_CCGR3_CG10_Pos // CCGR3, CG10
|
||||
ClockIpAcmp2 Clock = (3 << 8) | CCM_CCGR3_CG11_Pos // CCGR3, CG11
|
||||
ClockIpAcmp3 Clock = (3 << 8) | CCM_CCGR3_CG12_Pos // CCGR3, CG12
|
||||
ClockIpAcmp4 Clock = (3 << 8) | CCM_CCGR3_CG13_Pos // CCGR3, CG13
|
||||
ClockIpOcram Clock = (3 << 8) | CCM_CCGR3_CG14_Pos // CCGR3, CG14
|
||||
ClockIpIomuxcSnvsGpr Clock = (3 << 8) | CCM_CCGR3_CG15_Pos // CCGR3, CG15
|
||||
|
||||
ClockIpIomuxc Clock = (4 << 8) | CCM_CCGR4_CG1_Pos // CCGR4, CG1
|
||||
ClockIpIomuxcGpr Clock = (4 << 8) | CCM_CCGR4_CG2_Pos // CCGR4, CG2
|
||||
ClockIpBee Clock = (4 << 8) | CCM_CCGR4_CG3_Pos // CCGR4, CG3
|
||||
ClockIpSimM7 Clock = (4 << 8) | CCM_CCGR4_CG4_Pos // CCGR4, CG4
|
||||
ClockIpTsc Clock = (4 << 8) | CCM_CCGR4_CG5_Pos // CCGR4, CG5
|
||||
ClockIpSimM Clock = (4 << 8) | CCM_CCGR4_CG6_Pos // CCGR4, CG6
|
||||
ClockIpSimEms Clock = (4 << 8) | CCM_CCGR4_CG7_Pos // CCGR4, CG7
|
||||
ClockIpPwm1 Clock = (4 << 8) | CCM_CCGR4_CG8_Pos // CCGR4, CG8
|
||||
ClockIpPwm2 Clock = (4 << 8) | CCM_CCGR4_CG9_Pos // CCGR4, CG9
|
||||
ClockIpPwm3 Clock = (4 << 8) | CCM_CCGR4_CG10_Pos // CCGR4, CG10
|
||||
ClockIpPwm4 Clock = (4 << 8) | CCM_CCGR4_CG11_Pos // CCGR4, CG11
|
||||
ClockIpEnc1 Clock = (4 << 8) | CCM_CCGR4_CG12_Pos // CCGR4, CG12
|
||||
ClockIpEnc2 Clock = (4 << 8) | CCM_CCGR4_CG13_Pos // CCGR4, CG13
|
||||
ClockIpEnc3 Clock = (4 << 8) | CCM_CCGR4_CG14_Pos // CCGR4, CG14
|
||||
ClockIpEnc4 Clock = (4 << 8) | CCM_CCGR4_CG15_Pos // CCGR4, CG15
|
||||
|
||||
ClockIpRom Clock = (5 << 8) | CCM_CCGR5_CG0_Pos // CCGR5, CG0
|
||||
ClockIpFlexio1 Clock = (5 << 8) | CCM_CCGR5_CG1_Pos // CCGR5, CG1
|
||||
ClockIpWdog3 Clock = (5 << 8) | CCM_CCGR5_CG2_Pos // CCGR5, CG2
|
||||
ClockIpDma Clock = (5 << 8) | CCM_CCGR5_CG3_Pos // CCGR5, CG3
|
||||
ClockIpKpp Clock = (5 << 8) | CCM_CCGR5_CG4_Pos // CCGR5, CG4
|
||||
ClockIpWdog2 Clock = (5 << 8) | CCM_CCGR5_CG5_Pos // CCGR5, CG5
|
||||
ClockIpAipsTz4 Clock = (5 << 8) | CCM_CCGR5_CG6_Pos // CCGR5, CG6
|
||||
ClockIpSpdif Clock = (5 << 8) | CCM_CCGR5_CG7_Pos // CCGR5, CG7
|
||||
ClockIpSimMain Clock = (5 << 8) | CCM_CCGR5_CG8_Pos // CCGR5, CG8
|
||||
ClockIpSai1 Clock = (5 << 8) | CCM_CCGR5_CG9_Pos // CCGR5, CG9
|
||||
ClockIpSai2 Clock = (5 << 8) | CCM_CCGR5_CG10_Pos // CCGR5, CG10
|
||||
ClockIpSai3 Clock = (5 << 8) | CCM_CCGR5_CG11_Pos // CCGR5, CG11
|
||||
ClockIpLpuart1 Clock = (5 << 8) | CCM_CCGR5_CG12_Pos // CCGR5, CG12
|
||||
ClockIpLpuart7 Clock = (5 << 8) | CCM_CCGR5_CG13_Pos // CCGR5, CG13
|
||||
ClockIpSnvsHp Clock = (5 << 8) | CCM_CCGR5_CG14_Pos // CCGR5, CG14
|
||||
ClockIpSnvsLp Clock = (5 << 8) | CCM_CCGR5_CG15_Pos // CCGR5, CG15
|
||||
|
||||
ClockIpUsbOh3 Clock = (6 << 8) | CCM_CCGR6_CG0_Pos // CCGR6, CG0
|
||||
ClockIpUsdhc1 Clock = (6 << 8) | CCM_CCGR6_CG1_Pos // CCGR6, CG1
|
||||
ClockIpUsdhc2 Clock = (6 << 8) | CCM_CCGR6_CG2_Pos // CCGR6, CG2
|
||||
ClockIpDcdc Clock = (6 << 8) | CCM_CCGR6_CG3_Pos // CCGR6, CG3
|
||||
ClockIpIpmux4 Clock = (6 << 8) | CCM_CCGR6_CG4_Pos // CCGR6, CG4
|
||||
ClockIpFlexSpi Clock = (6 << 8) | CCM_CCGR6_CG5_Pos // CCGR6, CG5
|
||||
ClockIpTrng Clock = (6 << 8) | CCM_CCGR6_CG6_Pos // CCGR6, CG6
|
||||
ClockIpLpuart8 Clock = (6 << 8) | CCM_CCGR6_CG7_Pos // CCGR6, CG7
|
||||
ClockIpTimer4 Clock = (6 << 8) | CCM_CCGR6_CG8_Pos // CCGR6, CG8
|
||||
ClockIpAipsTz3 Clock = (6 << 8) | CCM_CCGR6_CG9_Pos // CCGR6, CG9
|
||||
ClockIpSimPer Clock = (6 << 8) | CCM_CCGR6_CG10_Pos // CCGR6, CG10
|
||||
ClockIpAnadig Clock = (6 << 8) | CCM_CCGR6_CG11_Pos // CCGR6, CG11
|
||||
ClockIpLpi2c4 Clock = (6 << 8) | CCM_CCGR6_CG12_Pos // CCGR6, CG12
|
||||
ClockIpTimer1 Clock = (6 << 8) | CCM_CCGR6_CG13_Pos // CCGR6, CG13
|
||||
ClockIpTimer2 Clock = (6 << 8) | CCM_CCGR6_CG14_Pos // CCGR6, CG14
|
||||
ClockIpTimer3 Clock = (6 << 8) | CCM_CCGR6_CG15_Pos // CCGR6, CG15
|
||||
|
||||
ClockIpEnet2 Clock = (7 << 8) | CCM_CCGR7_CG0_Pos // CCGR7, CG0
|
||||
ClockIpFlexSpi2 Clock = (7 << 8) | CCM_CCGR7_CG1_Pos // CCGR7, CG1
|
||||
ClockIpAxbsL Clock = (7 << 8) | CCM_CCGR7_CG2_Pos // CCGR7, CG2
|
||||
ClockIpCan3 Clock = (7 << 8) | CCM_CCGR7_CG3_Pos // CCGR7, CG3
|
||||
ClockIpCan3S Clock = (7 << 8) | CCM_CCGR7_CG4_Pos // CCGR7, CG4
|
||||
ClockIpAipsLite Clock = (7 << 8) | CCM_CCGR7_CG5_Pos // CCGR7, CG5
|
||||
ClockIpFlexio3 Clock = (7 << 8) | CCM_CCGR7_CG6_Pos // CCGR7, CG6
|
||||
)
|
||||
|
||||
// PLL name
|
||||
const (
|
||||
ClockPllArm Clock = ((offPllArm & 0xFFF) << 16) | CCM_ANALOG_PLL_ARM_ENABLE_Pos // PLL ARM
|
||||
ClockPllSys Clock = ((offPllSys & 0xFFF) << 16) | CCM_ANALOG_PLL_SYS_ENABLE_Pos // PLL SYS
|
||||
ClockPllUsb1 Clock = ((offPllUsb1 & 0xFFF) << 16) | CCM_ANALOG_PLL_USB1_ENABLE_Pos // PLL USB1
|
||||
ClockPllAudio Clock = ((offPllAudio & 0xFFF) << 16) | CCM_ANALOG_PLL_AUDIO_ENABLE_Pos // PLL Audio
|
||||
ClockPllVideo Clock = ((offPllVideo & 0xFFF) << 16) | CCM_ANALOG_PLL_VIDEO_ENABLE_Pos // PLL Video
|
||||
ClockPllEnet Clock = ((offPllEnet & 0xFFF) << 16) | CCM_ANALOG_PLL_ENET_ENABLE_Pos // PLL Enet0
|
||||
ClockPllEnet2 Clock = ((offPllEnet & 0xFFF) << 16) | CCM_ANALOG_PLL_ENET_ENET2_REF_EN_Pos // PLL Enet1
|
||||
ClockPllEnet25M Clock = ((offPllEnet & 0xFFF) << 16) | CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN_Pos // PLL Enet2
|
||||
ClockPllUsb2 Clock = ((offPllUsb2 & 0xFFF) << 16) | CCM_ANALOG_PLL_USB2_ENABLE_Pos // PLL USB2
|
||||
)
|
||||
|
||||
// PLL PFD name
|
||||
const (
|
||||
ClockPfd0 Clock = 0 // PLL PFD0
|
||||
ClockPfd1 Clock = 1 // PLL PFD1
|
||||
ClockPfd2 Clock = 2 // PLL PFD2
|
||||
ClockPfd3 Clock = 3 // PLL PFD3
|
||||
)
|
||||
|
||||
// Named clock muxes of integrated peripherals
|
||||
const (
|
||||
MuxIpPll3Sw Clock = (offCCSR & 0xFF) | (CCM_CCSR_PLL3_SW_CLK_SEL_Pos << 8) | (((CCM_CCSR_PLL3_SW_CLK_SEL_Msk >> CCM_CCSR_PLL3_SW_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // pll3_sw_clk mux name
|
||||
MuxIpPeriph Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_PERIPH_CLK_SEL_Pos << 8) | (((CCM_CBCDR_PERIPH_CLK_SEL_Msk >> CCM_CBCDR_PERIPH_CLK_SEL_Pos) & 0x1FFF) << 13) | (CCM_CDHIPR_PERIPH_CLK_SEL_BUSY_Pos << 26) // periph mux name
|
||||
MuxIpSemcAlt Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_SEMC_ALT_CLK_SEL_Pos << 8) | (((CCM_CBCDR_SEMC_ALT_CLK_SEL_Msk >> CCM_CBCDR_SEMC_ALT_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // semc mux name
|
||||
MuxIpSemc Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_SEMC_CLK_SEL_Pos << 8) | (((CCM_CBCDR_SEMC_CLK_SEL_Msk >> CCM_CBCDR_SEMC_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // semc mux name
|
||||
MuxIpPrePeriph Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_PRE_PERIPH_CLK_SEL_Pos << 8) | (((CCM_CBCMR_PRE_PERIPH_CLK_SEL_Msk >> CCM_CBCMR_PRE_PERIPH_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // pre-periph mux name
|
||||
MuxIpTrace Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_TRACE_CLK_SEL_Pos << 8) | (((CCM_CBCMR_TRACE_CLK_SEL_Msk >> CCM_CBCMR_TRACE_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // trace mux name
|
||||
MuxIpPeriphClk2 Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_PERIPH_CLK2_SEL_Pos << 8) | (((CCM_CBCMR_PERIPH_CLK2_SEL_Msk >> CCM_CBCMR_PERIPH_CLK2_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // periph clock2 mux name
|
||||
MuxIpFlexSpi2 Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_FLEXSPI2_CLK_SEL_Pos << 8) | (((CCM_CBCMR_FLEXSPI2_CLK_SEL_Msk >> CCM_CBCMR_FLEXSPI2_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexspi2 mux name
|
||||
MuxIpLpspi Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_LPSPI_CLK_SEL_Pos << 8) | (((CCM_CBCMR_LPSPI_CLK_SEL_Msk >> CCM_CBCMR_LPSPI_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lpspi mux name
|
||||
MuxIpFlexSpi Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_FLEXSPI_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_FLEXSPI_CLK_SEL_Msk >> CCM_CSCMR1_FLEXSPI_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexspi mux name
|
||||
MuxIpUsdhc2 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_USDHC2_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_USDHC2_CLK_SEL_Msk >> CCM_CSCMR1_USDHC2_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // usdhc2 mux name
|
||||
MuxIpUsdhc1 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_USDHC1_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_USDHC1_CLK_SEL_Msk >> CCM_CSCMR1_USDHC1_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // usdhc1 mux name
|
||||
MuxIpSai3 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_SAI3_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_SAI3_CLK_SEL_Msk >> CCM_CSCMR1_SAI3_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai3 mux name
|
||||
MuxIpSai2 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_SAI2_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_SAI2_CLK_SEL_Msk >> CCM_CSCMR1_SAI2_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai2 mux name
|
||||
MuxIpSai1 Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_SAI1_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_SAI1_CLK_SEL_Msk >> CCM_CSCMR1_SAI1_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai1 mux name
|
||||
MuxIpPerclk Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_PERCLK_CLK_SEL_Pos << 8) | (((CCM_CSCMR1_PERCLK_CLK_SEL_Msk >> CCM_CSCMR1_PERCLK_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // perclk mux name
|
||||
MuxIpFlexio2 Clock = (offCSCMR2 & 0xFF) | (CCM_CSCMR2_FLEXIO2_CLK_SEL_Pos << 8) | (((CCM_CSCMR2_FLEXIO2_CLK_SEL_Msk >> CCM_CSCMR2_FLEXIO2_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio2 mux name
|
||||
MuxIpCan Clock = (offCSCMR2 & 0xFF) | (CCM_CSCMR2_CAN_CLK_SEL_Pos << 8) | (((CCM_CSCMR2_CAN_CLK_SEL_Msk >> CCM_CSCMR2_CAN_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // can mux name
|
||||
MuxIpUart Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_UART_CLK_SEL_Pos << 8) | (((CCM_CSCDR1_UART_CLK_SEL_Msk >> CCM_CSCDR1_UART_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // uart mux name
|
||||
MuxIpSpdif Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_SPDIF0_CLK_SEL_Pos << 8) | (((CCM_CDCDR_SPDIF0_CLK_SEL_Msk >> CCM_CDCDR_SPDIF0_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // spdif mux name
|
||||
MuxIpFlexio1 Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_FLEXIO1_CLK_SEL_Pos << 8) | (((CCM_CDCDR_FLEXIO1_CLK_SEL_Msk >> CCM_CDCDR_FLEXIO1_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio1 mux name
|
||||
MuxIpLpi2c Clock = (offCSCDR2 & 0xFF) | (CCM_CSCDR2_LPI2C_CLK_SEL_Pos << 8) | (((CCM_CSCDR2_LPI2C_CLK_SEL_Msk >> CCM_CSCDR2_LPI2C_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lpi2c mux name
|
||||
MuxIpLcdifPre Clock = (offCSCDR2 & 0xFF) | (CCM_CSCDR2_LCDIF_PRE_CLK_SEL_Pos << 8) | (((CCM_CSCDR2_LCDIF_PRE_CLK_SEL_Msk >> CCM_CSCDR2_LCDIF_PRE_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lcdif pre mux name
|
||||
MuxIpCsi Clock = (offCSCDR3 & 0xFF) | (CCM_CSCDR3_CSI_CLK_SEL_Pos << 8) | (((CCM_CSCDR3_CSI_CLK_SEL_Msk >> CCM_CSCDR3_CSI_CLK_SEL_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // csi mux name
|
||||
)
|
||||
|
||||
// Named hardware clock divisors of integrated peripherals
|
||||
const (
|
||||
DivIpArm Clock = (offCACRR & 0xFF) | (CCM_CACRR_ARM_PODF_Pos << 8) | (((CCM_CACRR_ARM_PODF_Msk >> CCM_CACRR_ARM_PODF_Pos) & 0x1FFF) << 13) | (CCM_CDHIPR_ARM_PODF_BUSY_Pos << 26) // core div name
|
||||
DivIpPeriphClk2 Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_PERIPH_CLK2_PODF_Pos << 8) | (((CCM_CBCDR_PERIPH_CLK2_PODF_Msk >> CCM_CBCDR_PERIPH_CLK2_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // periph clock2 div name
|
||||
DivIpSemc Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_SEMC_PODF_Pos << 8) | (((CCM_CBCDR_SEMC_PODF_Msk >> CCM_CBCDR_SEMC_PODF_Pos) & 0x1FFF) << 13) | (CCM_CDHIPR_SEMC_PODF_BUSY_Pos << 26) // semc div name
|
||||
DivIpAhb Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_AHB_PODF_Pos << 8) | (((CCM_CBCDR_AHB_PODF_Msk >> CCM_CBCDR_AHB_PODF_Pos) & 0x1FFF) << 13) | (CCM_CDHIPR_AHB_PODF_BUSY_Pos << 26) // ahb div name
|
||||
DivIpIpg Clock = (offCBCDR & 0xFF) | (CCM_CBCDR_IPG_PODF_Pos << 8) | (((CCM_CBCDR_IPG_PODF_Msk >> CCM_CBCDR_IPG_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // ipg div name
|
||||
DivIpFlexSpi2 Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_FLEXSPI2_PODF_Pos << 8) | (((CCM_CBCMR_FLEXSPI2_PODF_Msk >> CCM_CBCMR_FLEXSPI2_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexspi2 div name
|
||||
DivIpLpspi Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_LPSPI_PODF_Pos << 8) | (((CCM_CBCMR_LPSPI_PODF_Msk >> CCM_CBCMR_LPSPI_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lpspi div name
|
||||
DivIpLcdif Clock = (offCBCMR & 0xFF) | (CCM_CBCMR_LCDIF_PODF_Pos << 8) | (((CCM_CBCMR_LCDIF_PODF_Msk >> CCM_CBCMR_LCDIF_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lcdif div name
|
||||
DivIpFlexSpi Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_FLEXSPI_PODF_Pos << 8) | (((CCM_CSCMR1_FLEXSPI_PODF_Msk >> CCM_CSCMR1_FLEXSPI_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexspi div name
|
||||
DivIpPerclk Clock = (offCSCMR1 & 0xFF) | (CCM_CSCMR1_PERCLK_PODF_Pos << 8) | (((CCM_CSCMR1_PERCLK_PODF_Msk >> CCM_CSCMR1_PERCLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // perclk div name
|
||||
DivIpCan Clock = (offCSCMR2 & 0xFF) | (CCM_CSCMR2_CAN_CLK_PODF_Pos << 8) | (((CCM_CSCMR2_CAN_CLK_PODF_Msk >> CCM_CSCMR2_CAN_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // can div name
|
||||
DivIpTrace Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_TRACE_PODF_Pos << 8) | (((CCM_CSCDR1_TRACE_PODF_Msk >> CCM_CSCDR1_TRACE_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // trace div name
|
||||
DivIpUsdhc2 Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_USDHC2_PODF_Pos << 8) | (((CCM_CSCDR1_USDHC2_PODF_Msk >> CCM_CSCDR1_USDHC2_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // usdhc2 div name
|
||||
DivIpUsdhc1 Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_USDHC1_PODF_Pos << 8) | (((CCM_CSCDR1_USDHC1_PODF_Msk >> CCM_CSCDR1_USDHC1_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // usdhc1 div name
|
||||
DivIpUart Clock = (offCSCDR1 & 0xFF) | (CCM_CSCDR1_UART_CLK_PODF_Pos << 8) | (((CCM_CSCDR1_UART_CLK_PODF_Msk >> CCM_CSCDR1_UART_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // uart div name
|
||||
DivIpFlexio2 Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_FLEXIO2_CLK_PODF_Pos << 8) | (((CCM_CS1CDR_FLEXIO2_CLK_PODF_Msk >> CCM_CS1CDR_FLEXIO2_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio2 pre div name
|
||||
DivIpSai3Pre Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_SAI3_CLK_PRED_Pos << 8) | (((CCM_CS1CDR_SAI3_CLK_PRED_Msk >> CCM_CS1CDR_SAI3_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai3 pre div name
|
||||
DivIpSai3 Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_SAI3_CLK_PODF_Pos << 8) | (((CCM_CS1CDR_SAI3_CLK_PODF_Msk >> CCM_CS1CDR_SAI3_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai3 div name
|
||||
DivIpFlexio2Pre Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_FLEXIO2_CLK_PRED_Pos << 8) | (((CCM_CS1CDR_FLEXIO2_CLK_PRED_Msk >> CCM_CS1CDR_FLEXIO2_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai3 pre div name
|
||||
DivIpSai1Pre Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_SAI1_CLK_PRED_Pos << 8) | (((CCM_CS1CDR_SAI1_CLK_PRED_Msk >> CCM_CS1CDR_SAI1_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai1 pre div name
|
||||
DivIpSai1 Clock = (offCS1CDR & 0xFF) | (CCM_CS1CDR_SAI1_CLK_PODF_Pos << 8) | (((CCM_CS1CDR_SAI1_CLK_PODF_Msk >> CCM_CS1CDR_SAI1_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai1 div name
|
||||
DivIpSai2Pre Clock = (offCS2CDR & 0xFF) | (CCM_CS2CDR_SAI2_CLK_PRED_Pos << 8) | (((CCM_CS2CDR_SAI2_CLK_PRED_Msk >> CCM_CS2CDR_SAI2_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai2 pre div name
|
||||
DivIpSai2 Clock = (offCS2CDR & 0xFF) | (CCM_CS2CDR_SAI2_CLK_PODF_Pos << 8) | (((CCM_CS2CDR_SAI2_CLK_PODF_Msk >> CCM_CS2CDR_SAI2_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // sai2 div name
|
||||
DivIpSpdif0Pre Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_SPDIF0_CLK_PRED_Pos << 8) | (((CCM_CDCDR_SPDIF0_CLK_PRED_Msk >> CCM_CDCDR_SPDIF0_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // spdif pre div name
|
||||
DivIpSpdif0 Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_SPDIF0_CLK_PODF_Pos << 8) | (((CCM_CDCDR_SPDIF0_CLK_PODF_Msk >> CCM_CDCDR_SPDIF0_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // spdif div name
|
||||
DivIpFlexio1Pre Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_FLEXIO1_CLK_PRED_Pos << 8) | (((CCM_CDCDR_FLEXIO1_CLK_PRED_Msk >> CCM_CDCDR_FLEXIO1_CLK_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio1 pre div name
|
||||
DivIpFlexio1 Clock = (offCDCDR & 0xFF) | (CCM_CDCDR_FLEXIO1_CLK_PODF_Pos << 8) | (((CCM_CDCDR_FLEXIO1_CLK_PODF_Msk >> CCM_CDCDR_FLEXIO1_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // flexio1 div name
|
||||
DivIpLpi2c Clock = (offCSCDR2 & 0xFF) | (CCM_CSCDR2_LPI2C_CLK_PODF_Pos << 8) | (((CCM_CSCDR2_LPI2C_CLK_PODF_Msk >> CCM_CSCDR2_LPI2C_CLK_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lpi2c div name
|
||||
DivIpLcdifPre Clock = (offCSCDR2 & 0xFF) | (CCM_CSCDR2_LCDIF_PRED_Pos << 8) | (((CCM_CSCDR2_LCDIF_PRED_Msk >> CCM_CSCDR2_LCDIF_PRED_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // lcdif pre div name
|
||||
DivIpCsi Clock = (offCSCDR3 & 0xFF) | (CCM_CSCDR3_CSI_PODF_Pos << 8) | (((CCM_CSCDR3_CSI_PODF_Msk >> CCM_CSCDR3_CSI_PODF_Pos) & 0x1FFF) << 13) | (noBusyWait << 26) // csi div name
|
||||
)
|
||||
|
||||
// Selected clock offsets
|
||||
const (
|
||||
offCCSR = 0x0C
|
||||
offCBCDR = 0x14
|
||||
offCBCMR = 0x18
|
||||
offCSCMR1 = 0x1C
|
||||
offCSCMR2 = 0x20
|
||||
offCSCDR1 = 0x24
|
||||
offCDCDR = 0x30
|
||||
offCSCDR2 = 0x38
|
||||
offCSCDR3 = 0x3C
|
||||
offCACRR = 0x10
|
||||
offCS1CDR = 0x28
|
||||
offCS2CDR = 0x2C
|
||||
|
||||
offPllArm = 0x00
|
||||
offPllSys = 0x30
|
||||
offPllUsb1 = 0x10
|
||||
offPllAudio = 0x70
|
||||
offPllVideo = 0xA0
|
||||
offPllEnet = 0xE0
|
||||
offPllUsb2 = 0x20
|
||||
|
||||
noBusyWait = 0x20
|
||||
)
|
||||
|
||||
// analog PLL definition
|
||||
const (
|
||||
pllBypassPos = 16
|
||||
pllBypassClkSrcMsk = 0xC000
|
||||
pllBypassClkSrcPos = 14
|
||||
)
|
||||
|
||||
// PLL clock source, bypass cloco source also
|
||||
const (
|
||||
pllSrc24M = 0 // Pll clock source 24M
|
||||
pllSrcClkPN = 1 // Pll clock source CLK1_P and CLK1_N
|
||||
)
|
||||
|
||||
const (
|
||||
clockNotNeeded uint32 = 0 // Clock is off during all modes
|
||||
clockNeededRun uint32 = 1 // Clock is on in run mode, but off in WAIT and STOP modes
|
||||
clockNeededRunWait uint32 = 3 // Clock is on during all modes, except STOP mode
|
||||
)
|
||||
|
||||
// getGate returns the CCM clock gating register for the receiver clk.
|
||||
func (clk Clock) getGate() *volatile.Register32 {
|
||||
switch clk >> 8 {
|
||||
case 0:
|
||||
return &CCM.CCGR0
|
||||
case 1:
|
||||
return &CCM.CCGR1
|
||||
case 2:
|
||||
return &CCM.CCGR2
|
||||
case 3:
|
||||
return &CCM.CCGR3
|
||||
case 4:
|
||||
return &CCM.CCGR4
|
||||
case 5:
|
||||
return &CCM.CCGR5
|
||||
case 6:
|
||||
return &CCM.CCGR6
|
||||
case 7:
|
||||
return &CCM.CCGR7
|
||||
default:
|
||||
panic("nxp: invalid clock")
|
||||
}
|
||||
}
|
||||
|
||||
// setGate enables or disables the receiver clk using its gating register.
|
||||
func (clk Clock) setGate(value uint32) {
|
||||
reg := clk.getGate()
|
||||
shift := clk & 0x1F
|
||||
reg.Set((reg.Get() & ^(3 << shift)) | (value << shift))
|
||||
}
|
||||
|
||||
func (clk Clock) setCcm(value uint32) {
|
||||
const ccmBase = 0x400fc000
|
||||
reg := (*volatile.Register32)(unsafe.Pointer(uintptr(ccmBase + (uint32(clk) & 0xFF))))
|
||||
msk := ((uint32(clk) >> 13) & 0x1FFF) << ((uint32(clk) >> 8) & 0x1F)
|
||||
pos := (uint32(clk) >> 8) & 0x1F
|
||||
bsy := (uint32(clk) >> 26) & 0x3F
|
||||
reg.Set((reg.Get() & ^uint32(msk)) | ((value << pos) & msk))
|
||||
if bsy < noBusyWait {
|
||||
for CCM.CDHIPR.HasBits(1 << bsy) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setSysPfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd528 := CCM_ANALOG.PFD_528.Get() &
|
||||
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
func setUsb1Pfd(value ...uint32) {
|
||||
for i, val := range value {
|
||||
pfd480 := CCM_ANALOG.PFD_480.Get() &
|
||||
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
|
||||
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
|
||||
// disable the clock output first
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
|
||||
// set the new value and enable output
|
||||
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
|
||||
}
|
||||
}
|
||||
|
||||
// PLL configuration for ARM
|
||||
type ClockConfigArmPll struct {
|
||||
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
|
||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
||||
}
|
||||
|
||||
func (cfg ClockConfigArmPll) Configure() {
|
||||
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_ARM.Set(
|
||||
(CCM_ANALOG.PLL_ARM.Get() & ^uint32(CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_ARM_BYPASS_Msk | src)
|
||||
|
||||
sel := (cfg.LoopDivider << CCM_ANALOG_PLL_ARM_DIV_SELECT_Pos) & CCM_ANALOG_PLL_ARM_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_ARM.Set(
|
||||
(CCM_ANALOG.PLL_ARM.Get() & ^uint32(CCM_ANALOG_PLL_ARM_DIV_SELECT_Msk|CCM_ANALOG_PLL_ARM_POWERDOWN_Msk)) |
|
||||
CCM_ANALOG_PLL_ARM_ENABLE_Msk | sel)
|
||||
|
||||
for !CCM_ANALOG.PLL_ARM.HasBits(CCM_ANALOG_PLL_ARM_LOCK_Msk) {
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_ARM.ClearBits(CCM_ANALOG_PLL_ARM_BYPASS_Msk)
|
||||
}
|
||||
|
||||
// PLL configuration for System
|
||||
type ClockConfigSysPll struct {
|
||||
LoopDivider uint8 // PLL loop divider. Intended to be 1 (528M): 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
||||
Numerator uint32 // 30 bit Numerator of fractional loop divider.
|
||||
Denominator uint32 // 30 bit Denominator of fractional loop divider
|
||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
||||
SsStop uint16 // Stop value to get frequency change.
|
||||
SsEnable uint8 // Enable spread spectrum modulation
|
||||
SsStep uint16 // Step value to get frequency change step.
|
||||
}
|
||||
|
||||
func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
|
||||
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_SYS.Set(
|
||||
(CCM_ANALOG.PLL_SYS.Get() & ^uint32(CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_SYS_BYPASS_Msk | src)
|
||||
|
||||
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_SYS_DIV_SELECT_Pos) & CCM_ANALOG_PLL_SYS_DIV_SELECT_Msk
|
||||
CCM_ANALOG.PLL_SYS.Set(
|
||||
(CCM_ANALOG.PLL_SYS.Get() & ^uint32(CCM_ANALOG_PLL_SYS_DIV_SELECT_Msk|CCM_ANALOG_PLL_SYS_POWERDOWN_Msk)) |
|
||||
CCM_ANALOG_PLL_SYS_ENABLE_Msk | sel)
|
||||
|
||||
// initialize the fractional mode
|
||||
CCM_ANALOG.PLL_SYS_NUM.Set((cfg.Numerator << CCM_ANALOG_PLL_SYS_NUM_A_Pos) & CCM_ANALOG_PLL_SYS_NUM_A_Msk)
|
||||
CCM_ANALOG.PLL_SYS_DENOM.Set((cfg.Denominator << CCM_ANALOG_PLL_SYS_DENOM_B_Pos) & CCM_ANALOG_PLL_SYS_DENOM_B_Msk)
|
||||
|
||||
// initialize the spread spectrum mode
|
||||
inc := (uint32(cfg.SsStep) << CCM_ANALOG_PLL_SYS_SS_STEP_Pos) & CCM_ANALOG_PLL_SYS_SS_STEP_Msk
|
||||
enb := (uint32(cfg.SsEnable) << CCM_ANALOG_PLL_SYS_SS_ENABLE_Pos) & CCM_ANALOG_PLL_SYS_SS_ENABLE_Msk
|
||||
stp := (uint32(cfg.SsStop) << CCM_ANALOG_PLL_SYS_SS_STOP_Pos) & CCM_ANALOG_PLL_SYS_SS_STOP_Msk
|
||||
CCM_ANALOG.PLL_SYS_SS.Set(inc | enb | stp)
|
||||
|
||||
for !CCM_ANALOG.PLL_SYS.HasBits(CCM_ANALOG_PLL_SYS_LOCK_Msk) {
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_SYS.ClearBits(CCM_ANALOG_PLL_SYS_BYPASS_Msk)
|
||||
|
||||
// update PFDs after update
|
||||
setSysPfd(pfd...)
|
||||
}
|
||||
|
||||
// PLL configuration for USB
|
||||
type ClockConfigUsbPll struct {
|
||||
Instance uint8 // USB PLL number (1 or 2)
|
||||
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
|
||||
Src uint8 // Pll clock source, reference _clock_pll_clk_src
|
||||
}
|
||||
|
||||
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
|
||||
|
||||
switch cfg.Instance {
|
||||
case 1:
|
||||
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB1.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB1_SET.Set(
|
||||
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
|
||||
|
||||
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
|
||||
|
||||
// update PFDs after update
|
||||
setUsb1Pfd(pfd...)
|
||||
|
||||
case 2:
|
||||
// bypass PLL first
|
||||
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
|
||||
|
||||
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
|
||||
CCM_ANALOG.PLL_USB2.Set(
|
||||
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
|
||||
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
|
||||
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
|
||||
|
||||
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
|
||||
}
|
||||
|
||||
// disable bypass
|
||||
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
|
||||
|
||||
default:
|
||||
panic("nxp: invalid USB PLL")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// Hardfault aliases for definitions that have inconsistent naming (which are
|
||||
// auto-generated by gen-device-svd.go) among devices in package nxp.
|
||||
|
||||
// +build nxp,mimxrt1062
|
||||
|
||||
package nxp
|
||||
|
||||
const (
|
||||
HardFault_CFSR_IACCVIOL = SCB_CFSR_IACCVIOL
|
||||
HardFault_CFSR_DACCVIOL = SCB_CFSR_DACCVIOL
|
||||
HardFault_CFSR_MUNSTKERR = SCB_CFSR_MUNSTKERR
|
||||
HardFault_CFSR_MSTKERR = SCB_CFSR_MSTKERR
|
||||
HardFault_CFSR_MLSPERR = SCB_CFSR_MLSPERR
|
||||
HardFault_CFSR_IBUSERR = SCB_CFSR_IBUSERR
|
||||
HardFault_CFSR_PRECISERR = SCB_CFSR_PRECISERR
|
||||
HardFault_CFSR_IMPRECISERR = SCB_CFSR_IMPRECISERR
|
||||
HardFault_CFSR_UNSTKERR = SCB_CFSR_UNSTKERR
|
||||
HardFault_CFSR_STKERR = SCB_CFSR_STKERR
|
||||
HardFault_CFSR_LSPERR = SCB_CFSR_LSPERR
|
||||
HardFault_CFSR_UNDEFINSTR = SCB_CFSR_UNDEFINSTR
|
||||
HardFault_CFSR_INVSTATE = SCB_CFSR_INVSTATE
|
||||
HardFault_CFSR_INVPC = SCB_CFSR_INVPC
|
||||
HardFault_CFSR_NOCP = SCB_CFSR_NOCP
|
||||
HardFault_CFSR_UNALIGNED = SCB_CFSR_UNALIGNED
|
||||
HardFault_CFSR_DIVBYZERO = SCB_CFSR_DIVBYZERO
|
||||
HardFault_CFSR_MMARVALID = SCB_CFSR_MMARVALID
|
||||
HardFault_CFSR_BFARVALID = SCB_CFSR_BFARVALID
|
||||
)
|
||||
@@ -0,0 +1,278 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// Type definitions, fields, and constants associated with the MPU peripheral
|
||||
// of the NXP MIMXRT1062.
|
||||
|
||||
// +build nxp,mimxrt1062
|
||||
|
||||
package nxp
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type MPU_Type struct {
|
||||
TYPE volatile.Register32 // 0x000 (R/ ) - MPU Type Register
|
||||
CTRL volatile.Register32 // 0x004 (R/W) - MPU Control Register
|
||||
RNR volatile.Register32 // 0x008 (R/W) - MPU Region RNRber Register
|
||||
RBAR volatile.Register32 // 0x00C (R/W) - MPU Region Base Address Register
|
||||
RASR volatile.Register32 // 0x010 (R/W) - MPU Region Attribute and Size Register
|
||||
RBAR_A1 volatile.Register32 // 0x014 (R/W) - MPU Alias 1 Region Base Address Register
|
||||
RASR_A1 volatile.Register32 // 0x018 (R/W) - MPU Alias 1 Region Attribute and Size Register
|
||||
RBAR_A2 volatile.Register32 // 0x01C (R/W) - MPU Alias 2 Region Base Address Register
|
||||
RASR_A2 volatile.Register32 // 0x020 (R/W) - MPU Alias 2 Region Attribute and Size Register
|
||||
RBAR_A3 volatile.Register32 // 0x024 (R/W) - MPU Alias 3 Region Base Address Register
|
||||
RASR_A3 volatile.Register32 // 0x028 (R/W) - MPU Alias 3 Region Attribute and Size Register
|
||||
}
|
||||
|
||||
var MPU = (*MPU_Type)(unsafe.Pointer(uintptr(0xe000ed90)))
|
||||
|
||||
type (
|
||||
RegionSize uint32
|
||||
AccessPerms uint32
|
||||
Extension uint32
|
||||
)
|
||||
|
||||
// MPU Control Register Definitions
|
||||
const (
|
||||
MPU_CTRL_PRIVDEFENA_Pos = 2 // MPU CTRL: PRIVDEFENA Position
|
||||
MPU_CTRL_PRIVDEFENA_Msk = 1 << MPU_CTRL_PRIVDEFENA_Pos // MPU CTRL: PRIVDEFENA Mask
|
||||
MPU_CTRL_HFNMIENA_Pos = 1 // MPU CTRL: HFNMIENA Position
|
||||
MPU_CTRL_HFNMIENA_Msk = 1 << MPU_CTRL_HFNMIENA_Pos // MPU CTRL: HFNMIENA Mask
|
||||
MPU_CTRL_ENABLE_Pos = 0 // MPU CTRL: ENABLE Position
|
||||
MPU_CTRL_ENABLE_Msk = 1 // MPU CTRL: ENABLE Mask
|
||||
)
|
||||
|
||||
// MPU Region Base Address Register Definitions
|
||||
const (
|
||||
MPU_RBAR_ADDR_Pos = 5 // MPU RBAR: ADDR Position
|
||||
MPU_RBAR_ADDR_Msk = 0x7FFFFFF << MPU_RBAR_ADDR_Pos // MPU RBAR: ADDR Mask
|
||||
MPU_RBAR_VALID_Pos = 4 // MPU RBAR: VALID Position
|
||||
MPU_RBAR_VALID_Msk = 1 << MPU_RBAR_VALID_Pos // MPU RBAR: VALID Mask
|
||||
MPU_RBAR_REGION_Pos = 0 // MPU RBAR: REGION Position
|
||||
MPU_RBAR_REGION_Msk = 0xF // MPU RBAR: REGION Mask
|
||||
)
|
||||
|
||||
// MPU Region Attribute and Size Register Definitions
|
||||
const (
|
||||
MPU_RASR_ATTRS_Pos = 16 // MPU RASR: MPU Region Attribute field Position
|
||||
MPU_RASR_ATTRS_Msk = 0xFFFF << MPU_RASR_ATTRS_Pos // MPU RASR: MPU Region Attribute field Mask
|
||||
MPU_RASR_XN_Pos = 28 // MPU RASR: ATTRS.XN Position
|
||||
MPU_RASR_XN_Msk = 1 << MPU_RASR_XN_Pos // MPU RASR: ATTRS.XN Mask
|
||||
MPU_RASR_AP_Pos = 24 // MPU RASR: ATTRS.AP Position
|
||||
MPU_RASR_AP_Msk = 0x7 << MPU_RASR_AP_Pos // MPU RASR: ATTRS.AP Mask
|
||||
MPU_RASR_TEX_Pos = 19 // MPU RASR: ATTRS.TEX Position
|
||||
MPU_RASR_TEX_Msk = 0x7 << MPU_RASR_TEX_Pos // MPU RASR: ATTRS.TEX Mask
|
||||
MPU_RASR_S_Pos = 18 // MPU RASR: ATTRS.S Position
|
||||
MPU_RASR_S_Msk = 1 << MPU_RASR_S_Pos // MPU RASR: ATTRS.S Mask
|
||||
MPU_RASR_C_Pos = 17 // MPU RASR: ATTRS.C Position
|
||||
MPU_RASR_C_Msk = 1 << MPU_RASR_C_Pos // MPU RASR: ATTRS.C Mask
|
||||
MPU_RASR_B_Pos = 16 // MPU RASR: ATTRS.B Position
|
||||
MPU_RASR_B_Msk = 1 << MPU_RASR_B_Pos // MPU RASR: ATTRS.B Mask
|
||||
MPU_RASR_SRD_Pos = 8 // MPU RASR: Sub-Region Disable Position
|
||||
MPU_RASR_SRD_Msk = 0xFF << MPU_RASR_SRD_Pos // MPU RASR: Sub-Region Disable Mask
|
||||
MPU_RASR_SIZE_Pos = 1 // MPU RASR: Region Size Field Position
|
||||
MPU_RASR_SIZE_Msk = 0x1F << MPU_RASR_SIZE_Pos // MPU RASR: Region Size Field Mask
|
||||
MPU_RASR_ENABLE_Pos = 0 // MPU RASR: Region enable bit Position
|
||||
MPU_RASR_ENABLE_Msk = 1 // MPU RASR: Region enable bit Disable Mask
|
||||
)
|
||||
|
||||
const (
|
||||
SCB_DCISW_WAY_Pos = 30 // SCB DCISW: Way Position
|
||||
SCB_DCISW_WAY_Msk = 3 << SCB_DCISW_WAY_Pos // SCB DCISW: Way Mask
|
||||
SCB_DCISW_SET_Pos = 5 // SCB DCISW: Set Position
|
||||
SCB_DCISW_SET_Msk = 0x1FF << SCB_DCISW_SET_Pos // SCB DCISW: Set Mask
|
||||
)
|
||||
|
||||
const (
|
||||
SCB_DCCISW_WAY_Pos = 30 // SCB DCCISW: Way Position
|
||||
SCB_DCCISW_WAY_Msk = 3 << SCB_DCCISW_WAY_Pos // SCB DCCISW: Way Mask
|
||||
SCB_DCCISW_SET_Pos = 5 // SCB DCCISW: Set Position
|
||||
SCB_DCCISW_SET_Msk = 0x1FF << SCB_DCCISW_SET_Pos // SCB DCCISW: Set Mask
|
||||
)
|
||||
|
||||
const (
|
||||
RGNSZ_32B RegionSize = 0x04 // MPU Region Size 32 Bytes
|
||||
RGNSZ_64B RegionSize = 0x05 // MPU Region Size 64 Bytes
|
||||
RGNSZ_128B RegionSize = 0x06 // MPU Region Size 128 Bytes
|
||||
RGNSZ_256B RegionSize = 0x07 // MPU Region Size 256 Bytes
|
||||
RGNSZ_512B RegionSize = 0x08 // MPU Region Size 512 Bytes
|
||||
RGNSZ_1KB RegionSize = 0x09 // MPU Region Size 1 KByte
|
||||
RGNSZ_2KB RegionSize = 0x0A // MPU Region Size 2 KBytes
|
||||
RGNSZ_4KB RegionSize = 0x0B // MPU Region Size 4 KBytes
|
||||
RGNSZ_8KB RegionSize = 0x0C // MPU Region Size 8 KBytes
|
||||
RGNSZ_16KB RegionSize = 0x0D // MPU Region Size 16 KBytes
|
||||
RGNSZ_32KB RegionSize = 0x0E // MPU Region Size 32 KBytes
|
||||
RGNSZ_64KB RegionSize = 0x0F // MPU Region Size 64 KBytes
|
||||
RGNSZ_128KB RegionSize = 0x10 // MPU Region Size 128 KBytes
|
||||
RGNSZ_256KB RegionSize = 0x11 // MPU Region Size 256 KBytes
|
||||
RGNSZ_512KB RegionSize = 0x12 // MPU Region Size 512 KBytes
|
||||
RGNSZ_1MB RegionSize = 0x13 // MPU Region Size 1 MByte
|
||||
RGNSZ_2MB RegionSize = 0x14 // MPU Region Size 2 MBytes
|
||||
RGNSZ_4MB RegionSize = 0x15 // MPU Region Size 4 MBytes
|
||||
RGNSZ_8MB RegionSize = 0x16 // MPU Region Size 8 MBytes
|
||||
RGNSZ_16MB RegionSize = 0x17 // MPU Region Size 16 MBytes
|
||||
RGNSZ_32MB RegionSize = 0x18 // MPU Region Size 32 MBytes
|
||||
RGNSZ_64MB RegionSize = 0x19 // MPU Region Size 64 MBytes
|
||||
RGNSZ_128MB RegionSize = 0x1A // MPU Region Size 128 MBytes
|
||||
RGNSZ_256MB RegionSize = 0x1B // MPU Region Size 256 MBytes
|
||||
RGNSZ_512MB RegionSize = 0x1C // MPU Region Size 512 MBytes
|
||||
RGNSZ_1GB RegionSize = 0x1D // MPU Region Size 1 GByte
|
||||
RGNSZ_2GB RegionSize = 0x1E // MPU Region Size 2 GBytes
|
||||
RGNSZ_4GB RegionSize = 0x1F // MPU Region Size 4 GBytes
|
||||
)
|
||||
|
||||
const (
|
||||
PERM_NONE AccessPerms = 0 // MPU Access Permission no access
|
||||
PERM_PRIV AccessPerms = 1 // MPU Access Permission privileged access only
|
||||
PERM_URO AccessPerms = 2 // MPU Access Permission unprivileged access read-only
|
||||
PERM_FULL AccessPerms = 3 // MPU Access Permission full access
|
||||
PERM_PRO AccessPerms = 5 // MPU Access Permission privileged access read-only
|
||||
PERM_RO AccessPerms = 6 // MPU Access Permission read-only access
|
||||
)
|
||||
|
||||
const (
|
||||
EXTN_NORMAL Extension = 0
|
||||
EXTN_DEVICE Extension = 2
|
||||
)
|
||||
|
||||
func (mpu *MPU_Type) Enable(enable bool) {
|
||||
if enable {
|
||||
mpu.CTRL.Set(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk)
|
||||
SystemControl.SHCSR.SetBits(SCB_SHCSR_MEMFAULTENA_Msk)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
enableDcache(true)
|
||||
enableIcache(true)
|
||||
} else {
|
||||
enableIcache(false)
|
||||
enableDcache(false)
|
||||
arm.AsmFull(`
|
||||
dmb 0xF
|
||||
`, nil)
|
||||
SystemControl.SHCSR.ClearBits(SCB_SHCSR_MEMFAULTENA_Msk)
|
||||
mpu.CTRL.ClearBits(MPU_CTRL_ENABLE_Msk)
|
||||
}
|
||||
}
|
||||
|
||||
// MPU Region Base Address Register value
|
||||
func (mpu *MPU_Type) SetRBAR(region uint32, baseAddress uint32) {
|
||||
mpu.RBAR.Set((baseAddress & MPU_RBAR_ADDR_Msk) |
|
||||
(region & MPU_RBAR_REGION_Msk) | MPU_RBAR_VALID_Msk)
|
||||
}
|
||||
|
||||
// MPU Region Attribute and Size Register value
|
||||
func (mpu *MPU_Type) SetRASR(size RegionSize, access AccessPerms, ext Extension, exec, share, cache, buffer, disable bool) {
|
||||
boolBit := func(b bool) uint32 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
attr := ((uint32(ext) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) |
|
||||
((boolBit(share) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) |
|
||||
((boolBit(cache) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) |
|
||||
((boolBit(buffer) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk)
|
||||
mpu.RASR.Set(((boolBit(!exec) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) |
|
||||
((uint32(access) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) |
|
||||
(attr & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk)) |
|
||||
((boolBit(disable) << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk) |
|
||||
((uint32(size) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk) |
|
||||
MPU_RASR_ENABLE_Msk)
|
||||
}
|
||||
|
||||
func enableIcache(enable bool) {
|
||||
if enable != SystemControl.CCR.HasBits(SCB_CCR_IC_Msk) {
|
||||
if enable {
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
SystemControl.ICIALLU.Set(0)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
SystemControl.CCR.SetBits(SCB_CCR_IC_Msk)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
} else {
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
|
||||
SystemControl.ICIALLU.Set(0)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enableDcache(enable bool) {
|
||||
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
|
||||
if enable {
|
||||
SystemControl.CSSELR.Set(0)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
ccsidr := SystemControl.CCSIDR.Get()
|
||||
sets := (ccsidr & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos
|
||||
for sets != 0 {
|
||||
ways := (ccsidr & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos
|
||||
for ways != 0 {
|
||||
SystemControl.DCISW.Set(
|
||||
((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) |
|
||||
((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk))
|
||||
ways--
|
||||
}
|
||||
sets--
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
SystemControl.CCR.SetBits(SCB_CCR_DC_Msk)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
} else {
|
||||
var (
|
||||
ccsidr volatile.Register32
|
||||
sets volatile.Register32
|
||||
ways volatile.Register32
|
||||
)
|
||||
SystemControl.CSSELR.Set(0)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
SystemControl.CCR.ClearBits(SCB_CCR_DC_Msk)
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
`, nil)
|
||||
ccsidr.Set(SystemControl.CCSIDR.Get())
|
||||
sets.Set((ccsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
|
||||
for sets.Get() != 0 {
|
||||
ways.Set((ccsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
|
||||
for ways.Get() != 0 {
|
||||
SystemControl.DCCISW.Set(
|
||||
((sets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
|
||||
((ways.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk))
|
||||
ways.Set(ways.Get() - 1)
|
||||
}
|
||||
sets.Set(sets.Get() - 1)
|
||||
}
|
||||
arm.AsmFull(`
|
||||
dsb 0xF
|
||||
isb 0xF
|
||||
`, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// STM32FXXX (except stm32f1xx) bitfield definitions that are not
|
||||
// auto-generated by gen-device-svd.go
|
||||
|
||||
// These apply to the stm32 families that use the MODER, OTYPE amd PUPDR
|
||||
// registers for managing GPIO functionality.
|
||||
|
||||
// Add in other families that use the same settings, e.g. stm32f0xx, etc
|
||||
|
||||
// +build stm32,!stm32f103xx
|
||||
|
||||
package stm32
|
||||
|
||||
// AltFunc represents the alternate function peripherals that can be mapped to
|
||||
// the GPIO ports. Since these differ by what is supported on the stm32 family
|
||||
// they are defined in the more specific files
|
||||
type AltFunc uint8
|
||||
|
||||
// Family-wide common post-reset AltFunc. This represents
|
||||
// normal GPIO operation of the pins
|
||||
const AF0_SYSTEM AltFunc = 0
|
||||
|
||||
const (
|
||||
// Register values for the chip
|
||||
// GPIOx_MODER
|
||||
GPIOModeInput = 0
|
||||
GPIOModeOutputGeneral = 1
|
||||
GPIOModeOutputAltFunc = 2
|
||||
GPIOModeAnalog = 3
|
||||
|
||||
// GPIOx_OTYPER
|
||||
GPIOOutputTypePushPull = 0
|
||||
GPIOOutputTypeOpenDrain = 1
|
||||
|
||||
// GPIOx_OSPEEDR
|
||||
GPIOSpeedLow = 0
|
||||
GPIOSpeedMid = 1
|
||||
GPIOSpeedHigh = 2 // Note: this is also low speed on stm32f0, see RM0091
|
||||
GPIOSpeedVeryHigh = 3
|
||||
|
||||
// GPIOx_PUPDR
|
||||
GPIOPUPDRFloating = 0
|
||||
GPIOPUPDRPullUp = 1
|
||||
GPIOPUPDRPullDown = 2
|
||||
)
|
||||
|
||||
// SPI prescaler values fPCLK / X
|
||||
const (
|
||||
SPI_PCLK_2 = 0
|
||||
SPI_PCLK_4 = 1
|
||||
SPI_PCLK_8 = 2
|
||||
SPI_PCLK_16 = 3
|
||||
SPI_PCLK_32 = 4
|
||||
SPI_PCLK_64 = 5
|
||||
SPI_PCLK_128 = 6
|
||||
SPI_PCLK_256 = 7
|
||||
)
|
||||
@@ -1,88 +0,0 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||
|
||||
// +build stm32,stm32f103xx
|
||||
|
||||
package stm32
|
||||
|
||||
const (
|
||||
// Flash Access Control Register flag values.
|
||||
FLASH_ACR_LATENCY_0 = 0x00000001
|
||||
FLASH_ACR_LATENCY_1 = 0x00000002
|
||||
FLASH_ACR_LATENCY_2 = 0x00000004
|
||||
|
||||
// Reset and Clock Control Control Register flag values.
|
||||
|
||||
// System Clock source
|
||||
RCC_CFGR_SW_HSI = 0
|
||||
RCC_CFGR_SW_HSE = 1
|
||||
RCC_CFGR_SW_PLL = 2
|
||||
|
||||
// Flags for when System Clock source is set.
|
||||
RCC_CFGR_SWS_HSI = 0x00000000
|
||||
RCC_CFGR_SWS_HSE = 0x00000004
|
||||
RCC_CFGR_SWS_PLL = 0x00000008
|
||||
|
||||
// Sets PCLK1
|
||||
RCC_CFGR_PPRE1_DIV_NONE = 0x00000000
|
||||
RCC_CFGR_PPRE1_DIV_2 = 0x00000400
|
||||
RCC_CFGR_PPRE1_DIV_4 = 0x00000500
|
||||
RCC_CFGR_PPRE1_DIV_8 = 0x00000600
|
||||
RCC_CFGR_PPRE1_DIV_16 = 0x00000700
|
||||
|
||||
// Sets PCLK2
|
||||
RCC_CFGR_PPRE2_DIV_NONE = 0x00000000
|
||||
RCC_CFGR_PPRE2_DIV_2 = 0x00002000
|
||||
RCC_CFGR_PPRE2_DIV_4 = 0x00002800
|
||||
RCC_CFGR_PPRE2_DIV_8 = 0x00003000
|
||||
RCC_CFGR_PPRE2_DIV_16 = 0x00003800
|
||||
|
||||
// Sets PLL multiplier
|
||||
RCC_CFGR_PLLMUL_2 = 0x00000000
|
||||
RCC_CFGR_PLLMUL_3 = 0x00040000
|
||||
RCC_CFGR_PLLMUL_4 = 0x00080000
|
||||
RCC_CFGR_PLLMUL_5 = 0x000C0000
|
||||
RCC_CFGR_PLLMUL_6 = 0x00100000
|
||||
RCC_CFGR_PLLMUL_7 = 0x00140000
|
||||
RCC_CFGR_PLLMUL_8 = 0x00180000
|
||||
RCC_CFGR_PLLMUL_9 = 0x001C0000
|
||||
RCC_CFGR_PLLMUL_10 = 0x00200000
|
||||
RCC_CFGR_PLLMUL_11 = 0x00240000
|
||||
RCC_CFGR_PLLMUL_12 = 0x00280000
|
||||
RCC_CFGR_PLLMUL_13 = 0x002C0000
|
||||
RCC_CFGR_PLLMUL_14 = 0x00300000
|
||||
RCC_CFGR_PLLMUL_15 = 0x00340000
|
||||
RCC_CFGR_PLLMUL_16 = 0x00380000
|
||||
|
||||
// RTC clock source
|
||||
RCC_RTCCLKSource_LSE = 0x00000100
|
||||
RCC_RTCCLKSource_LSI = 0x00000200
|
||||
RCC_RTCCLKSource_HSE_Div128 = 0x00000300
|
||||
|
||||
// SPI settings
|
||||
SPI_FirstBit_MSB = 0x0000
|
||||
SPI_FirstBit_LSB = 0x0080
|
||||
|
||||
SPI_BaudRatePrescaler_2 = 0x0000
|
||||
SPI_BaudRatePrescaler_4 = 0x0008
|
||||
SPI_BaudRatePrescaler_8 = 0x0010
|
||||
SPI_BaudRatePrescaler_16 = 0x0018
|
||||
SPI_BaudRatePrescaler_32 = 0x0020
|
||||
SPI_BaudRatePrescaler_64 = 0x0028
|
||||
SPI_BaudRatePrescaler_128 = 0x0030
|
||||
SPI_BaudRatePrescaler_256 = 0x0038
|
||||
|
||||
SPI_Direction_2Lines_FullDuplex = 0x0000
|
||||
SPI_Direction_2Lines_RxOnly = 0x0400
|
||||
SPI_Direction_1Line_Rx = 0x8000
|
||||
SPI_Direction_1Line_Tx = 0xC000
|
||||
|
||||
SPI_Mode_Master = 0x0104
|
||||
SPI_Mode_Slave = 0x0000
|
||||
|
||||
SPI_NSS_Soft = 0x0200
|
||||
SPI_NSS_Hard = 0x0000
|
||||
|
||||
SPI_NSSInternalSoft_Set = 0x0100
|
||||
SPI_NSSInternalSoft_Reset = 0xFEFF
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// STM32FXXX (except stm32f1xx) bitfield definitions that are not
|
||||
// auto-generated by gen-device-svd.go
|
||||
// +build stm32f4
|
||||
|
||||
// Alternate function settings on the stm32f4 series
|
||||
|
||||
package stm32
|
||||
|
||||
const (
|
||||
// Alternative peripheral pin functions
|
||||
// AF0_SYSTEM is defined im the common bitfields package
|
||||
AF1_TIM1_2 AltFunc = 1
|
||||
AF2_TIM3_4_5 = 2
|
||||
AF3_TIM8_9_10_11 = 3
|
||||
AF4_I2C1_2_3 = 4
|
||||
AF5_SPI1_SPI2 = 5
|
||||
AF6_SPI3 = 6
|
||||
AF7_USART1_2_3 = 7
|
||||
AF8_USART4_5_6 = 8
|
||||
AF9_CAN1_CAN2_TIM12_13_14 = 9
|
||||
AF10_OTG_FS_OTG_HS = 10
|
||||
AF11_ETH = 11
|
||||
AF12_FSMC_SDIO_OTG_HS_1 = 12
|
||||
AF13_DCMI = 13
|
||||
AF14 = 14
|
||||
AF15_EVENTOUT = 15
|
||||
)
|
||||
@@ -45,6 +45,11 @@ func Pause() {
|
||||
currentTask.state.pause()
|
||||
}
|
||||
|
||||
//export tinygo_pause
|
||||
func pause() {
|
||||
Pause()
|
||||
}
|
||||
|
||||
// Resume the task until it pauses or completes.
|
||||
// This may only be called from the scheduler.
|
||||
func (t *Task) Resume() {
|
||||
@@ -58,10 +63,32 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||
// Create a stack.
|
||||
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
|
||||
|
||||
// Set up the stack canary, a random number that should be checked when
|
||||
// switching from the task back to the scheduler. The stack canary pointer
|
||||
// points to the first word of the stack. If it has changed between now and
|
||||
// the next stack switch, there was a stack overflow.
|
||||
s.canaryPtr = &stack[0]
|
||||
*s.canaryPtr = stackCanary
|
||||
|
||||
// Get a pointer to the top of the stack, where the initial register values
|
||||
// are stored. They will be popped off the stack on the first stack switch
|
||||
// to the goroutine, and will start running tinygo_startTask (this setup
|
||||
// happens in archInit).
|
||||
r := (*calleeSavedRegs)(unsafe.Pointer(&stack[uintptr(len(stack))-(unsafe.Sizeof(calleeSavedRegs{})/unsafe.Sizeof(uintptr(0)))]))
|
||||
|
||||
// Invoke architecture-specific initialization.
|
||||
s.archInit(stack, fn, args)
|
||||
s.archInit(r, fn, args)
|
||||
}
|
||||
|
||||
//export tinygo_swapTask
|
||||
func swapTask(oldStack uintptr, newStack *uintptr)
|
||||
|
||||
// startTask is a small wrapper function that sets up the first (and only)
|
||||
// argument to the new goroutine and makes sure it is exited when the goroutine
|
||||
// finishes.
|
||||
//go:extern tinygo_startTask
|
||||
var startTask [0]uint8
|
||||
|
||||
//go:linkname runqueuePushBack runtime.runqueuePushBack
|
||||
func runqueuePushBack(*Task)
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
.section .bss.tinygo_systemStack
|
||||
.global tinygo_systemStack
|
||||
.type tinygo_systemStack, %object
|
||||
tinygo_systemStack:
|
||||
.short 0
|
||||
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
// Most importantly, r2r3 contain the pc of the to-be-started function and
|
||||
// r4r5 contain the only argument it is given. Multiple arguments are packed
|
||||
// into one by storing them in a new allocation.
|
||||
|
||||
// Set the first argument of the goroutine start wrapper, which contains all
|
||||
// the arguments.
|
||||
movw r24, r4
|
||||
|
||||
// Branch to the "goroutine start" function. Note that the Z register is
|
||||
// call-clobbered, so does not need to be restored after use.
|
||||
movw Z, r2
|
||||
icall
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
#if __AVR_ARCH__ == 2 || __AVR_ARCH__ == 25
|
||||
// Small memory devices (≤8kB flash) that do not have the long call
|
||||
// instruction availble will need to use rcall instead.
|
||||
// Note that they will probably not be able to run more than the main
|
||||
// goroutine anyway, but this file is compiled for all AVRs so it needs to
|
||||
// compile at least.
|
||||
rcall tinygo_pause
|
||||
#else
|
||||
// Other devices can (and must) use the regular call instruction.
|
||||
call tinygo_pause
|
||||
#endif
|
||||
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
// This function gets the following parameters:
|
||||
// r24:r25 = newStack uintptr
|
||||
// r22:r23 = oldStack *uintptr
|
||||
|
||||
// Save all call-saved registers:
|
||||
// https://gcc.gnu.org/wiki/avr-gcc#Call-Saved_Registers
|
||||
push r29 // Y
|
||||
push r28 // Y
|
||||
push r17
|
||||
push r16
|
||||
push r15
|
||||
push r14
|
||||
push r13
|
||||
push r12
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push r7
|
||||
push r6
|
||||
push r5
|
||||
push r4
|
||||
push r3
|
||||
push r2
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
in r2, 0x3d; SPL
|
||||
in r3, 0x3e; SPH
|
||||
movw Y, r22
|
||||
std Y+0, r2
|
||||
std Y+1, r3
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
out 0x3d, r24; SPL
|
||||
out 0x3e, r25; SPH
|
||||
|
||||
// Load saved register from the new stack.
|
||||
pop r2
|
||||
pop r3
|
||||
pop r4
|
||||
pop r5
|
||||
pop r6
|
||||
pop r7
|
||||
pop r8
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
pop r12
|
||||
pop r13
|
||||
pop r14
|
||||
pop r15
|
||||
pop r16
|
||||
pop r17
|
||||
pop r28 // Y
|
||||
pop r29 // Y
|
||||
|
||||
// Return into the new task, as if tinygo_swapTask was a regular call.
|
||||
ret
|
||||
@@ -4,9 +4,12 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:extern tinygo_systemStack
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see scheduler_avr.S that relies on the
|
||||
// exact layout of this struct.
|
||||
// switching between tasks. Also see task_stack_avr.S that relies on the exact
|
||||
// layout of this struct.
|
||||
//
|
||||
// https://gcc.gnu.org/wiki/avr-gcc#Call-Saved_Registers
|
||||
type calleeSavedRegs struct {
|
||||
@@ -23,34 +26,15 @@ type calleeSavedRegs struct {
|
||||
pc uintptr
|
||||
}
|
||||
|
||||
// registers gets a pointer to the registers stored at the top of the stack.
|
||||
func (s *state) registers() *calleeSavedRegs {
|
||||
return (*calleeSavedRegs)(unsafe.Pointer(s.sp + 1))
|
||||
}
|
||||
|
||||
// startTask is a small wrapper function that sets up the first (and only)
|
||||
// argument to the new goroutine and makes sure it is exited when the goroutine
|
||||
// finishes.
|
||||
//go:extern tinygo_startTask
|
||||
var startTask [0]uint8
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
// Note: adding //go:noinline to work around an AVR backend bug.
|
||||
//go:noinline
|
||||
func (s *state) archInit(stack []uintptr, fn uintptr, args unsafe.Pointer) {
|
||||
// Set up the stack canary, a random number that should be checked when
|
||||
// switching from the task back to the scheduler. The stack canary pointer
|
||||
// points to the first word of the stack. If it has changed between now and
|
||||
// the next stack switch, there was a stack overflow.
|
||||
s.canaryPtr = &stack[0]
|
||||
*s.canaryPtr = stackCanary
|
||||
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the initial sp for the startTask function (implemented in assembly).
|
||||
s.sp = uintptr(unsafe.Pointer(&stack[uintptr(len(stack))-(unsafe.Sizeof(calleeSavedRegs{})/unsafe.Sizeof(uintptr(0)))])) - 1
|
||||
s.sp = uintptr(unsafe.Pointer(r)) - 1
|
||||
|
||||
// Initialize the registers.
|
||||
// These will be popped off of the stack on the first resume of the goroutine.
|
||||
r := s.registers()
|
||||
|
||||
// Start the function at tinygo_startTask.
|
||||
startTask := uintptr(unsafe.Pointer(&startTask))
|
||||
@@ -67,20 +51,17 @@ func (s *state) archInit(stack []uintptr, fn uintptr, args unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
switchToTask(s.sp)
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
//export tinygo_switchToTask
|
||||
func switchToTask(uintptr)
|
||||
|
||||
//export tinygo_switchToScheduler
|
||||
func switchToScheduler(*uintptr)
|
||||
|
||||
func (s *state) pause() {
|
||||
switchToScheduler(&s.sp)
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
}
|
||||
|
||||
//export tinygo_pause
|
||||
func pause() {
|
||||
Pause()
|
||||
// SystemStack returns the system stack pointer when called from a task stack.
|
||||
// When called from the system stack, it returns 0.
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
|
||||
@@ -30,17 +30,6 @@ tinygo_startTask:
|
||||
.cfi_endproc
|
||||
.size tinygo_startTask, .-tinygo_startTask
|
||||
|
||||
.section .text.tinygo_getSystemStackPointer
|
||||
.global tinygo_getSystemStackPointer
|
||||
.type tinygo_getSystemStackPointer, %function
|
||||
tinygo_getSystemStackPointer:
|
||||
.cfi_startproc
|
||||
// The system stack pointer is always stored in the MSP register.
|
||||
mrs r0, MSP
|
||||
bx lr
|
||||
.cfi_endproc
|
||||
.size tinygo_getSystemStackPointer, .-tinygo_getSystemStackPointer
|
||||
|
||||
.section .text.tinygo_switchToScheduler
|
||||
.global tinygo_switchToScheduler
|
||||
.type tinygo_switchToScheduler, %function
|
||||
@@ -136,34 +125,3 @@ tinygo_swapTask:
|
||||
#endif
|
||||
.cfi_endproc
|
||||
.size tinygo_swapTask, .-tinygo_swapTask
|
||||
|
||||
.section .text.tinygo_scanCurrentStack
|
||||
.global tinygo_scanCurrentStack
|
||||
.type tinygo_scanCurrentStack, %function
|
||||
tinygo_scanCurrentStack:
|
||||
.cfi_startproc
|
||||
// Save callee-saved registers onto the stack.
|
||||
#if defined(__thumb2__)
|
||||
push {r4-r11, lr}
|
||||
.cfi_def_cfa_offset 9*4
|
||||
#else
|
||||
mov r0, r8
|
||||
mov r1, r9
|
||||
mov r2, r10
|
||||
mov r3, r11
|
||||
push {r0-r3, lr}
|
||||
.cfi_def_cfa_offset 5*4
|
||||
push {r4-r7}
|
||||
.cfi_def_cfa_offset 4*4
|
||||
#endif
|
||||
|
||||
// Scan the stack.
|
||||
mov r0, sp
|
||||
bl tinygo_scanstack
|
||||
|
||||
// Restore stack state and return.
|
||||
add sp, #32
|
||||
.cfi_def_cfa_offset 1*4
|
||||
pop {pc}
|
||||
.cfi_endproc
|
||||
.size tinygo_scanCurrentStack, .-tinygo_scanCurrentStack
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
package task
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"device/arm"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see scheduler_cortexm.S that relies on the
|
||||
// switching between tasks. Also see task_stack_cortexm.S that relies on the
|
||||
// exact layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
r4 uintptr
|
||||
@@ -20,34 +23,15 @@ type calleeSavedRegs struct {
|
||||
pc uintptr
|
||||
}
|
||||
|
||||
// registers gets a pointer to the registers stored at the top of the stack.
|
||||
func (s *state) registers() *calleeSavedRegs {
|
||||
return (*calleeSavedRegs)(unsafe.Pointer(s.sp))
|
||||
}
|
||||
|
||||
// startTask is a small wrapper function that sets up the first (and only)
|
||||
// argument to the new goroutine and makes sure it is exited when the goroutine
|
||||
// finishes.
|
||||
//go:extern tinygo_startTask
|
||||
var startTask [0]uint8
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
func (s *state) archInit(stack []uintptr, fn uintptr, args unsafe.Pointer) {
|
||||
// Set up the stack canary, a random number that should be checked when
|
||||
// switching from the task back to the scheduler. The stack canary pointer
|
||||
// points to the first word of the stack. If it has changed between now and
|
||||
// the next stack switch, there was a stack overflow.
|
||||
s.canaryPtr = &stack[0]
|
||||
*s.canaryPtr = stackCanary
|
||||
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the initial sp for the startTask function (implemented in assembly).
|
||||
s.sp = uintptr(unsafe.Pointer(&stack[uintptr(len(stack))-(unsafe.Sizeof(calleeSavedRegs{})/unsafe.Sizeof(uintptr(0)))]))
|
||||
s.sp = uintptr(unsafe.Pointer(r))
|
||||
|
||||
// Initialize the registers.
|
||||
// These will be popped off of the stack on the first resume of the goroutine.
|
||||
r := s.registers()
|
||||
|
||||
// Start the function at tinygo_startTask (defined in src/runtime/scheduler_cortexm.S).
|
||||
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_cortexm.S).
|
||||
// This assembly code calls a function (passed in r4) with a single argument (passed in r5).
|
||||
// After the function returns, it calls Pause().
|
||||
r.pc = uintptr(unsafe.Pointer(&startTask))
|
||||
@@ -75,7 +59,8 @@ func (s *state) pause() {
|
||||
switchToScheduler(&s.sp)
|
||||
}
|
||||
|
||||
//export tinygo_pause
|
||||
func pause() {
|
||||
Pause()
|
||||
// SystemStack returns the system stack pointer. On Cortex-M, it is always
|
||||
// available.
|
||||
func SystemStack() uintptr {
|
||||
return arm.AsmFull("mrs {}, MSP", nil)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
.section .text.tinygo_startTask,"ax",@progbits
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
// Small assembly stub for starting a goroutine. This already runs on the
|
||||
// new stack, control reaches this function after returning from the initial
|
||||
// tinygo_swapTask below (the retw.n instruction).
|
||||
//
|
||||
// The stack was set up in such a way that it looks as if this function was
|
||||
// paused using tinygo_swapTask by setting up the parent register window and
|
||||
// return pointer as a call4 instruction - except such a call never took
|
||||
// place. Instead, the stack pointer is switched to the new stack after all
|
||||
// live-but-invisible registers have been flushed to the stack. This means
|
||||
// that all registers as present in tinygo_swapTask are moved four up (a2 in
|
||||
// tinygo_swapTask is a6 in this function). We don't use any of those
|
||||
// registers however. Instead, the retw.n instruction will load them through
|
||||
// an underflow exception from the stack which means we get a0-a3 as defined
|
||||
// in task_stack_esp32.go.
|
||||
|
||||
// Branch to the "goroutine start" function. The first (and only) parameter
|
||||
// is stored in a2, but has to be moved to a6 to make it appear as a2 in the
|
||||
// goroutine start function (due to changing the register window by four
|
||||
// with callx4).
|
||||
mov.n a6, a2
|
||||
callx4 a3
|
||||
|
||||
// After return, exit this goroutine. This call never returns.
|
||||
call4 tinygo_pause
|
||||
|
||||
.section .text.tinygo_swapTask,"ax",@progbits
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
// This function gets the following parameters:
|
||||
// a2 = newStack uintptr
|
||||
// a3 = oldStack *uintptr
|
||||
|
||||
// Reserve 32 bytes on the stack. It really needs to be 32 bytes, with 16
|
||||
// extra at the bottom to adhere to the ABI.
|
||||
entry sp, 32
|
||||
|
||||
// Disable interrupts while flushing registers. This is necessary because
|
||||
// interrupts might want to use the stack pointer (at a2) which will be some
|
||||
// arbitrary register while registers are flushed.
|
||||
rsil a4, 3 // XCHAL_EXCM_LEVEL
|
||||
|
||||
// Flush all unsaved registers to the stack.
|
||||
// This trick has been borrowed from the Zephyr project:
|
||||
// https://github.com/zephyrproject-rtos/zephyr/blob/d79b003758/arch/xtensa/include/xtensa-asm2-s.h#L17
|
||||
and a12, a12, a12
|
||||
rotw 3
|
||||
and a12, a12, a12
|
||||
rotw 3
|
||||
and a12, a12, a12
|
||||
rotw 3
|
||||
and a12, a12, a12
|
||||
rotw 3
|
||||
and a12, a12, a12
|
||||
rotw 4
|
||||
|
||||
// Restore interrupts.
|
||||
wsr.ps a4
|
||||
|
||||
// At this point, the following is true:
|
||||
// WindowStart == 1 << WindowBase
|
||||
// Therefore, we don't need to do this manually.
|
||||
// It also means that the stack pointer can now be safely modified.
|
||||
|
||||
// Save a0, which stores the return address and the parent register window
|
||||
// in the upper two bits.
|
||||
s32i.n a0, sp, 0
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
s32i.n sp, a3, 0
|
||||
|
||||
// Switch to the new stack pointer (newStack).
|
||||
mov.n sp, a2
|
||||
|
||||
// Load a0, which is the previous return addres from before the previous
|
||||
// switch or the constructed return address to tinygo_startTask. This
|
||||
// register also stores the parent register window.
|
||||
l32i.n a0, sp, 0
|
||||
|
||||
// Return into the new stack. This instruction will trigger a window
|
||||
// underflow, reloading the saved registers from the stack.
|
||||
retw.n
|
||||
@@ -0,0 +1,76 @@
|
||||
// +build scheduler.tasks,esp32
|
||||
|
||||
package task
|
||||
|
||||
// The windowed ABI (used on the ESP32) is as follows:
|
||||
// a0: return address (link register)
|
||||
// a1: stack pointer (must be 16-byte aligned)
|
||||
// a2-a7: incoming arguments
|
||||
// a7: stack frame pointer (optional, normally unused in TinyGo)
|
||||
// Sources:
|
||||
// http://cholla.mmto.org/esp8266/xtensa.html
|
||||
// https://0x04.net/~mwk/doc/xtensa.pdf
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_esp8266.S that relies on the
|
||||
// exact layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
// Registers in the register window of tinygo_startTask.
|
||||
a0 uintptr
|
||||
a1 uintptr
|
||||
a2 uintptr
|
||||
a3 uintptr
|
||||
|
||||
// Locals that can be used by tinygo_swapTask.
|
||||
// The first field is the a0 loaded in tinygo_swapTask, the rest is unused.
|
||||
locals [4]uintptr
|
||||
}
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the stack pointer for the tinygo_swapTask function (implemented in
|
||||
// assembly). It needs to point to the locals field instead of a0 so that
|
||||
// the retw.n at the end of tinygo_swapTask will return into
|
||||
// tinygo_startTask with a0-a3 loaded (using the register window mechanism).
|
||||
s.sp = uintptr(unsafe.Pointer(&r.locals[0]))
|
||||
|
||||
// Start the goroutine at tinygo_startTask (defined in
|
||||
// src/internal/task/task_stack_esp32.S). The topmost two bits are not part
|
||||
// of the address but instead store the register window of the caller.
|
||||
// In this case there is no caller, instead we set up the return address as
|
||||
// if tinygo_startTask called tinygo_swapTask with a call4 instruction.
|
||||
r.locals[0] = uintptr(unsafe.Pointer(&startTask))&^(3<<30) | (1 << 30)
|
||||
|
||||
// Set up the stack pointer inside tinygo_startTask.
|
||||
// Unlike most calling conventions, the windowed ABI actually saves the
|
||||
// stack pointer on the stack to make register windowing work.
|
||||
r.a1 = uintptr(unsafe.Pointer(r)) + 32
|
||||
|
||||
// Store the function pointer and the (only) parameter on the stack in a
|
||||
// location that will be reloaded into registers when doing the
|
||||
// pseudo-return to tinygo_startTask using the register window mechanism.
|
||||
r.a3 = fn
|
||||
r.a2 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
}
|
||||
|
||||
// SystemStack returns the system stack pointer when called from a task stack.
|
||||
// When called from the system stack, it returns 0.
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
.section .text.tinygo_startTask,"ax",@progbits
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
// Most importantly, r4 contains the pc of the to-be-started function and r5
|
||||
// contains the only argument it is given. Multiple arguments are packed
|
||||
// into one by storing them in a new allocation.
|
||||
|
||||
// Set the first argument of the goroutine start wrapper, which contains all
|
||||
// the arguments.
|
||||
mov.n a2, a13
|
||||
|
||||
// Branch to the "goroutine start" function.
|
||||
callx0 a12
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
call0 tinygo_pause
|
||||
.size tinygo_startTask, .-tinygo_startTask
|
||||
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
// This function gets the following parameters:
|
||||
// a2 = newStack uintptr
|
||||
// a3 = oldStack *uintptr
|
||||
// Note:
|
||||
// a0 is the return address
|
||||
// a1 is the stack pointer (sp)
|
||||
|
||||
// Save all callee-saved registers:
|
||||
addi sp, sp, -20
|
||||
s32i.n a12, sp, 0
|
||||
s32i.n a13, sp, 4
|
||||
s32i.n a14, sp, 8
|
||||
s32i.n a15, sp, 12
|
||||
s32i.n a0, sp, 16
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
s32i.n sp, a3, 0
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
mov.n sp, a2
|
||||
|
||||
// Load state from new task and branch to the previous position in the
|
||||
// program.
|
||||
l32i.n a12, sp, 0
|
||||
l32i.n a13, sp, 4
|
||||
l32i.n a14, sp, 8
|
||||
l32i.n a15, sp, 12
|
||||
l32i.n a0, sp, 16
|
||||
addi sp, sp, 20
|
||||
ret.n
|
||||
@@ -0,0 +1,70 @@
|
||||
// +build scheduler.tasks,esp8266
|
||||
|
||||
package task
|
||||
|
||||
// Stack switch implementation for the ESP8266, which does not use the windowed
|
||||
// ABI of Xtensa. Registers are assigned as follows:
|
||||
// a0: return address (link register)
|
||||
// a1: stack pointer (must be 16-byte aligned)
|
||||
// a2-a7: incoming arguments
|
||||
// a8: static chain (unused)
|
||||
// a12-a15: callee-saved
|
||||
// a15: stack frame pointer (optional, unused)
|
||||
// Sources:
|
||||
// http://cholla.mmto.org/esp8266/xtensa.html
|
||||
// https://0x04.net/~mwk/doc/xtensa.pdf
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_esp8266.S that relies on the
|
||||
// exact layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
a12 uintptr
|
||||
a13 uintptr
|
||||
a14 uintptr
|
||||
a15 uintptr
|
||||
|
||||
pc uintptr // also link register or r0
|
||||
}
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the initial sp for the startTask function (implemented in assembly).
|
||||
s.sp = uintptr(unsafe.Pointer(r))
|
||||
|
||||
// Initialize the registers.
|
||||
// These will be popped off of the stack on the first resume of the goroutine.
|
||||
|
||||
// Start the function at tinygo_startTask (defined in
|
||||
// src/internal/task/task_stack_esp8266.S).
|
||||
// This assembly code calls a function (passed in a12) with a single argument
|
||||
// (passed in a13). After the function returns, it calls Pause().
|
||||
r.pc = uintptr(unsafe.Pointer(&startTask))
|
||||
|
||||
// Pass the function to call in a12.
|
||||
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
|
||||
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
|
||||
r.a12 = fn
|
||||
|
||||
// Pass the pointer to the arguments struct in a13.
|
||||
r.a13 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
}
|
||||
|
||||
// SystemStack returns the system stack pointer when called from a task stack.
|
||||
// When called from the system stack, it returns 0.
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// +build arduino_mkr1000
|
||||
|
||||
// This contains the pin mappings for the Arduino MKR1000 board.
|
||||
//
|
||||
// For more information, see: https://store.arduino.cc/usa/arduino-mkr1000-with-headers-mounted
|
||||
//
|
||||
package machine
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0x07738135
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
RX0 Pin = PB23 // UART2 RX
|
||||
TX1 Pin = PB22 // UART2 TX
|
||||
|
||||
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 // SDA
|
||||
D12 Pin = PA09 // PWM available, SCL
|
||||
D13 Pin = PB23 // RX
|
||||
D14 Pin = PB22 // TX
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 Pin = PA02 // ADC0/AIN[0]
|
||||
A1 Pin = PB02 // AIN[10]
|
||||
A2 Pin = PB03 // AIN[11]
|
||||
A3 Pin = PA04 // AIN[04]
|
||||
A4 Pin = PA05 // AIN[05]
|
||||
A5 Pin = PA06 // AIN[06]
|
||||
A6 Pin = PA07 // AIN[07]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D6
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN Pin = PA24
|
||||
USBCDC_DP_PIN Pin = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN Pin = PB22
|
||||
UART_RX_PIN Pin = PB23
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = D11 // SDA
|
||||
SCL_PIN Pin = D12 // SCL
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = D9 // SCK: S1
|
||||
SPI0_SDO_PIN Pin = D8 // SDO: S1
|
||||
SPI0_SDI_PIN Pin = D10 // SDI: S1
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN Pin = PA10
|
||||
I2S_SD_PIN Pin = PA07
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33.
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Arduino MKR1000"
|
||||
usb_STRING_MANUFACTURER = "Arduino"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2341
|
||||
usb_PID uint16 = 0x804e
|
||||
)
|
||||
@@ -58,7 +58,7 @@ const (
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
UART0 = &USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -105,7 +105,7 @@ const (
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
UART0 = &USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// +build esp32_coreboard_v2
|
||||
|
||||
package machine
|
||||
|
||||
// Built-in LED on some ESP32 boards.
|
||||
const LED = IO2
|
||||
|
||||
const (
|
||||
CLK Pin = 6
|
||||
CMD Pin = 11
|
||||
IO0 Pin = 0
|
||||
IO1 Pin = 1
|
||||
IO10 Pin = 10
|
||||
IO16 Pin = 16
|
||||
IO17 Pin = 17
|
||||
IO18 Pin = 18
|
||||
IO19 Pin = 19
|
||||
IO2 Pin = 2
|
||||
IO21 Pin = 21
|
||||
IO22 Pin = 22
|
||||
IO23 Pin = 23
|
||||
IO25 Pin = 25
|
||||
IO26 Pin = 26
|
||||
IO27 Pin = 27
|
||||
IO3 Pin = 3
|
||||
IO32 Pin = 32
|
||||
IO33 Pin = 33
|
||||
IO34 Pin = 34
|
||||
IO35 Pin = 35
|
||||
IO36 Pin = 36
|
||||
IO39 Pin = 39
|
||||
IO4 Pin = 4
|
||||
IO5 Pin = 5
|
||||
IO9 Pin = 9
|
||||
RXD Pin = 3
|
||||
SD0 Pin = 7
|
||||
SD1 Pin = 8
|
||||
SD2 Pin = 9
|
||||
SD3 Pin = 10
|
||||
SVN Pin = 39
|
||||
SVP Pin = 36
|
||||
TCK Pin = 13
|
||||
TD0 Pin = 15
|
||||
TDI Pin = 12
|
||||
TMS Pin = 14
|
||||
TXD Pin = 1
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = IO18
|
||||
SPI0_SDO_PIN = IO23
|
||||
SPI0_SDI_PIN = IO19
|
||||
SPI0_CS0_PIN = IO5
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = IO21
|
||||
SCL_PIN = IO22
|
||||
)
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC0 Pin = IO34
|
||||
ADC1 Pin = IO35
|
||||
ADC2 Pin = IO36
|
||||
ADC3 Pin = IO39
|
||||
)
|
||||
|
||||
// UART0 pins
|
||||
const (
|
||||
UART_TX_PIN = IO1
|
||||
UART_RX_PIN = IO3
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART1_TX_PIN = IO9
|
||||
UART1_RX_PIN = IO10
|
||||
)
|
||||
|
||||
// PWM pins
|
||||
const (
|
||||
PWM0_PIN Pin = IO2
|
||||
PWM1_PIN Pin = IO0
|
||||
PWM2_PIN Pin = IO4
|
||||
)
|
||||
@@ -1,6 +0,0 @@
|
||||
// +build esp32_wroom_32
|
||||
|
||||
package machine
|
||||
|
||||
// Blue LED on the ESP32-WROOM-32 module.
|
||||
const LED = Pin(2)
|
||||
@@ -77,7 +77,7 @@ const (
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
UART0 = &USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -122,17 +122,17 @@ var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART3,
|
||||
AltFuncSelector: stm32.AF7_USART1_2_3,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART6,
|
||||
AltFuncSelector: stm32.AF8_USART4_5_6,
|
||||
AltFuncSelector: AF8_USART4_5_6,
|
||||
}
|
||||
UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
AltFuncSelector: stm32.AF7_USART1_2_3,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART0 = UART1
|
||||
)
|
||||
@@ -178,6 +178,22 @@ const (
|
||||
SPI_SDO_PIN = SPI0_SDO_PIN //
|
||||
)
|
||||
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: stm32.SPI2,
|
||||
AltFuncSelector: AF5_SPI1_SPI2,
|
||||
}
|
||||
SPI2 = SPI{
|
||||
Bus: stm32.SPI3,
|
||||
AltFuncSelector: AF6_SPI3,
|
||||
}
|
||||
SPI3 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: AF5_SPI1_SPI2,
|
||||
}
|
||||
SPI0 = SPI1
|
||||
)
|
||||
|
||||
func initSPI() {}
|
||||
|
||||
// -- I2C ----------------------------------------------------------------------
|
||||
@@ -186,11 +202,11 @@ const (
|
||||
// #===========#==========#==============#==============#=======#=======#
|
||||
// | Interface | Hardware | Bus(Freq) | SDA/SCL Pins | AltFn | Alias |
|
||||
// #===========#==========#==============#==============#=======#=======#
|
||||
// | I2C1 | I2C1 | | D14/D15 | | ~ |
|
||||
// | I2C2 | I2C2 | | D0/D1 | | ~ |
|
||||
// | I2C3 | I2C1 | | D9/D10 | | ~ |
|
||||
// | I2C1 | I2C1 | APB1(42 MHz) | D14/D15 | 4 | ~ |
|
||||
// | I2C2 | I2C2 | APB1(42 MHz) | D0/D1 | 4 | ~ |
|
||||
// | I2C3 | I2C1 | APB1(42 MHz) | D9/D10 | 4 | ~ |
|
||||
// | --------- | -------- | ------------ | ------------ | ----- | ----- |
|
||||
// | I2C0 | I2C1 | | D14/D15 | | I2C1 |
|
||||
// | I2C0 | I2C1 | APB1(42 MHz) | D14/D15 | 4 | I2C1 |
|
||||
// #===========#==========#==============#==============#=======#=======#
|
||||
NUM_I2C_INTERFACES = 3
|
||||
|
||||
@@ -210,4 +226,20 @@ const (
|
||||
I2C_SCL_PIN = I2C0_SCL_PIN //
|
||||
)
|
||||
|
||||
var (
|
||||
I2C1 = I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
I2C2 = I2C{
|
||||
Bus: stm32.I2C2,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
I2C3 = I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
func initI2C() {}
|
||||
|
||||
@@ -72,7 +72,7 @@ const (
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
UART0 = &USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// +build lgt92
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
LED1 = PA12
|
||||
LED2 = PA8
|
||||
LED3 = PA11
|
||||
|
||||
LED_RED = LED1
|
||||
LED_BLUE = LED2
|
||||
LED_GREEN = LED3
|
||||
|
||||
// Default led
|
||||
LED = LED1
|
||||
|
||||
BUTTON = PB14
|
||||
|
||||
// LG GPS module
|
||||
GPS_STANDBY_PIN = PB3
|
||||
GPS_RESET_PIN = PB4
|
||||
GPS_POWER_PIN = PB5
|
||||
|
||||
MEMS_ACCEL_CS = PE3
|
||||
MEMS_ACCEL_INT1 = PE0
|
||||
MEMS_ACCEL_INT2 = PE1
|
||||
|
||||
// SPI
|
||||
SPI1_SCK_PIN = PA5
|
||||
SPI1_SDI_PIN = PA6
|
||||
SPI1_SDO_PIN = PA7
|
||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
||||
|
||||
// LORA RFM95 Radio
|
||||
RFM95_DIO0_PIN = PC13
|
||||
|
||||
//TinyGo UART is MCU LPUSART1
|
||||
UART_RX_PIN = PA13
|
||||
UART_TX_PIN = PA14
|
||||
|
||||
//TinyGo UART1 is MCU USART1
|
||||
UART1_RX_PIN = PB6
|
||||
UART1_TX_PIN = PB7
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// Console UART (LPUSART1)
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.LPUART1,
|
||||
AltFuncSelector: 6,
|
||||
}
|
||||
|
||||
// Gps UART
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
AltFuncSelector: 0,
|
||||
}
|
||||
|
||||
// SPI
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
}
|
||||
SPI1 = &SPI0
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Enable UARTs Interrupts
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_AES_RNG_LPUART1, UART0.handleInterrupt)
|
||||
UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, UART1.handleInterrupt)
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
// +build matrixportal_m4
|
||||
|
||||
package machine
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xF01669EF
|
||||
|
||||
// Digital pins
|
||||
const (
|
||||
// Pin // Function SERCOM PWM Interrupt
|
||||
// ---- // ---------------- ------ --- ---------
|
||||
D0 = PA01 // UART RX 1[1] PWM EXTI1
|
||||
D1 = PA00 // UART TX 1[0] PWM EXTI0
|
||||
D2 = PB22 // Button "Up" EXTI6
|
||||
D3 = PB23 // Button "Down" EXTI7
|
||||
D4 = PA23 // NeoPixel EXTI7
|
||||
D5 = PB31 // I2C SDA 5[1] EXTI15
|
||||
D6 = PB30 // I2C SCL 5[0] EXTI14
|
||||
D7 = PB00 // HUB75 R1 EXTI0
|
||||
D8 = PB01 // HUB75 G1 EXTI1
|
||||
D9 = PB02 // HUB75 B1 EXTI2
|
||||
D10 = PB03 // HUB75 R2 EXTI3
|
||||
D11 = PB04 // HUB75 G2 EXTI4
|
||||
D12 = PB05 // HUB75 B2 EXTI5
|
||||
D13 = PA14 // LED PWM EXTI14
|
||||
D14 = PB06 // HUB75 CLK EXTI6
|
||||
D15 = PB14 // HUB75 LAT EXTI14
|
||||
D16 = PB12 // HUB75 OE EXTI12
|
||||
D17 = PB07 // HUB75 ADDR A EXTI7
|
||||
D18 = PB08 // HUB75 ADDR B EXTI8
|
||||
D19 = PB09 // HUB75 ADDR C EXTI9
|
||||
D20 = PB15 // HUB75 ADDR D EXTI15
|
||||
D21 = PB13 // HUB75 ADDR E EXTI13
|
||||
D22 = PA02 // ADC (A0) EXTI2
|
||||
D23 = PA05 // ADC (A1) EXTI5
|
||||
D24 = PA04 // ADC (A2) PWM EXTI4
|
||||
D25 = PA06 // ADC (A3) PWM EXTI6
|
||||
D26 = PA07 // ADC (A4) EXTI7
|
||||
D27 = PA12 // ESP32 UART RX 4[1] PWM EXTI12
|
||||
D28 = PA13 // ESP32 UART TX 4[0] PWM EXTI13
|
||||
D29 = PA20 // ESP32 GPIO0 PWM EXTI4
|
||||
D30 = PA21 // ESP32 Reset PWM EXTI5
|
||||
D31 = PA22 // ESP32 Busy PWM EXTI6
|
||||
D32 = PA18 // ESP32 RTS PWM EXTI2
|
||||
D33 = PB17 // ESP32 SPI CS PWM EXTI1
|
||||
D34 = PA16 // ESP32 SPI SCK 3[1] PWM EXTI0
|
||||
D35 = PA17 // ESP32 SPI SDI 3[0] PWM EXTI1
|
||||
D36 = PA19 // ESP32 SPI SDO 1[3] PWM EXTI3
|
||||
D37 = NoPin // USB Host enable
|
||||
D38 = PA24 // USB DM
|
||||
D39 = PA27 // USB DP
|
||||
D40 = PA03 // DAC/VREFP
|
||||
D41 = PB10 // Flash QSPI SCK
|
||||
D42 = PB11 // Flash QSPI CS
|
||||
D43 = PA08 // Flash QSPI I00
|
||||
D44 = PA09 // Flash QSPI IO1
|
||||
D45 = PA10 // Flash QSPI IO2
|
||||
D46 = PA11 // Flash QSPI IO3
|
||||
D47 = PA27 // LIS3DH IRQ EXTI11
|
||||
D48 = PA05 // SPI SCK 0[1] EXTI5
|
||||
D49 = PA04 // SPI SDO 0[0] PWM EXTI4
|
||||
D50 = PA07 // SPI SDI 0[3] EXTI7
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = PA02 // ADC Channel 0
|
||||
A1 = PA05 // ADC Channel 5
|
||||
A2 = PA04 // ADC Channel 4
|
||||
A3 = PA06 // ADC Channel 6
|
||||
A4 = PA07 // ADC Channel 7
|
||||
)
|
||||
|
||||
// LED pins
|
||||
const (
|
||||
LED = D13
|
||||
NEOPIXEL = D4
|
||||
)
|
||||
|
||||
// Button pins
|
||||
const (
|
||||
BUTTON_UP = D2
|
||||
BUTTON_DOWN = D3
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART1_RX_PIN = D0 // SERCOM1[1]
|
||||
UART1_TX_PIN = D1 // SERCOM1[0]
|
||||
|
||||
UART2_RX_PIN = D27 // SERCOM4[1] (ESP32 RX)
|
||||
UART2_TX_PIN = D28 // SERCOM4[0] (ESP32 TX)
|
||||
|
||||
UART_RX_PIN = UART1_RX_PIN
|
||||
UART_TX_PIN = UART1_TX_PIN
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D34 // SERCOM3[1] (ESP32 SCK)
|
||||
SPI0_SDO_PIN = D36 // SERCOM1[3] (ESP32 SDO)
|
||||
SPI0_SDI_PIN = D35 // SERCOM3[0] (ESP32 SDI)
|
||||
|
||||
SPI1_SCK_PIN = D48 // SERCOM0[1]
|
||||
SPI1_SDO_PIN = D49 // SERCOM0[0]
|
||||
SPI1_SDI_PIN = D50 // SERCOM0[3]
|
||||
|
||||
SPI_SCK_PIN = SPI0_SCK_PIN
|
||||
SPI_SDO_PIN = SPI0_SDO_PIN
|
||||
SPI_SDI_PIN = SPI0_SDI_PIN
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
I2C0_SDA_PIN = D5 // SERCOM5[1]
|
||||
I2C0_SCL_PIN = D6 // SERCOM5[0]
|
||||
|
||||
I2C_SDA_PIN = I2C0_SDA_PIN
|
||||
I2C_SCL_PIN = I2C0_SCL_PIN
|
||||
|
||||
SDA_PIN = I2C_SDA_PIN // awkward naming required by machine_atsamd51.go
|
||||
SCL_PIN = I2C_SCL_PIN //
|
||||
)
|
||||
|
||||
// ESP32 pins
|
||||
const (
|
||||
NINA_ACK = D31
|
||||
NINA_GPIO0 = D29
|
||||
NINA_RESETN = D30
|
||||
|
||||
NINA_RX = UART2_RX_PIN
|
||||
NINA_TX = UART2_TX_PIN
|
||||
NINA_RTS = D32
|
||||
|
||||
NINA_CS = D33
|
||||
NINA_SDO = SPI0_SDO_PIN
|
||||
NINA_SDI = SPI0_SDI_PIN
|
||||
NINA_SCK = SPI0_SCK_PIN
|
||||
)
|
||||
|
||||
// HUB75 pins
|
||||
const (
|
||||
HUB75_R1 = D7
|
||||
HUB75_G1 = D8
|
||||
HUB75_B1 = D9
|
||||
HUB75_R2 = D10
|
||||
HUB75_G2 = D11
|
||||
HUB75_B2 = D12
|
||||
|
||||
HUB75_CLK = D14
|
||||
HUB75_LAT = D15
|
||||
HUB75_OE = D16
|
||||
HUB75_ADDR_A = D17
|
||||
HUB75_ADDR_B = D18
|
||||
HUB75_ADDR_C = D19
|
||||
HUB75_ADDR_D = D20
|
||||
HUB75_ADDR_E = D21
|
||||
)
|
||||
|
||||
// USB CDC pins (UART0)
|
||||
const (
|
||||
USBCDC_DM_PIN = D38
|
||||
USBCDC_DP_PIN = D39
|
||||
|
||||
UART0_RX_PIN = USBCDC_DM_PIN
|
||||
UART0_TX_PIN = USBCDC_DP_PIN
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Matrix Portal M4"
|
||||
usb_STRING_MANUFACTURER = "Adafruit Industries"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x80C9
|
||||
)
|
||||
@@ -0,0 +1,50 @@
|
||||
// +build sam,atsamd51,matrixportal_m4
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART on the MatrixPortal M4
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM1_USART_INT,
|
||||
SERCOM: 1,
|
||||
}
|
||||
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART_INT,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1_1, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_1, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the MatrixPortal M4
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the MatrixPortal M4
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM3_SPIM,
|
||||
SERCOM: 3, // BUG: SDO on SERCOM1!
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM0_SPIM,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,114 @@
|
||||
// +build microbit_v2
|
||||
|
||||
package machine
|
||||
|
||||
// The micro:bit does not have a 32kHz crystal on board.
|
||||
const HasLowFrequencyCrystal = false
|
||||
|
||||
const (
|
||||
LED = P13
|
||||
LED1 = LED
|
||||
)
|
||||
|
||||
// Buttons on the micro:bit v2 (A and B)
|
||||
const (
|
||||
BUTTON Pin = BUTTONA
|
||||
BUTTONA Pin = P5
|
||||
BUTTONB Pin = P11
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = P34
|
||||
UART_RX_PIN Pin = P33
|
||||
)
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC0 Pin = P0
|
||||
ADC1 Pin = P1
|
||||
ADC2 Pin = P2
|
||||
)
|
||||
|
||||
// I2C0 (internal) pins
|
||||
const (
|
||||
SDA_PIN Pin = SDA0_PIN
|
||||
SCL_PIN Pin = SCL0_PIN
|
||||
SDA0_PIN Pin = P30
|
||||
SCL0_PIN Pin = P31
|
||||
)
|
||||
|
||||
// I2C1 (external) pins
|
||||
const (
|
||||
SDA1_PIN Pin = P20
|
||||
SCL1_PIN Pin = P19
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = P13
|
||||
SPI0_SDO_PIN Pin = P15
|
||||
SPI0_SDI_PIN Pin = P14
|
||||
)
|
||||
|
||||
// GPIO/Analog pins
|
||||
const (
|
||||
P0 Pin = 2
|
||||
P1 Pin = 3
|
||||
P2 Pin = 4
|
||||
P3 Pin = 31
|
||||
P4 Pin = 28
|
||||
P5 Pin = 14
|
||||
P6 Pin = 37
|
||||
P7 Pin = 11
|
||||
P8 Pin = 10
|
||||
P9 Pin = 9
|
||||
P10 Pin = 30
|
||||
P11 Pin = 23
|
||||
P12 Pin = 12
|
||||
P13 Pin = 17
|
||||
P14 Pin = 1
|
||||
P15 Pin = 13
|
||||
P16 Pin = 34
|
||||
P19 Pin = 26
|
||||
P20 Pin = 32
|
||||
P21 Pin = 21
|
||||
P22 Pin = 22
|
||||
P23 Pin = 15
|
||||
P24 Pin = 24
|
||||
P25 Pin = 19
|
||||
P26 Pin = 36
|
||||
P27 Pin = 0
|
||||
P28 Pin = 20
|
||||
P29 Pin = 5
|
||||
P30 Pin = 16
|
||||
P31 Pin = 8
|
||||
P32 Pin = 25
|
||||
P33 Pin = 40
|
||||
P34 Pin = 6
|
||||
)
|
||||
|
||||
// LED matrix pins
|
||||
const (
|
||||
LED_COL_1 Pin = P4
|
||||
LED_COL_2 Pin = P7
|
||||
LED_COL_3 Pin = P3
|
||||
LED_COL_4 Pin = P6
|
||||
LED_COL_5 Pin = P10
|
||||
LED_ROW_1 Pin = P21
|
||||
LED_ROW_2 Pin = P22
|
||||
LED_ROW_3 Pin = P23
|
||||
LED_ROW_4 Pin = P24
|
||||
LED_ROW_5 Pin = P25
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "BBC micro:bit V2"
|
||||
usb_STRING_MANUFACTURER = "BBC"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x0d28
|
||||
usb_PID uint16 = 0x0204
|
||||
)
|
||||
@@ -19,3 +19,17 @@ const (
|
||||
|
||||
// Onboard blue LED (on the AI-Thinker module).
|
||||
const LED = D4
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D5
|
||||
SPI0_SDO_PIN = D7
|
||||
SPI0_SDI_PIN = D6
|
||||
SPI0_CS0_PIN = D8
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = D2
|
||||
SCL_PIN = D1
|
||||
)
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// +build nucleof722ze
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
LED = LED_BUILTIN
|
||||
LED_BUILTIN = LED_GREEN
|
||||
LED_GREEN = PB0
|
||||
LED_BLUE = PB7
|
||||
LED_RED = PB14
|
||||
)
|
||||
|
||||
const (
|
||||
BUTTON = BUTTON_USER
|
||||
BUTTON_USER = PC13
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
// PD8 and PD9 are connected to the ST-Link Virtual Com Port (VCP)
|
||||
UART_TX_PIN = PD8
|
||||
UART_RX_PIN = PD9
|
||||
UART_ALT_FN = 7 // GPIO_AF7_UART3
|
||||
)
|
||||
|
||||
var (
|
||||
// USART3 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 USART2.
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART3,
|
||||
AltFuncSelector: UART_ALT_FN,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART3, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA5
|
||||
SPI0_SDI_PIN = PA6
|
||||
SPI0_SDO_PIN = PA7
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SCL_PIN = PB6
|
||||
SDA_PIN = PB7
|
||||
)
|
||||
@@ -0,0 +1,120 @@
|
||||
// +build p1am_100
|
||||
|
||||
// This contains the pin mappings for the ProductivityOpen P1AM-100 board.
|
||||
//
|
||||
// For more information, see: https://facts-engineering.github.io/
|
||||
//
|
||||
package machine
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0x07738135
|
||||
|
||||
// Note: On the P1AM-100, pins D8, D9, D10, A3, and A4 are used for
|
||||
// communication with the base controller.
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 Pin = PA22 // PWM available
|
||||
D1 Pin = PA23 // PWM available
|
||||
D2 Pin = PA10 // PWM available
|
||||
D3 Pin = PA11 // PWM available
|
||||
D4 Pin = PB10 // PWM available
|
||||
D5 Pin = PB11 // PWM available
|
||||
D6 Pin = PA20 // PWM available
|
||||
D7 Pin = PA21 // PWM available
|
||||
|
||||
D8 Pin = PA16 // PWM available
|
||||
D9 Pin = PA17
|
||||
D10 Pin = PA19 // PWM available
|
||||
D11 Pin = PA08
|
||||
D12 Pin = PA09
|
||||
D13 Pin = PB23
|
||||
D14 Pin = PB22
|
||||
|
||||
// Remaining pins are shared with analog pins
|
||||
D15 Pin = PA02
|
||||
|
||||
D16 Pin = PB02
|
||||
D17 Pin = PB03
|
||||
D18 Pin = PA04 // PWM available
|
||||
D19 Pin = PA05 // PWM available
|
||||
D20 Pin = PA06
|
||||
D21 Pin = PA07
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 Pin = PA02 // ADC/AIN[0]
|
||||
A1 Pin = PB02 // ADC/AIN[10]
|
||||
A2 Pin = PB03 // ADC/AIN[11]
|
||||
A3 Pin = PA04 // ADC/AIN[4]
|
||||
A4 Pin = PA05 // ADC/AIN[5]
|
||||
A5 Pin = PA06 // ADC/AIN[6]
|
||||
A6 Pin = PA07 // ADC/AIN[7]
|
||||
)
|
||||
|
||||
const (
|
||||
SWITCH Pin = PA28
|
||||
LED Pin = PB08
|
||||
ADC_BATTERY Pin = PB09 // ADC/AIN[3]
|
||||
)
|
||||
|
||||
// P1AM Base Controller
|
||||
const (
|
||||
BASE_SLAVE_SELECT_PIN Pin = A3
|
||||
BASE_SLAVE_ACK_PIN Pin = A4
|
||||
BASE_ENABLE_PIN Pin = PB09
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN Pin = PA24
|
||||
USBCDC_DP_PIN Pin = PA25
|
||||
USBCDC_HOST_ENABLE_PIN Pin = PA18
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_RX_PIN Pin = PB23 // RX: SERCOM5/PAD[3]
|
||||
UART_TX_PIN Pin = PB22 // TX: SERCOM5/PAD[2]
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = PA08 // SDA: SERCOM0/PAD[0]
|
||||
SCL_PIN Pin = PA09 // SCL: SERCOM0/PAD[1]
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = D9 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_SDO_PIN Pin = D8 // SDO: SERCOM1/PAD[0]
|
||||
SPI0_SDI_PIN Pin = D10 // SDI: SERCOM1/PAD[3]
|
||||
)
|
||||
|
||||
// SD card pins
|
||||
const (
|
||||
SDCARD_SDI_PIN Pin = PA15 // SDI: SERCOM2/PAD[3]
|
||||
SDCARD_SDO_PIN Pin = PA12 // SDO: SERCOM2/PAD[0]
|
||||
SDCARD_SCK_PIN Pin = PA13 // SCK: SERCOM2/PAD[1]
|
||||
SDCARD_SS_PIN Pin = PA14 // SS: as GPIO
|
||||
SDCARD_CD_PIN Pin = PA27
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN Pin = D2
|
||||
I2S_SD_PIN Pin = A6
|
||||
I2S_WS_PIN = D3
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "P1AM-100"
|
||||
usb_STRING_MANUFACTURER = "Facts Engineering"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x1354
|
||||
usb_PID uint16 = 0x4000
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
// +build sam,atsamd21,p1am_100
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART1 on the P1AM-100 connects to the normal TX/RX pins.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM3_USART,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the P1AM-100.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM0_I2CM,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the P1AM-100 is used for Base Controller.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPI,
|
||||
SERCOM: 1,
|
||||
}
|
||||
BASE_CONTROLLER_SPI = SPI0
|
||||
)
|
||||
|
||||
// SPI1 is connected to the SD card slot on the P1AM-100
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM2_SPI,
|
||||
SERCOM: 2,
|
||||
}
|
||||
SDCARD_SPI = SPI1
|
||||
)
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = USB
|
||||
Serial = &USB
|
||||
UART0 = NRF_UART0
|
||||
)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = USB
|
||||
Serial = &USB
|
||||
UART0 = NRF_UART0
|
||||
)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = USB
|
||||
Serial = &USB
|
||||
UART0 = NRF_UART0
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
// +build sam,atsamd21,qtpy
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PA02 // PWM available
|
||||
D1 = PA03
|
||||
D2 = PA04 // PWM available
|
||||
D3 = PA05 // PWM available
|
||||
D4 = PA16 // PWM available
|
||||
D5 = PA17 // PWM available
|
||||
D6 = PA06
|
||||
D7 = PA07
|
||||
D8 = PA11
|
||||
D9 = PA09
|
||||
D10 = PA10
|
||||
D11 = PA18
|
||||
D12 = PA15
|
||||
D13 = PA27
|
||||
D14 = PA23
|
||||
D15 = PA19
|
||||
D16 = PA22
|
||||
D17 = PA08
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = D1
|
||||
A1 = D1
|
||||
A2 = D2
|
||||
A3 = D3
|
||||
A4 = D4
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = D6
|
||||
UART_RX_PIN = D7
|
||||
)
|
||||
|
||||
// UART1 on the QT Py M0.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D8
|
||||
SPI0_SDO_PIN = D10
|
||||
SPI0_SDI_PIN = D9
|
||||
)
|
||||
|
||||
// SPI on the QT Py M0.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM0_SPI,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = D4 // SDA
|
||||
SCL_PIN = D5 // SCL
|
||||
)
|
||||
|
||||
// I2C on the QT Py M0.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN = PA10
|
||||
I2S_SD_PIN = PA08
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on QT Py M0.
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit QTPy M0"
|
||||
usb_STRING_MANUFACTURER = "Adafruit"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x80CB
|
||||
)
|
||||
@@ -30,7 +30,7 @@ var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
AltFuncSelector: stm32.AF7_USART1_2_3,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
@@ -62,7 +62,7 @@ const (
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: stm32.AF5_SPI1_SPI2,
|
||||
AltFuncSelector: AF5_SPI1_SPI2,
|
||||
}
|
||||
SPI1 = &SPI0
|
||||
)
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
// +build teensy40
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// Digital pins
|
||||
const (
|
||||
// = Pin // [Pad]: Alt Func 0 Alt Func 1 Alt Func 2 Alt Func 3 Alt Func 4 Alt Func 5 Alt Func 6 Alt Func 7 Alt Func 8 Alt Func 9
|
||||
// = ---- ----------- --------------- --------------- --------------- -------------- -------------------- ---------- -------------------- --------------------- --------------------- ----------------
|
||||
D0 = PA3 // [AD_B0_03]: FLEXCAN2_RX XBAR1_INOUT17 LPUART6_RX USB_OTG1_OC FLEXPWM1_PWMX01 GPIO1_IO03 REF_CLK_24M LPSPI3_PCS0 ~ ~
|
||||
D1 = PA2 // [AD_B0_02]: FLEXCAN2_TX XBAR1_INOUT16 LPUART6_TX USB_OTG1_PWR FLEXPWM1_PWMX00 GPIO1_IO02 LPI2C1_HREQ LPSPI3_SDI ~ ~
|
||||
D2 = PD4 // [EMC_04]: SEMC_DATA04 FLEXPWM4_PWMA02 SAI2_TX_DATA XBAR1_INOUT06 FLEXIO1_FLEXIO04 GPIO4_IO04 ~ ~ ~ ~
|
||||
D3 = PD5 // [EMC_05]: SEMC_DATA05 FLEXPWM4_PWMB02 SAI2_TX_SYNC XBAR1_INOUT07 FLEXIO1_FLEXIO05 GPIO4_IO05 ~ ~ ~ ~
|
||||
D4 = PD6 // [EMC_06]: SEMC_DATA06 FLEXPWM2_PWMA00 SAI2_TX_BCLK XBAR1_INOUT08 FLEXIO1_FLEXIO06 GPIO4_IO06 ~ ~ ~ ~
|
||||
D5 = PD8 // [EMC_08]: SEMC_DM00 FLEXPWM2_PWMA01 SAI2_RX_DATA XBAR1_INOUT17 FLEXIO1_FLEXIO08 GPIO4_IO08 ~ ~ ~ ~
|
||||
D6 = PB10 // [B0_10]: LCD_DATA06 QTIMER4_TIMER1 FLEXPWM2_PWMA02 SAI1_TX_DATA03 FLEXIO2_FLEXIO10 GPIO2_IO10 SRC_BOOT_CFG06 ENET2_CRS ~ ~
|
||||
D7 = PB17 // [B1_01]: LCD_DATA13 XBAR1_INOUT15 LPUART4_RX SAI1_TX_DATA00 FLEXIO2_FLEXIO17 GPIO2_IO17 FLEXPWM1_PWMB03 ENET2_RDATA00 FLEXIO3_FLEXIO17 ~
|
||||
D8 = PB16 // [B1_00]: LCD_DATA12 XBAR1_INOUT14 LPUART4_TX SAI1_RX_DATA00 FLEXIO2_FLEXIO16 GPIO2_IO16 FLEXPWM1_PWMA03 ENET2_RX_ER FLEXIO3_FLEXIO16 ~
|
||||
D9 = PB11 // [B0_11]: LCD_DATA07 QTIMER4_TIMER2 FLEXPWM2_PWMB02 SAI1_TX_DATA02 FLEXIO2_FLEXIO11 GPIO2_IO11 SRC_BOOT_CFG07 ENET2_COL ~ ~
|
||||
D10 = PB0 // [B0_00]: LCD_CLK QTIMER1_TIMER0 MQS_RIGHT LPSPI4_PCS0 FLEXIO2_FLEXIO00 GPIO2_IO00 SEMC_CSX01 ENET2_MDC ~ ~
|
||||
D11 = PB2 // [B0_02]: LCD_HSYNC QTIMER1_TIMER2 FLEXCAN1_TX LPSPI4_SDO FLEXIO2_FLEXIO02 GPIO2_IO02 SEMC_CSX03 ENET2_1588_EVENT0_OUT ~ ~
|
||||
D12 = PB1 // [B0_01]: LCD_ENABLE QTIMER1_TIMER1 MQS_LEFT LPSPI4_SDI FLEXIO2_FLEXIO01 GPIO2_IO01 SEMC_CSX02 ENET2_MDIO ~ ~
|
||||
D13 = PB3 // [B0_03]: LCD_VSYNC QTIMER2_TIMER0 FLEXCAN1_RX LPSPI4_SCK FLEXIO2_FLEXIO03 GPIO2_IO03 WDOG2_RESET_B_DEB ENET2_1588_EVENT0_IN ~ ~
|
||||
D14 = PA18 // [AD_B1_02]: USB_OTG1_ID QTIMER3_TIMER2 LPUART2_TX SPDIF_OUT ENET_1588_EVENT2_OUT GPIO1_IO18 USDHC1_CD_B KPP_ROW06 GPT2_CLK FLEXIO3_FLEXIO02
|
||||
D15 = PA19 // [AD_B1_03]: USB_OTG1_OC QTIMER3_TIMER3 LPUART2_RX SPDIF_IN ENET_1588_EVENT2_IN GPIO1_IO19 USDHC2_CD_B KPP_COL06 GPT2_CAPTURE1 FLEXIO3_FLEXIO03
|
||||
D16 = PA23 // [AD_B1_07]: FLEXSPIB_DATA00 LPI2C3_SCL LPUART3_RX SPDIF_EXT_CLK CSI_HSYNC GPIO1_IO23 USDHC2_DATA3 KPP_COL04 GPT2_COMPARE3 FLEXIO3_FLEXIO07
|
||||
D17 = PA22 // [AD_B1_06]: FLEXSPIB_DATA01 LPI2C3_SDA LPUART3_TX SPDIF_LOCK CSI_VSYNC GPIO1_IO22 USDHC2_DATA2 KPP_ROW04 GPT2_COMPARE2 FLEXIO3_FLEXIO06
|
||||
D18 = PA17 // [AD_B1_01]: USB_OTG1_PWR QTIMER3_TIMER1 LPUART2_RTS_B LPI2C1_SDA CCM_PMIC_READY GPIO1_IO17 USDHC1_VSELECT KPP_COL07 ENET2_1588_EVENT0_IN FLEXIO3_FLEXIO01
|
||||
D19 = PA16 // [AD_B1_00]: USB_OTG2_ID QTIMER3_TIMER0 LPUART2_CTS_B LPI2C1_SCL WDOG1_B GPIO1_IO16 USDHC1_WP KPP_ROW07 ENET2_1588_EVENT0_OUT FLEXIO3_FLEXIO00
|
||||
D20 = PA26 // [AD_B1_10]: FLEXSPIA_DATA03 WDOG1_B LPUART8_TX SAI1_RX_SYNC CSI_DATA07 GPIO1_IO26 USDHC2_WP KPP_ROW02 ENET2_1588_EVENT1_OUT FLEXIO3_FLEXIO10
|
||||
D21 = PA27 // [AD_B1_11]: FLEXSPIA_DATA02 EWM_OUT_B LPUART8_RX SAI1_RX_BCLK CSI_DATA06 GPIO1_IO27 USDHC2_RESET_B KPP_COL02 ENET2_1588_EVENT1_IN FLEXIO3_FLEXIO11
|
||||
D22 = PA24 // [AD_B1_08]: FLEXSPIA_SS1_B FLEXPWM4_PWMA00 FLEXCAN1_TX CCM_PMIC_READY CSI_DATA09 GPIO1_IO24 USDHC2_CMD KPP_ROW03 FLEXIO3_FLEXIO08 ~
|
||||
D23 = PA25 // [AD_B1_09]: FLEXSPIA_DQS FLEXPWM4_PWMA01 FLEXCAN1_RX SAI1_MCLK CSI_DATA08 GPIO1_IO25 USDHC2_CLK KPP_COL03 FLEXIO3_FLEXIO09 ~
|
||||
D24 = PA12 // [AD_B0_12]: LPI2C4_SCL CCM_PMIC_READY LPUART1_TX WDOG2_WDOG_B FLEXPWM1_PWMX02 GPIO1_IO12 ENET_1588_EVENT1_OUT NMI_GLUE_NMI ~ ~
|
||||
D25 = PA13 // [AD_B0_13]: LPI2C4_SDA GPT1_CLK LPUART1_RX EWM_OUT_B FLEXPWM1_PWMX03 GPIO1_IO13 ENET_1588_EVENT1_IN REF_CLK_24M ~ ~
|
||||
D26 = PA30 // [AD_B1_14]: FLEXSPIA_SCLK ACMP_OUT02 LPSPI3_SDO SAI1_TX_BCLK CSI_DATA03 GPIO1_IO30 USDHC2_DATA6 KPP_ROW00 ENET2_1588_EVENT3_OUT FLEXIO3_FLEXIO14
|
||||
D27 = PA31 // [AD_B1_15]: FLEXSPIA_SS0_B ACMP_OUT03 LPSPI3_SCK SAI1_TX_SYNC CSI_DATA02 GPIO1_IO31 USDHC2_DATA7 KPP_COL00 ENET2_1588_EVENT3_IN FLEXIO3_FLEXIO15
|
||||
D28 = PC18 // [EMC_32]: SEMC_DATA10 FLEXPWM3_PWMB01 LPUART7_RX CCM_PMIC_RDY CSI_DATA21 GPIO3_IO18 ENET2_TX_EN ~ ~ ~
|
||||
D29 = PD31 // [EMC_31]: SEMC_DATA09 FLEXPWM3_PWMA01 LPUART7_TX LPSPI1_PCS1 CSI_DATA22 GPIO4_IO31 ENET2_TDATA01 ~ ~ ~
|
||||
D30 = PC23 // [EMC_37]: SEMC_DATA15 XBAR1_IN23 GPT1_COMPARE3 SAI3_MCLK CSI_DATA16 GPIO3_IO23 USDHC2_WP ENET2_RX_EN FLEXCAN3_RX ~
|
||||
D31 = PC22 // [EMC_36]: SEMC_DATA14 XBAR1_IN22 GPT1_COMPARE2 SAI3_TX_DATA CSI_DATA17 GPIO3_IO22 USDHC1_WP ENET2_RDATA01 FLEXCAN3_TX ~
|
||||
D32 = PB12 // [B0_12]: LCD_DATA08 XBAR1_INOUT10 ARM_TRACE_CLK SAI1_TX_DATA01 FLEXIO2_FLEXIO12 GPIO2_IO12 SRC_BOOT_CFG08 ENET2_TDATA00 ~ ~
|
||||
D33 = PD7 // [EMC_07]: SEMC_DATA07 FLEXPWM2_PWMB00 SAI2_MCLK XBAR1_INOUT09 FLEXIO1_FLEXIO07 GPIO4_IO07 ~ ~ ~ ~
|
||||
D34 = PC15 // [SD_B0_03]: USDHC1_DATA1 FLEXPWM1_PWMB01 LPUART8_RTS_B XBAR1_INOUT07 LPSPI1_SDI GPIO3_IO15 ENET2_RDATA00 SEMC_CLK6 ~ ~
|
||||
D35 = PC14 // [SD_B0_02]: USDHC1_DATA0 FLEXPWM1_PWMA01 LPUART8_CTS_B XBAR1_INOUT06 LPSPI1_SDO GPIO3_IO14 ENET2_RX_ER SEMC_CLK5 ~ ~
|
||||
D36 = PC13 // [SD_B0_01]: USDHC1_CLK FLEXPWM1_PWMB00 LPI2C3_SDA XBAR1_INOUT05 LPSPI1_PCS0 GPIO3_IO13 FLEXSPIB_SS1_B ENET2_TX_CLK ENET2_REF_CLK2 ~
|
||||
D37 = PC12 // [SD_B0_00]: USDHC1_CMD FLEXPWM1_PWMA00 LPI2C3_SCL XBAR1_INOUT04 LPSPI1_SCK GPIO3_IO12 FLEXSPIA_SS1_B ENET2_TX_EN SEMC_DQS4 ~
|
||||
D38 = PC17 // [SD_B0_05]: USDHC1_DATA3 FLEXPWM1_PWMB02 LPUART8_RX XBAR1_INOUT09 FLEXSPIB_DQS GPIO3_IO17 CCM_CLKO2 ENET2_RX_EN ~ ~
|
||||
D39 = PC16 // [SD_B0_04]: USDHC1_DATA2 FLEXPWM1_PWMA02 LPUART8_TX XBAR1_INOUT08 FLEXSPIB_SS0_B GPIO3_IO16 CCM_CLKO1 ENET2_RDATA01 ~ ~
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
// = Pin // Dig | [Pad] {ADC1/ADC2}
|
||||
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
|
||||
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
|
||||
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
|
||||
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
|
||||
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
|
||||
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
|
||||
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
|
||||
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
|
||||
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
|
||||
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
|
||||
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
|
||||
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
|
||||
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
|
||||
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
|
||||
)
|
||||
|
||||
// Default peripheral pins
|
||||
const (
|
||||
LED = D13
|
||||
|
||||
UART_RX_PIN = UART1_RX_PIN // D0
|
||||
UART_TX_PIN = UART1_TX_PIN // D1
|
||||
|
||||
SPI_SDI_PIN = SPI1_SDI_PIN // D12
|
||||
SPI_SDO_PIN = SPI1_SDO_PIN // D11
|
||||
SPI_SCK_PIN = SPI1_SCK_PIN // D13
|
||||
SPI_CS_PIN = SPI1_CS_PIN // D10
|
||||
|
||||
I2C_SDA_PIN = I2C1_SDA_PIN // D18/A4
|
||||
I2C_SCL_PIN = I2C1_SCL_PIN // D19/A5
|
||||
)
|
||||
|
||||
func init() {
|
||||
// register any interrupt handlers for this board's peripherals
|
||||
UART1.Interrupt = interrupt.New(nxp.IRQ_LPUART6, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(nxp.IRQ_LPUART4, UART2.handleInterrupt)
|
||||
UART3.Interrupt = interrupt.New(nxp.IRQ_LPUART2, UART3.handleInterrupt)
|
||||
UART4.Interrupt = interrupt.New(nxp.IRQ_LPUART3, UART4.handleInterrupt)
|
||||
UART5.Interrupt = interrupt.New(nxp.IRQ_LPUART8, UART5.handleInterrupt)
|
||||
UART6.Interrupt = interrupt.New(nxp.IRQ_LPUART1, UART6.handleInterrupt)
|
||||
UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, UART7.handleInterrupt)
|
||||
}
|
||||
|
||||
// #=====================================================#
|
||||
// | UART |
|
||||
// #===========#===========#=============#===============#
|
||||
// | Interface | Hardware | Clock(Freq) | RX/TX : Alt |
|
||||
// #===========#===========#=============#=========-=====#
|
||||
// | UART1 | LPUART6 | OSC(24 MHz) | D0/D1 : 2/2 |
|
||||
// | UART2 | LPUART4 | OSC(24 MHz) | D7/D8 : 2/2 |
|
||||
// | UART3 | LPUART2 | OSC(24 MHz) | D15/D14 : 2/2 |
|
||||
// | UART4 | LPUART3 | OSC(24 MHz) | D16/D17 : 2/2 |
|
||||
// | UART5 | LPUART8 | OSC(24 MHz) | D21/D20 : 2/2 |
|
||||
// | UART6 | LPUART1 | OSC(24 MHz) | D25/D24 : 2/2 |
|
||||
// | UART7 | LPUART7 | OSC(24 MHz) | D28/D29 : 2/2 |
|
||||
// #===========#===========#=============#=========-=====#
|
||||
const (
|
||||
UART1_RX_PIN = D0
|
||||
UART1_TX_PIN = D1
|
||||
|
||||
UART2_RX_PIN = D7
|
||||
UART2_TX_PIN = D8
|
||||
|
||||
UART3_RX_PIN = D15
|
||||
UART3_TX_PIN = D14
|
||||
|
||||
UART4_RX_PIN = D16
|
||||
UART4_TX_PIN = D17
|
||||
|
||||
UART5_RX_PIN = D21
|
||||
UART5_TX_PIN = D20
|
||||
|
||||
UART6_RX_PIN = D25
|
||||
UART6_TX_PIN = D24
|
||||
|
||||
UART7_RX_PIN = D28
|
||||
UART7_TX_PIN = D29
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART6,
|
||||
muxRX: muxSelect{ // D0 (PA3 [AD_B0_03])
|
||||
mux: nxp.IOMUXC_LPUART6_RX_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART6_RX_SELECT_INPUT,
|
||||
},
|
||||
muxTX: muxSelect{ // D1 (PA2 [AD_B0_02])
|
||||
mux: nxp.IOMUXC_LPUART6_TX_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART6_TX_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART4,
|
||||
muxRX: muxSelect{ // D7 (PB17 [B1_01])
|
||||
mux: nxp.IOMUXC_LPUART4_RX_SELECT_INPUT_DAISY_GPIO_B1_01_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART4_RX_SELECT_INPUT,
|
||||
},
|
||||
muxTX: muxSelect{ // D8 (PB16 [B1_00])
|
||||
mux: nxp.IOMUXC_LPUART4_TX_SELECT_INPUT_DAISY_GPIO_B1_00_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART4_TX_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART2,
|
||||
muxRX: muxSelect{ // D15 (PA19 [AD_B1_03])
|
||||
mux: nxp.IOMUXC_LPUART2_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_03_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART2_RX_SELECT_INPUT,
|
||||
},
|
||||
muxTX: muxSelect{ // D14 (PA18 [AD_B1_02])
|
||||
mux: nxp.IOMUXC_LPUART2_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_02_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART2_TX_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
UART4 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART3,
|
||||
muxRX: muxSelect{ // D16 (PA23 [AD_B1_07])
|
||||
mux: nxp.IOMUXC_LPUART3_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_07_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART3_RX_SELECT_INPUT,
|
||||
},
|
||||
muxTX: muxSelect{ // D17 (PA22 [AD_B1_06])
|
||||
mux: nxp.IOMUXC_LPUART3_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_06_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART3_TX_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
UART5 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART8,
|
||||
muxRX: muxSelect{ // D21 (PA27 [AD_B1_11])
|
||||
mux: nxp.IOMUXC_LPUART8_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_11_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART8_RX_SELECT_INPUT,
|
||||
},
|
||||
muxTX: muxSelect{ // D20 (PA26 [AD_B1_10])
|
||||
mux: nxp.IOMUXC_LPUART8_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_10_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART8_TX_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
UART6 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART1,
|
||||
// LPUART1 not connected via IOMUXC
|
||||
// RX: D24 (PA12 [AD_B0_12])
|
||||
// TX: D25 (PA13 [AD_B0_13])
|
||||
}
|
||||
UART7 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: nxp.LPUART7,
|
||||
muxRX: muxSelect{ // D28 (PC18 [EMC_32])
|
||||
mux: nxp.IOMUXC_LPUART7_RX_SELECT_INPUT_DAISY_GPIO_EMC_32_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART7_RX_SELECT_INPUT,
|
||||
},
|
||||
muxTX: muxSelect{ // D29 (PD31 [EMC_31])
|
||||
mux: nxp.IOMUXC_LPUART7_TX_SELECT_INPUT_DAISY_GPIO_EMC_31_ALT2,
|
||||
sel: &nxp.IOMUXC.LPUART7_TX_SELECT_INPUT,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// #===========#==========#===============#===========================#
|
||||
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
|
||||
// #===========#==========#===============#=================-=========#
|
||||
// | SPI1 | LPSPI4 | PLL2(132 MHz) | D12/D11/D13/D10 : 3/3/3/3 |
|
||||
// | SPI2 | LPSPI3 | PLL2(132 MHz) | D1/D26/D27/D0 : 7/2/2/7 |
|
||||
// | SPI3 | LPSPI1 | PLL2(132 MHz) | D34/D35/D37/D36 : 4/4/4/4 |
|
||||
// #===========#==========#===============#=================-=========#
|
||||
const (
|
||||
SPI1_SDI_PIN = D12
|
||||
SPI1_SDO_PIN = D11
|
||||
SPI1_SCK_PIN = D13
|
||||
SPI1_CS_PIN = D10
|
||||
|
||||
SPI2_SDI_PIN = D1
|
||||
SPI2_SDO_PIN = D26
|
||||
SPI2_SCK_PIN = D27
|
||||
SPI2_CS_PIN = D0
|
||||
|
||||
SPI3_SDI_PIN = D34
|
||||
SPI3_SDO_PIN = D35
|
||||
SPI3_SCK_PIN = D37
|
||||
SPI3_CS_PIN = D36
|
||||
)
|
||||
|
||||
// #====================================================#
|
||||
// | I2C |
|
||||
// #===========#==========#=============#===============#
|
||||
// | Interface | Hardware | Clock(Freq) | SDA/SCL : Alt |
|
||||
// #===========#==========#=============#=========-=====#
|
||||
// | I2C1 | LPI2C1 | OSC(24 MHz) | D18/D19 : 3/3 |
|
||||
// | I2C2 | LPI2C3 | OSC(24 MHz) | D17/D16 : 1/1 |
|
||||
// | I2C3 | LPI2C4 | OSC(24 MHz) | D25/D24 : 0/0 |
|
||||
// #===========#==========#=============#=========-=====#
|
||||
const (
|
||||
I2C1_SDA_PIN = D18
|
||||
I2C1_SCL_PIN = D19
|
||||
|
||||
I2C2_SDA_PIN = D17
|
||||
I2C2_SCL_PIN = D16
|
||||
|
||||
I2C3_SDA_PIN = D25
|
||||
I2C3_SCL_PIN = D24
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user