mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 15:18:41 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71fa131ef9 |
+66
-33
@@ -40,34 +40,70 @@ commands:
|
||||
sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
|
||||
sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
|
||||
rm node-v10.15.1-linux-x64.tar.xz
|
||||
dep:
|
||||
steps:
|
||||
- run:
|
||||
name: "Install Go dependencies"
|
||||
command: |
|
||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
dep ensure --vendor-only
|
||||
llvm-source-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-8-v5
|
||||
- llvm-source-8-v2
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-8-v5
|
||||
key: llvm-source-8-v2
|
||||
paths:
|
||||
- llvm-project
|
||||
- llvm
|
||||
smoketest:
|
||||
steps:
|
||||
- smoketest-no-avr
|
||||
- run: tinygo build -size short -o test.elf -target=arduino examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=digispark examples/blinky1
|
||||
smoketest-no-avr:
|
||||
steps:
|
||||
- run: tinygo build -size short -o test.elf -target=pca10040 examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=pca10040 examples/blinky2
|
||||
- run: tinygo build -o blinky2 examples/blinky2 # TODO: re-enable -size flag with MachO support
|
||||
- run: tinygo build -size short -o test.elf -target=pca10040 examples/test
|
||||
- run: tinygo build -size short -o test.elf -target=microbit examples/echo
|
||||
- run: tinygo build -size short -o test.elf -target=nrf52840-mdk examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=pca10031 examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=bluepill examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=reelboard examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=reelboard examples/blinky2
|
||||
- run: tinygo build -size short -o test.elf -target=pca10056 examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=pca10056 examples/blinky2
|
||||
- run: tinygo build -size short -o test.elf -target=itsybitsy-m0 examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
||||
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
||||
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/export
|
||||
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
||||
test-linux:
|
||||
parameters:
|
||||
llvm:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- submodules
|
||||
- apt-dependencies:
|
||||
llvm: "-8"
|
||||
llvm: <<parameters.llvm>>
|
||||
- install-node
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-{{ checksum "Gopkg.lock" }}
|
||||
- llvm-source-linux
|
||||
- dep
|
||||
- run: go install .
|
||||
- run: go test -v
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest RISCV=0
|
||||
- smoketest
|
||||
- save_cache:
|
||||
key: go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
@@ -100,7 +136,7 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-8-linux-v7
|
||||
- llvm-build-8-linux-v4
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -118,7 +154,7 @@ commands:
|
||||
make llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-8-linux-v7
|
||||
key: llvm-build-8-linux-v4
|
||||
paths:
|
||||
llvm-build
|
||||
- run:
|
||||
@@ -127,6 +163,7 @@ commands:
|
||||
ln -s $PWD/llvm-build/bin/clang-8 /go/bin/clang-8
|
||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-8
|
||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-8
|
||||
- dep
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make test
|
||||
@@ -149,12 +186,7 @@ commands:
|
||||
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
||||
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
||||
tinygo version
|
||||
- run:
|
||||
name: "Download SiFive GNU toolchain"
|
||||
command: |
|
||||
curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz
|
||||
sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz
|
||||
- run: make smoketest
|
||||
- smoketest
|
||||
build-macos:
|
||||
steps:
|
||||
- checkout
|
||||
@@ -162,23 +194,20 @@ commands:
|
||||
- run:
|
||||
name: "Install dependencies"
|
||||
command: |
|
||||
curl https://dl.google.com/go/go1.12.5.darwin-amd64.tar.gz -o go1.12.5.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.12.5.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install go dep qemu
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-8-macos-v5
|
||||
- llvm-source-8-macos-v2
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-8-macos-v5
|
||||
key: llvm-source-8-macos-v2
|
||||
paths:
|
||||
- llvm-project
|
||||
- llvm
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-8-macos-v6
|
||||
- llvm-build-8-macos-v3
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -190,13 +219,16 @@ commands:
|
||||
make llvm-build
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-8-macos-v6
|
||||
key: llvm-build-8-macos-v3
|
||||
paths:
|
||||
llvm-build
|
||||
- run:
|
||||
name: "Create LLVM symlinks"
|
||||
command: |
|
||||
ln -s $PWD/llvm-build/bin/clang-8 /usr/local/bin/clang-8
|
||||
- run:
|
||||
name: "Install Go dependencies"
|
||||
command: dep ensure --vendor-only
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make test
|
||||
@@ -214,33 +246,34 @@ commands:
|
||||
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
|
||||
tinygo version
|
||||
- run:
|
||||
name: "Download SiFive GNU toolchain"
|
||||
command: |
|
||||
curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-apple-darwin.tar.gz
|
||||
sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-apple-darwin.tar.gz
|
||||
- run: make smoketest AVR=0
|
||||
- smoketest-no-avr
|
||||
|
||||
|
||||
jobs:
|
||||
test-llvm8-go111:
|
||||
docker:
|
||||
- image: circleci/golang:1.11-stretch
|
||||
- image: circleci/golang:1.11
|
||||
working_directory: /go/src/github.com/tinygo-org/tinygo
|
||||
steps:
|
||||
- test-linux
|
||||
- test-linux:
|
||||
llvm: "-8"
|
||||
test-llvm8-go112:
|
||||
docker:
|
||||
- image: circleci/golang:1.12-stretch
|
||||
- image: circleci/golang:1.12
|
||||
working_directory: /go/src/github.com/tinygo-org/tinygo
|
||||
steps:
|
||||
- test-linux
|
||||
- test-linux:
|
||||
llvm: "-8"
|
||||
build-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.12-stretch
|
||||
- image: circleci/golang:1.12
|
||||
working_directory: /go/src/github.com/tinygo-org/tinygo
|
||||
steps:
|
||||
- build-linux
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: "10.1.0"
|
||||
working_directory: ~/go/src/github.com/tinygo-org/tinygo
|
||||
steps:
|
||||
- build-macos
|
||||
|
||||
|
||||
+2
-4
@@ -5,12 +5,10 @@ src/device/avr/*.ld
|
||||
src/device/avr/*.s
|
||||
src/device/nrf/*.go
|
||||
src/device/nrf/*.s
|
||||
src/device/sam/*.go
|
||||
src/device/sam/*.s
|
||||
src/device/sifive/*.go
|
||||
src/device/sifive/*.s
|
||||
src/device/stm32/*.go
|
||||
src/device/stm32/*.s
|
||||
src/device/sam/*.go
|
||||
src/device/sam/*.s
|
||||
vendor
|
||||
llvm
|
||||
llvm-build
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
url = https://github.com/avr-rust/avr-mcu.git
|
||||
[submodule "lib/cmsis-svd"]
|
||||
path = lib/cmsis-svd
|
||||
url = https://github.com/tinygo-org/cmsis-svd
|
||||
url = https://github.com/posborne/cmsis-svd
|
||||
[submodule "lib/compiler-rt"]
|
||||
path = lib/compiler-rt
|
||||
url = https://github.com/llvm-mirror/compiler-rt.git
|
||||
|
||||
+2
-2
@@ -27,8 +27,8 @@ on a different system like Mac.
|
||||
|
||||
## Download the source
|
||||
|
||||
The first step is to download the TinyGo sources (use `--recursive` if you clone
|
||||
the git repository). Then, inside the directory, perform these steps:
|
||||
The first step is to download the TinyGo sources. Then, inside the directory,
|
||||
perform these steps:
|
||||
|
||||
dep ensure -vendor-only # download Go dependencies
|
||||
make llvm-source # download LLVM
|
||||
|
||||
@@ -1,99 +1,3 @@
|
||||
0.7.1
|
||||
---
|
||||
* **targets**
|
||||
- `atsamd21`: add support for the `-port` flag in the flash subcommand
|
||||
|
||||
0.7.0
|
||||
---
|
||||
* **command line**
|
||||
- try more locations to find Clang built-in headers
|
||||
- add support for `tinygo test`
|
||||
- build current directory if no package is specified
|
||||
- support custom .json target spec with `-target` flag
|
||||
- use zversion.go to detect version of GOROOT version
|
||||
- make initial heap size configurable for some targets (currently WebAssembly
|
||||
only)
|
||||
* **cgo**
|
||||
- add support for bitfields using generated getters and setters
|
||||
- add support for anonymous structs
|
||||
* **compiler**
|
||||
- show an error instead of panicking on duplicate function definitions
|
||||
- allow packages like github.com/tinygo-org/tinygo/src/\* by aliasing it
|
||||
- remove `//go:volatile` support
|
||||
It has been replaced with the runtime/volatile package.
|
||||
- allow poiners in map keys
|
||||
- support non-constant syscall numbers
|
||||
- implement non-blocking selects
|
||||
- add support for the `-tags` flag
|
||||
- add support for `string` to `[]rune` conversion
|
||||
- implement a portable conservative garbage collector (with support for wasm)
|
||||
- add the `//go:noinline` pragma
|
||||
* **standard library**
|
||||
- `os`: add `os.Exit` and `syscall.Exit`
|
||||
- `os`: add several stubs
|
||||
- `runtime`: fix heap corruption in conservative GC
|
||||
- `runtime`: add support for math intrinsics where supported, massively
|
||||
speeding up some benchmarks
|
||||
- `testing`: add basic support for testing
|
||||
* **targets**
|
||||
- add support for a generic target that calls `__tinygo_*` functions for
|
||||
peripheral access
|
||||
- `arduino-nano33`: add support for this board
|
||||
- `hifive1`: add support for this RISC-V board
|
||||
- `reelboard`: add e-paper pins
|
||||
- `reelboard`: add `PowerSupplyActive` to enable voltage for on-board devices
|
||||
- `wasm`: put the stack at the start of linear memory, to detect stack
|
||||
overflows
|
||||
|
||||
0.6.0
|
||||
---
|
||||
* **command line**
|
||||
- some portability improvements
|
||||
- make `$GOROOT` more robust and configurable
|
||||
- check for Clang at the Homebrew install location as fallback
|
||||
* **compiler driver**
|
||||
- support multiple variations of LLVM commands, for non-Debian distributions
|
||||
* **compiler**
|
||||
- improve code quality in multiple ways
|
||||
- make panic configurable, adding trap on panic
|
||||
- refactor many internal parts of the compiler
|
||||
- print all errors encountered during compilation
|
||||
- implement calling function values of a named type
|
||||
- implement returning values from blocking functions
|
||||
- allow larger-than-int values to be sent across a channel
|
||||
- implement complex arithmetic
|
||||
- improve hashmap support
|
||||
- add debuginfo for function arguments
|
||||
- insert nil checks on stores (increasing code size)
|
||||
- implement volatile operations as compiler builtins
|
||||
- add `//go:inline` pragma
|
||||
- add build tags for the Go stdlib version
|
||||
* **cgo**
|
||||
- implement `char`, `enum` and `void*` types
|
||||
- support `#include` for builtin headers
|
||||
- improve typedef/struct/enum support
|
||||
- only include symbols that are necessary, for broader support
|
||||
- mark external function args as `nocapture`
|
||||
- implement support for some `#define` constants
|
||||
- implement support for multiple CGo files in a single package
|
||||
- **standard library**
|
||||
- `machine`: remove microbit matrix (moved to drivers repository)
|
||||
- `machine`: refactor pins to use `Pin` type instead of `GPIO`
|
||||
- `runtime`: print more interface types on panic, including `error`
|
||||
* **targets**
|
||||
- `arm`: print an error on HardFault (including stack overflows)
|
||||
- `atsamd21`: fix a bug in the ADC peripheral
|
||||
- `atsamd21`: add support for I2S
|
||||
- `feather-m0`: add support for this board
|
||||
- `nrf51`: fix a bug in I2C
|
||||
- `stm32f103xx`: fix a bug in I2C
|
||||
- `syscall`: implement `Exit` on unix
|
||||
- `trinket-m0`: add support for this board
|
||||
- `wasm`: make _main_ example smaller
|
||||
- `wasm`: don't cache wasm file in the server, for ease of debugging
|
||||
- `wasm`: work around bug #41508 that caused a deadlock while linking
|
||||
- `wasm`: add support for `js.FuncOf`
|
||||
|
||||
0.5.0
|
||||
---
|
||||
- **compiler driver**
|
||||
|
||||
@@ -16,16 +16,12 @@ Please open a Github issue with your problem, and we will be happy to assist.
|
||||
|
||||
We probably have not implemented it yet. Please take a look at our [Roadmap](https://github.com/tinygo-org/tinygo/wiki/Roadmap). Your pull request adding the functionality to TinyGo would be greatly appreciated.
|
||||
|
||||
Please open a Github issue. We want to help, and also make sure that there is no duplications of efforts. Sometimes what you need is already being worked on by someone else.
|
||||
|
||||
A long tail of small (and large) language features haven't been implemented yet. In almost all cases, the compiler will show a `todo:` error from `compiler/compiler.go` when you try to use it. You can try implementing it, or open a bug report with a small code sample that fails to compile.
|
||||
|
||||
### Some specific hardware you want to use does not appear to be in TinyGo
|
||||
|
||||
As above, we probably have not implemented it yet. Your contribution adding the hardware support to TinyGo would be greatly appreciated.
|
||||
|
||||
Please start by opening a Github issue. We want to help you to help us to help you.
|
||||
|
||||
Lots of targets/boards are still unsupported. Adding an architecture often requires a few compiler changes, but if the architecture is supported you can try implementing support for a new chip or board in `src/runtime`. For details, see [this wiki entry on adding archs/chips/boards](https://github.com/tinygo-org/tinygo/wiki/Adding-a-new-board).
|
||||
|
||||
Microcontrollers have lots of peripherals (I2C, SPI, ADC, etc.) and many don't have an implementation yet in the `machine` package. Adding support for new peripherals is very useful.
|
||||
|
||||
+3
-3
@@ -46,7 +46,7 @@ RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device-avr && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get remove -y python3 make && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
|
||||
@@ -64,7 +64,7 @@ RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make clang-8 && \
|
||||
make gen-device-nrf && make gen-device-stm32 && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get remove -y python3 make && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
|
||||
@@ -79,7 +79,7 @@ RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make clang-8 binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get remove -y python3 make && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
all: tinygo
|
||||
tinygo: build/tinygo
|
||||
|
||||
.PHONY: all tinygo build/tinygo test llvm-build llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
|
||||
|
||||
# Default build and source directories, as created by `make llvm-build`.
|
||||
LLVM_BUILDDIR ?= llvm-build
|
||||
CLANG_SRC ?= llvm-project/clang
|
||||
LLD_SRC ?= llvm-project/lld
|
||||
|
||||
.PHONY: all tinygo build/tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
|
||||
CLANG_SRC ?= llvm/tools/clang
|
||||
LLD_SRC ?= llvm/tools/lld
|
||||
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
||||
|
||||
@@ -18,30 +18,27 @@ ifeq ($(UNAME_S),Linux)
|
||||
END_GROUP = -Wl,--end-group
|
||||
endif
|
||||
|
||||
CLANG_LIBS = $(START_GROUP) $(abspath $(LLVM_BUILDDIR))/lib/libclang.a -lclangAnalysis -lclangARCMigrate -lclangAST -lclangASTMatchers -lclangBasic -lclangCodeGen -lclangCrossTU -lclangDriver -lclangDynamicASTMatchers -lclangEdit -lclangFormat -lclangFrontend -lclangFrontendTool -lclangHandleCXX -lclangHandleLLVM -lclangIndex -lclangLex -lclangParse -lclangRewrite -lclangRewriteFrontend -lclangSema -lclangSerialization -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangStaticAnalyzerFrontend -lclangTooling -lclangToolingASTDiff -lclangToolingCore -lclangToolingInclusions $(END_GROUP) -lstdc++
|
||||
CLANG_LIBS = $(START_GROUP) $(abspath $(LLVM_BUILDDIR))/lib/libclang.a -lclangAnalysis -lclangARCMigrate -lclangAST -lclangASTMatchers -lclangBasic -lclangCodeGen -lclangCrossTU -lclangDriver -lclangDynamicASTMatchers -lclangEdit -lclangFormat -lclangFrontend -lclangFrontendTool -lclangHandleCXX -lclangHandleLLVM -lclangIndex -lclangLex -lclangParse -lclangRewrite -lclangRewriteFrontend -lclangSema -lclangSerialization -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangStaticAnalyzerFrontend -lclangTooling -lclangToolingASTDiff -lclangToolingCore -lclangToolingInclusions -lclangToolingRefactor $(END_GROUP) -lstdc++
|
||||
|
||||
LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML $(END_GROUP)
|
||||
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config)","")
|
||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++11
|
||||
CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS))
|
||||
endif
|
||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++11
|
||||
CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS))
|
||||
|
||||
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go cgo compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall
|
||||
FMT_PATHS = ./*.go compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall
|
||||
fmt:
|
||||
@gofmt -l -w $(FMT_PATHS)
|
||||
fmt-check:
|
||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||
|
||||
|
||||
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32
|
||||
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-stm32
|
||||
|
||||
gen-device-avr:
|
||||
./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
||||
@@ -56,84 +53,37 @@ gen-device-sam:
|
||||
./tools/gen-device-svd.py lib/cmsis-svd/data/Atmel/ src/device/sam/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel
|
||||
go fmt ./src/device/sam
|
||||
|
||||
gen-device-sifive:
|
||||
./tools/gen-device-svd.py lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/ --source=https://github.com/AdaCore/svd2ada/tree/master/CMSIS-SVD/SiFive-Community
|
||||
go fmt ./src/device/sifive
|
||||
|
||||
gen-device-stm32:
|
||||
./tools/gen-device-svd.py lib/cmsis-svd/data/STMicro/ src/device/stm32/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro
|
||||
go fmt ./src/device/stm32
|
||||
|
||||
|
||||
# Get LLVM sources.
|
||||
llvm-project/README.md:
|
||||
git clone -b release/8.x https://github.com/llvm/llvm-project
|
||||
llvm-source: llvm-project/README.md
|
||||
llvm/README.txt:
|
||||
git clone -b release_80 https://github.com/llvm-mirror/llvm.git llvm
|
||||
llvm/tools/clang/README.txt:
|
||||
git clone -b release_80 https://github.com/llvm-mirror/clang.git llvm/tools/clang
|
||||
llvm/tools/lld/README.md:
|
||||
git clone -b release_80 https://github.com/llvm-mirror/lld.git llvm/tools/lld
|
||||
llvm-source: llvm/README.txt llvm/tools/clang/README.txt llvm/tools/lld/README.md
|
||||
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
||||
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/llvm-project/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;RISCV" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=OFF -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF
|
||||
llvm-build/build.ninja: llvm-source
|
||||
mkdir -p llvm-build; cd llvm-build; cmake -G Ninja ../llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=OFF -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF
|
||||
|
||||
# Build LLVM.
|
||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
cd $(LLVM_BUILDDIR); ninja
|
||||
llvm-build: llvm-build/build.ninja
|
||||
cd llvm-build; ninja
|
||||
|
||||
|
||||
# Build the Go compiler.
|
||||
build/tinygo:
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
@if [ ! -f llvm-build/bin/llvm-config ]; then echo "Fetch and build LLVM first by running:\n make llvm-source\n make llvm-build"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go build -o build/tinygo -tags byollvm .
|
||||
|
||||
test:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test -v -tags byollvm .
|
||||
|
||||
tinygo-test:
|
||||
cd tests/tinygotest && tinygo test
|
||||
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
# test all examples
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/adc
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/blinkm
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/button
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/button2
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/echo
|
||||
tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/mcp3008
|
||||
tinygo build -size short -o test.elf -target=microbit examples/microbit-blink
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/pwm
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/serial
|
||||
tinygo build -size short -o test.elf -target=pca10040 examples/test
|
||||
# test all targets/boards
|
||||
tinygo build -o test.wasm -tags=pca10040 examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=microbit examples/echo
|
||||
tinygo build -size short -o test.elf -target=nrf52840-mdk examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=pca10031 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=bluepill examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=reelboard examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=reelboard examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=pca10056 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=pca10056 examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=itsybitsy-m0 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=feather-m0 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=trinket-m0 examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
||||
tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
|
||||
ifneq ($(AVR), 0)
|
||||
tinygo build -size short -o test.elf -target=arduino examples/blinky1
|
||||
tinygo build -size short -o test.elf -target=digispark examples/blinky1
|
||||
endif
|
||||
ifneq ($(RISCV), 0)
|
||||
tinygo build -size short -o test.elf -target=hifive1b examples/blinky1
|
||||
endif
|
||||
tinygo build -o wasm.wasm -target=wasm examples/wasm/export
|
||||
tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
||||
|
||||
release: build/tinygo gen-device
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led := machine.GPIO{machine.LED}
|
||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
for {
|
||||
led.Low()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
@@ -43,17 +43,13 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 15 microcontroller boards are currently supported:
|
||||
The following microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
* [BBC micro:bit](https://microbit.org/)
|
||||
* [BBC:Microbit](https://microbit.org/)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||
* [Digispark](http://digistump.com/products/1)
|
||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||
|
||||
-997
@@ -1,997 +0,0 @@
|
||||
// Package cgo implements CGo by modifying a loaded AST. It does this by parsing
|
||||
// the `import "C"` statements found in the source code with libclang and
|
||||
// generating stub function and global declarations.
|
||||
//
|
||||
// There are a few advantages to modifying the AST directly instead of doing CGo
|
||||
// as a preprocessing step, with the main advantage being that debug information
|
||||
// is kept intact as much as possible.
|
||||
package cgo
|
||||
|
||||
// This file extracts the `import "C"` statement from the source and modifies
|
||||
// the AST for CGo. It does not use libclang directly: see libclang.go for the C
|
||||
// source file parsing.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// cgoPackage holds all CGo-related information of a package.
|
||||
type cgoPackage struct {
|
||||
generated *ast.File
|
||||
generatedPos token.Pos
|
||||
errors []error
|
||||
dir string
|
||||
fset *token.FileSet
|
||||
tokenFiles map[string]*token.File
|
||||
missingSymbols map[string]struct{}
|
||||
constants map[string]constantInfo
|
||||
functions map[string]*functionInfo
|
||||
globals map[string]globalInfo
|
||||
typedefs map[string]*typedefInfo
|
||||
elaboratedTypes map[string]*elaboratedTypeInfo
|
||||
enums map[string]enumInfo
|
||||
anonStructNum int
|
||||
}
|
||||
|
||||
// constantInfo stores some information about a CGo constant found by libclang
|
||||
// and declared in the Go AST.
|
||||
type constantInfo struct {
|
||||
expr *ast.BasicLit
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
// functionInfo stores some information about a CGo function found by libclang
|
||||
// and declared in the AST.
|
||||
type functionInfo struct {
|
||||
args []paramInfo
|
||||
results *ast.FieldList
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
// paramInfo is a parameter of a CGo function (see functionInfo).
|
||||
type paramInfo struct {
|
||||
name string
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// typedefInfo contains information about a single typedef in C.
|
||||
type typedefInfo struct {
|
||||
typeExpr ast.Expr
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
// elaboratedTypeInfo contains some information about an elaborated type
|
||||
// (struct, union) found in the C AST.
|
||||
type elaboratedTypeInfo struct {
|
||||
typeExpr *ast.StructType
|
||||
pos token.Pos
|
||||
bitfields []bitfieldInfo
|
||||
}
|
||||
|
||||
// bitfieldInfo contains information about a single bitfield in a struct. It
|
||||
// keeps information about the start, end, and the special (renamed) base field
|
||||
// of this bitfield.
|
||||
type bitfieldInfo struct {
|
||||
field *ast.Field
|
||||
name string
|
||||
pos token.Pos
|
||||
startBit int64
|
||||
endBit int64 // may be 0 meaning "until the end of the field"
|
||||
}
|
||||
|
||||
// enumInfo contains information about an enum in the C.
|
||||
type enumInfo struct {
|
||||
typeExpr ast.Expr
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
// globalInfo contains information about a declared global variable in C.
|
||||
type globalInfo struct {
|
||||
typeExpr ast.Expr
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||
// in both languages. See addTypeAliases.
|
||||
var cgoAliases = map[string]string{
|
||||
"C.int8_t": "int8",
|
||||
"C.int16_t": "int16",
|
||||
"C.int32_t": "int32",
|
||||
"C.int64_t": "int64",
|
||||
"C.uint8_t": "uint8",
|
||||
"C.uint16_t": "uint16",
|
||||
"C.uint32_t": "uint32",
|
||||
"C.uint64_t": "uint64",
|
||||
"C.uintptr_t": "uintptr",
|
||||
}
|
||||
|
||||
// builtinAliases are handled specially because they only exist on the Go side
|
||||
// of CGo, not on the CGo side (they're prefixed with "_Cgo_" there).
|
||||
var builtinAliases = map[string]struct{}{
|
||||
"char": struct{}{},
|
||||
"schar": struct{}{},
|
||||
"uchar": struct{}{},
|
||||
"short": struct{}{},
|
||||
"ushort": struct{}{},
|
||||
"int": struct{}{},
|
||||
"uint": struct{}{},
|
||||
"long": struct{}{},
|
||||
"ulong": struct{}{},
|
||||
"longlong": struct{}{},
|
||||
"ulonglong": struct{}{},
|
||||
}
|
||||
|
||||
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
||||
// somehow from C. This is done by adding some typedefs to get the size of each
|
||||
// type.
|
||||
const cgoTypes = `
|
||||
typedef char _Cgo_char;
|
||||
typedef signed char _Cgo_schar;
|
||||
typedef unsigned char _Cgo_uchar;
|
||||
typedef short _Cgo_short;
|
||||
typedef unsigned short _Cgo_ushort;
|
||||
typedef int _Cgo_int;
|
||||
typedef unsigned int _Cgo_uint;
|
||||
typedef long _Cgo_long;
|
||||
typedef unsigned long _Cgo_ulong;
|
||||
typedef long long _Cgo_longlong;
|
||||
typedef unsigned long long _Cgo_ulonglong;
|
||||
`
|
||||
|
||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||
// with libclang, and modifies the AST to use this information. It returns a
|
||||
// newly created *ast.File that should be added to the list of to-be-parsed
|
||||
// files. If there is one or more error, it returns these in the []error slice
|
||||
// but still modifies the AST.
|
||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []error) {
|
||||
p := &cgoPackage{
|
||||
dir: dir,
|
||||
fset: fset,
|
||||
tokenFiles: map[string]*token.File{},
|
||||
missingSymbols: map[string]struct{}{},
|
||||
constants: map[string]constantInfo{},
|
||||
functions: map[string]*functionInfo{},
|
||||
globals: map[string]globalInfo{},
|
||||
typedefs: map[string]*typedefInfo{},
|
||||
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
||||
enums: map[string]enumInfo{},
|
||||
}
|
||||
|
||||
// Add a new location for the following file.
|
||||
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
|
||||
generatedTokenPos.SetLines([]int{0})
|
||||
p.generatedPos = generatedTokenPos.Pos(0)
|
||||
|
||||
// Construct a new in-memory AST for CGo declarations of this package.
|
||||
unsafeImport := &ast.ImportSpec{
|
||||
Path: &ast.BasicLit{
|
||||
ValuePos: p.generatedPos,
|
||||
Kind: token.STRING,
|
||||
Value: "\"unsafe\"",
|
||||
},
|
||||
EndPos: p.generatedPos,
|
||||
}
|
||||
p.generated = &ast.File{
|
||||
Package: p.generatedPos,
|
||||
Name: &ast.Ident{
|
||||
NamePos: p.generatedPos,
|
||||
Name: files[0].Name.Name,
|
||||
},
|
||||
Decls: []ast.Decl{
|
||||
&ast.GenDecl{
|
||||
TokPos: p.generatedPos,
|
||||
Tok: token.IMPORT,
|
||||
Specs: []ast.Spec{
|
||||
unsafeImport,
|
||||
},
|
||||
},
|
||||
},
|
||||
Imports: []*ast.ImportSpec{unsafeImport},
|
||||
}
|
||||
|
||||
// Find all C.* symbols.
|
||||
for _, f := range files {
|
||||
astutil.Apply(f, p.findMissingCGoNames, nil)
|
||||
}
|
||||
for name := range builtinAliases {
|
||||
p.missingSymbols["_Cgo_"+name] = struct{}{}
|
||||
}
|
||||
|
||||
// Find `import "C"` statements in the file.
|
||||
for _, f := range files {
|
||||
for i := 0; i < len(f.Decls); i++ {
|
||||
decl := f.Decls[i]
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(genDecl.Specs) != 1 {
|
||||
continue
|
||||
}
|
||||
spec, ok := genDecl.Specs[0].(*ast.ImportSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
path, err := strconv.Unquote(spec.Path.Value)
|
||||
if err != nil {
|
||||
panic("could not parse import path: " + err.Error())
|
||||
}
|
||||
if path != "C" {
|
||||
continue
|
||||
}
|
||||
cgoComment := genDecl.Doc.Text()
|
||||
|
||||
pos := genDecl.Pos()
|
||||
if genDecl.Doc != nil {
|
||||
pos = genDecl.Doc.Pos()
|
||||
}
|
||||
position := fset.PositionFor(pos, true)
|
||||
p.parseFragment(cgoComment+cgoTypes, cflags, position.Filename, position.Line)
|
||||
|
||||
// Remove this import declaration.
|
||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||
i--
|
||||
}
|
||||
|
||||
// Print the AST, for debugging.
|
||||
//ast.Print(fset, f)
|
||||
}
|
||||
|
||||
// Declare functions found by libclang.
|
||||
p.addFuncDecls()
|
||||
|
||||
// Declare stub function pointer values found by libclang.
|
||||
p.addFuncPtrDecls()
|
||||
|
||||
// Declare globals found by libclang.
|
||||
p.addConstDecls()
|
||||
|
||||
// Declare globals found by libclang.
|
||||
p.addVarDecls()
|
||||
|
||||
// Forward C types to Go types (like C.uint32_t -> uint32).
|
||||
p.addTypeAliases()
|
||||
|
||||
// Add type declarations for C types, declared using typedef in C.
|
||||
p.addTypedefs()
|
||||
|
||||
// Add elaborated types for C structs and unions.
|
||||
p.addElaboratedTypes()
|
||||
|
||||
// Add enum types and enum constants for C enums.
|
||||
p.addEnumTypes()
|
||||
|
||||
// Patch the AST to use the declared types and functions.
|
||||
for _, f := range files {
|
||||
astutil.Apply(f, p.walker, nil)
|
||||
}
|
||||
|
||||
// Print the newly generated in-memory AST, for debugging.
|
||||
//ast.Print(fset, p.generated)
|
||||
|
||||
return p.generated, p.errors
|
||||
}
|
||||
|
||||
// addFuncDecls adds the C function declarations found by libclang in the
|
||||
// comment above the `import "C"` statement.
|
||||
func (p *cgoPackage) addFuncDecls() {
|
||||
names := make([]string, 0, len(p.functions))
|
||||
for name := range p.functions {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
fn := p.functions[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: "C." + name,
|
||||
}
|
||||
args := make([]*ast.Field, len(fn.args))
|
||||
decl := &ast.FuncDecl{
|
||||
Name: &ast.Ident{
|
||||
NamePos: fn.pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: fn.pos,
|
||||
Params: &ast.FieldList{
|
||||
Opening: fn.pos,
|
||||
List: args,
|
||||
Closing: fn.pos,
|
||||
},
|
||||
Results: fn.results,
|
||||
},
|
||||
}
|
||||
obj.Decl = decl
|
||||
for i, arg := range fn.args {
|
||||
args[i] = &ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: fn.pos,
|
||||
Name: arg.name,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: arg.name,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: arg.typeExpr,
|
||||
}
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, decl)
|
||||
}
|
||||
}
|
||||
|
||||
// addFuncPtrDecls creates stub declarations of function pointer values. These
|
||||
// values will later be replaced with the real values in the compiler.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// var (
|
||||
// C.add unsafe.Pointer
|
||||
// C.mul unsafe.Pointer
|
||||
// // ...
|
||||
// )
|
||||
func (p *cgoPackage) addFuncPtrDecls() {
|
||||
if len(p.functions) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.VAR,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
names := make([]string, 0, len(p.functions))
|
||||
for name := range p.functions {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
fn := p.functions[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "C." + name + "$funcaddr",
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{&ast.Ident{
|
||||
NamePos: fn.pos,
|
||||
Name: "C." + name + "$funcaddr",
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
NamePos: fn.pos,
|
||||
Name: "unsafe",
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: fn.pos,
|
||||
Name: "Pointer",
|
||||
},
|
||||
},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, gen)
|
||||
}
|
||||
|
||||
// addConstDecls declares external C constants in the Go source.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// const (
|
||||
// C.CONST_INT = 5
|
||||
// C.CONST_FLOAT = 5.8
|
||||
// // ...
|
||||
// )
|
||||
func (p *cgoPackage) addConstDecls() {
|
||||
if len(p.constants) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.CONST,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
names := make([]string, 0, len(p.constants))
|
||||
for name := range p.constants {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
constVal := p.constants[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Con,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{&ast.Ident{
|
||||
NamePos: constVal.pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Values: []ast.Expr{constVal.expr},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, gen)
|
||||
}
|
||||
|
||||
// addVarDecls declares external C globals in the Go source.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// var (
|
||||
// C.globalInt int
|
||||
// C.globalBool bool
|
||||
// // ...
|
||||
// )
|
||||
func (p *cgoPackage) addVarDecls() {
|
||||
if len(p.globals) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.VAR,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
names := make([]string, 0, len(p.globals))
|
||||
for name := range p.globals {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
global := p.globals[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{&ast.Ident{
|
||||
NamePos: global.pos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: global.typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, gen)
|
||||
}
|
||||
|
||||
// addTypeAliases aliases some built-in Go types with their equivalent C types.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// type (
|
||||
// C.int8_t = int8
|
||||
// C.int16_t = int16
|
||||
// // ...
|
||||
// )
|
||||
func (p *cgoPackage) addTypeAliases() {
|
||||
aliasKeys := make([]string, 0, len(cgoAliases))
|
||||
for key := range cgoAliases {
|
||||
aliasKeys = append(aliasKeys, key)
|
||||
}
|
||||
sort.Strings(aliasKeys)
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.TYPE,
|
||||
Lparen: token.NoPos,
|
||||
Rparen: token.NoPos,
|
||||
}
|
||||
for _, typeName := range aliasKeys {
|
||||
goTypeName := cgoAliases[typeName]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: token.NoPos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Assign: p.generatedPos,
|
||||
Type: &ast.Ident{
|
||||
NamePos: token.NoPos,
|
||||
Name: goTypeName,
|
||||
},
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, gen)
|
||||
}
|
||||
|
||||
func (p *cgoPackage) addTypedefs() {
|
||||
if len(p.typedefs) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.TYPE,
|
||||
}
|
||||
names := make([]string, 0, len(p.typedefs))
|
||||
for name := range p.typedefs {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
typedef := p.typedefs[name]
|
||||
typeName := "C." + name
|
||||
isAlias := true
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
typeName = "C." + name[len("_Cgo_"):]
|
||||
isAlias = false // C.short etc. should not be aliased to the equivalent Go type (not portable)
|
||||
}
|
||||
if _, ok := cgoAliases[typeName]; ok {
|
||||
// This is a type that also exists in Go (defined in stdint.h).
|
||||
continue
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: typedef.pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typedef.typeExpr,
|
||||
}
|
||||
if isAlias {
|
||||
typeSpec.Assign = typedef.pos
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, gen)
|
||||
}
|
||||
|
||||
// addElaboratedTypes adds C elaborated types as aliases. These are the "struct
|
||||
// foo" or "union foo" types, often used in a typedef.
|
||||
//
|
||||
// See also:
|
||||
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
||||
func (p *cgoPackage) addElaboratedTypes() {
|
||||
if len(p.elaboratedTypes) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.TYPE,
|
||||
}
|
||||
names := make([]string, 0, len(p.elaboratedTypes))
|
||||
for name := range p.elaboratedTypes {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
typ := p.elaboratedTypes[name]
|
||||
typeName := "C." + name
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: typ.pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typ.typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
// If this struct has bitfields, create getters for them.
|
||||
for _, bitfield := range typ.bitfields {
|
||||
p.createBitfieldGetter(bitfield, typeName)
|
||||
p.createBitfieldSetter(bitfield, typeName)
|
||||
}
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, gen)
|
||||
}
|
||||
|
||||
// createBitfieldGetter creates a bitfield getter function like the following:
|
||||
//
|
||||
// func (s *C.struct_foo) bitfield_b() byte {
|
||||
// return (s.__bitfield_1 >> 5) & 0x1
|
||||
// }
|
||||
func (p *cgoPackage) createBitfieldGetter(bitfield bitfieldInfo, typeName string) {
|
||||
// The value to return from the getter.
|
||||
// Not complete: this is just an expression to get the complete field.
|
||||
var result ast.Expr = &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: bitfield.field.Names[0].Name,
|
||||
},
|
||||
}
|
||||
if bitfield.startBit != 0 {
|
||||
// Shift to the right by .startBit so that fields that come before are
|
||||
// shifted off.
|
||||
result = &ast.BinaryExpr{
|
||||
X: result,
|
||||
OpPos: bitfield.pos,
|
||||
Op: token.SHR,
|
||||
Y: &ast.BasicLit{
|
||||
ValuePos: bitfield.pos,
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(bitfield.startBit, 10),
|
||||
},
|
||||
}
|
||||
}
|
||||
if bitfield.endBit != 0 {
|
||||
// Mask off the high bits so that fields that come after this field are
|
||||
// masked off.
|
||||
and := (uint64(1) << uint64(bitfield.endBit-bitfield.startBit)) - 1
|
||||
result = &ast.BinaryExpr{
|
||||
X: result,
|
||||
OpPos: bitfield.pos,
|
||||
Op: token.AND,
|
||||
Y: &ast.BasicLit{
|
||||
ValuePos: bitfield.pos,
|
||||
Kind: token.INT,
|
||||
Value: "0x" + strconv.FormatUint(and, 16),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Create the getter function.
|
||||
getter := &ast.FuncDecl{
|
||||
Recv: &ast.FieldList{
|
||||
Opening: bitfield.pos,
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "s",
|
||||
Decl: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
Star: bitfield.pos,
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Closing: bitfield.pos,
|
||||
},
|
||||
Name: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "bitfield_" + bitfield.name,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: bitfield.pos,
|
||||
Params: &ast.FieldList{
|
||||
Opening: bitfield.pos,
|
||||
Closing: bitfield.pos,
|
||||
},
|
||||
Results: &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Type: bitfield.field.Type,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Body: &ast.BlockStmt{
|
||||
Lbrace: bitfield.pos,
|
||||
List: []ast.Stmt{
|
||||
&ast.ReturnStmt{
|
||||
Return: bitfield.pos,
|
||||
Results: []ast.Expr{
|
||||
result,
|
||||
},
|
||||
},
|
||||
},
|
||||
Rbrace: bitfield.pos,
|
||||
},
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, getter)
|
||||
}
|
||||
|
||||
// createBitfieldSetter creates a bitfield setter function like the following:
|
||||
//
|
||||
// func (s *C.struct_foo) set_bitfield_b(value byte) {
|
||||
// s.__bitfield_1 = s.__bitfield_1 ^ 0x60 | ((value & 1) << 5)
|
||||
// }
|
||||
//
|
||||
// Or the following:
|
||||
//
|
||||
// func (s *C.struct_foo) set_bitfield_c(value byte) {
|
||||
// s.__bitfield_1 = s.__bitfield_1 & 0x3f | (value << 6)
|
||||
// }
|
||||
func (p *cgoPackage) createBitfieldSetter(bitfield bitfieldInfo, typeName string) {
|
||||
// The full field with all bitfields.
|
||||
var field ast.Expr = &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: bitfield.field.Names[0].Name,
|
||||
},
|
||||
}
|
||||
// The value to insert into the field.
|
||||
var valueToInsert ast.Expr = &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "value",
|
||||
}
|
||||
|
||||
if bitfield.endBit != 0 {
|
||||
// Make sure the value is in range with a mask.
|
||||
valueToInsert = &ast.BinaryExpr{
|
||||
X: valueToInsert,
|
||||
OpPos: bitfield.pos,
|
||||
Op: token.AND,
|
||||
Y: &ast.BasicLit{
|
||||
ValuePos: bitfield.pos,
|
||||
Kind: token.INT,
|
||||
Value: "0x" + strconv.FormatUint((uint64(1)<<uint64(bitfield.endBit-bitfield.startBit))-1, 16),
|
||||
},
|
||||
}
|
||||
// Create a mask for the AND NOT operation.
|
||||
mask := ((uint64(1) << uint64(bitfield.endBit-bitfield.startBit)) - 1) << uint64(bitfield.startBit)
|
||||
// Zero the bits in the field that will soon be inserted.
|
||||
field = &ast.BinaryExpr{
|
||||
X: field,
|
||||
OpPos: bitfield.pos,
|
||||
Op: token.AND_NOT,
|
||||
Y: &ast.BasicLit{
|
||||
ValuePos: bitfield.pos,
|
||||
Kind: token.INT,
|
||||
Value: "0x" + strconv.FormatUint(mask, 16),
|
||||
},
|
||||
}
|
||||
} else { // bitfield.endBit == 0
|
||||
// We don't know exactly how many high bits should be zeroed. So we do
|
||||
// something different: keep the low bits with a mask and OR the new
|
||||
// value with it.
|
||||
mask := (uint64(1) << uint64(bitfield.startBit)) - 1
|
||||
// Extract the lower bits.
|
||||
field = &ast.BinaryExpr{
|
||||
X: field,
|
||||
OpPos: bitfield.pos,
|
||||
Op: token.AND,
|
||||
Y: &ast.BasicLit{
|
||||
ValuePos: bitfield.pos,
|
||||
Kind: token.INT,
|
||||
Value: "0x" + strconv.FormatUint(mask, 16),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Bitwise OR with the new value (after the new value has been shifted).
|
||||
field = &ast.BinaryExpr{
|
||||
X: field,
|
||||
OpPos: bitfield.pos,
|
||||
Op: token.OR,
|
||||
Y: &ast.BinaryExpr{
|
||||
X: valueToInsert,
|
||||
OpPos: bitfield.pos,
|
||||
Op: token.SHL,
|
||||
Y: &ast.BasicLit{
|
||||
ValuePos: bitfield.pos,
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(bitfield.startBit, 10),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create the setter function.
|
||||
setter := &ast.FuncDecl{
|
||||
Recv: &ast.FieldList{
|
||||
Opening: bitfield.pos,
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "s",
|
||||
Decl: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
Star: bitfield.pos,
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Closing: bitfield.pos,
|
||||
},
|
||||
Name: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "set_bitfield_" + bitfield.name,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: bitfield.pos,
|
||||
Params: &ast.FieldList{
|
||||
Opening: bitfield.pos,
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "value",
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
Type: bitfield.field.Type,
|
||||
},
|
||||
},
|
||||
Closing: bitfield.pos,
|
||||
},
|
||||
},
|
||||
Body: &ast.BlockStmt{
|
||||
Lbrace: bitfield.pos,
|
||||
List: []ast.Stmt{
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: "s",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: bitfield.pos,
|
||||
Name: bitfield.field.Names[0].Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
TokPos: bitfield.pos,
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{
|
||||
field,
|
||||
},
|
||||
},
|
||||
},
|
||||
Rbrace: bitfield.pos,
|
||||
},
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, setter)
|
||||
}
|
||||
|
||||
// addEnumTypes adds C enums to the AST. For example, the following C code:
|
||||
//
|
||||
// enum option {
|
||||
// optionA,
|
||||
// optionB = 5,
|
||||
// };
|
||||
//
|
||||
// is translated to the following Go code equivalent:
|
||||
//
|
||||
// type C.enum_option int32
|
||||
//
|
||||
// The constants are treated just like macros so are inserted into the AST by
|
||||
// addConstDecls.
|
||||
// See also: https://en.cppreference.com/w/c/language/enum
|
||||
func (p *cgoPackage) addEnumTypes() {
|
||||
if len(p.enums) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: token.NoPos,
|
||||
Tok: token.TYPE,
|
||||
}
|
||||
names := make([]string, 0, len(p.enums))
|
||||
for name := range p.enums {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
typ := p.enums[name]
|
||||
typeName := "C.enum_" + name
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: typ.pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typ.typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, gen)
|
||||
}
|
||||
|
||||
// findMissingCGoNames traverses the AST and finds all C.something names. Only
|
||||
// these symbols are extracted from the parsed C AST and converted to the Go
|
||||
// equivalent.
|
||||
func (p *cgoPackage) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
||||
switch node := cursor.Node().(type) {
|
||||
case *ast.SelectorExpr:
|
||||
x, ok := node.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if x.Name == "C" {
|
||||
name := node.Sel.Name
|
||||
if _, ok := builtinAliases[name]; ok {
|
||||
name = "_Cgo_" + name
|
||||
}
|
||||
p.missingSymbols[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
||||
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
||||
// used in the middle of a name) so in practice all C identifiers live in a
|
||||
// separate namespace (no _Cgo_ hacks like in gc).
|
||||
func (p *cgoPackage) walker(cursor *astutil.Cursor) bool {
|
||||
switch node := cursor.Node().(type) {
|
||||
case *ast.CallExpr:
|
||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
x, ok := fun.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := p.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
||||
node.Fun = &ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: "C." + fun.Sel.Name,
|
||||
}
|
||||
}
|
||||
case *ast.SelectorExpr:
|
||||
x, ok := node.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if x.Name == "C" {
|
||||
name := "C." + node.Sel.Name
|
||||
if _, ok := p.functions[node.Sel.Name]; ok {
|
||||
name += "$funcaddr"
|
||||
}
|
||||
cursor.Replace(&ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
-12
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -16,16 +15,6 @@ var commands = map[string][]string{
|
||||
"wasm-ld": {"wasm-ld-8", "wasm-ld"},
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Add the path to a Homebrew-installed LLVM 8 for ease of use (no need to
|
||||
// manually set $PATH).
|
||||
if runtime.GOOS == "darwin" {
|
||||
commands["clang"] = append(commands["clang"], "/usr/local/opt/llvm/bin/clang-8")
|
||||
commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/opt/llvm/bin/ld.lld")
|
||||
commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/opt/llvm/bin/wasm-ld")
|
||||
}
|
||||
}
|
||||
|
||||
func execCommand(cmdNames []string, args ...string) error {
|
||||
for _, cmdName := range cmdNames {
|
||||
cmd := exec.Command(cmdName, args...)
|
||||
@@ -37,7 +26,6 @@ func execCommand(cmdNames []string, args ...string) error {
|
||||
// this command was not found, try the next
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+3
-8
@@ -42,7 +42,7 @@ func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Valu
|
||||
|
||||
// Fail: this is a nil pointer, exit with a panic.
|
||||
c.builder.SetInsertPointAtEnd(faultBlock)
|
||||
c.createRuntimeCall("lookupPanic", nil, "")
|
||||
c.createRuntimeCall("lookuppanic", nil, "")
|
||||
c.builder.CreateUnreachable()
|
||||
|
||||
// Ok: this is a valid pointer.
|
||||
@@ -103,7 +103,7 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V
|
||||
|
||||
// Fail: this is a nil pointer, exit with a panic.
|
||||
c.builder.SetInsertPointAtEnd(faultBlock)
|
||||
c.createRuntimeCall("slicePanic", nil, "")
|
||||
c.createRuntimeCall("slicepanic", nil, "")
|
||||
c.builder.CreateUnreachable()
|
||||
|
||||
// Ok: this is a valid pointer.
|
||||
@@ -114,11 +114,6 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V
|
||||
// has no effect in well-behaved programs, but makes sure no uncaught nil
|
||||
// pointer dereferences exist in valid Go code.
|
||||
func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string) {
|
||||
// Check whether we need to emit this check at all.
|
||||
if !ptr.IsAGlobalValue().IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether this is a nil pointer.
|
||||
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".nil")
|
||||
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".next")
|
||||
@@ -146,7 +141,7 @@ func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string
|
||||
|
||||
// Fail: this is a nil pointer, exit with a panic.
|
||||
c.builder.SetInsertPointAtEnd(faultBlock)
|
||||
c.createRuntimeCall("nilPanic", nil, "")
|
||||
c.createRuntimeCall("nilpanic", nil, "")
|
||||
c.builder.CreateUnreachable()
|
||||
|
||||
// Ok: this is a valid pointer.
|
||||
|
||||
+1
-41
@@ -38,7 +38,7 @@ func (c *Compiler) createCall(fn llvm.Value, args []llvm.Value, name string) llv
|
||||
}
|
||||
|
||||
// Expand an argument type to a list that can be used in a function call
|
||||
// parameter list.
|
||||
// paramter list.
|
||||
func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
@@ -55,25 +55,6 @@ func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
|
||||
}
|
||||
}
|
||||
|
||||
// Expand an argument type to a list of offsets from the start of the object.
|
||||
// Used together with expandFormalParam to get the offset of each value from the
|
||||
// start of the non-expanded value.
|
||||
func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fields := c.flattenAggregateTypeOffsets(t)
|
||||
if len(fields) <= MaxFieldsPerParam {
|
||||
return fields
|
||||
} else {
|
||||
// failed to lower
|
||||
return []uint64{0}
|
||||
}
|
||||
default:
|
||||
// TODO: split small arrays
|
||||
return []uint64{0}
|
||||
}
|
||||
}
|
||||
|
||||
// Equivalent of expandFormalParamType for parameter values.
|
||||
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||
switch v.Type().TypeKind() {
|
||||
@@ -111,27 +92,6 @@ func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the offsets from the start of the object if this object type were
|
||||
// flattened like in flattenAggregate. Used together with flattenAggregate to
|
||||
// know the start indices of each value in the non-flattened object.
|
||||
func (c *Compiler) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
||||
switch t.TypeKind() {
|
||||
case llvm.StructTypeKind:
|
||||
fields := make([]uint64, 0, t.StructElementTypesCount())
|
||||
for fieldIndex, field := range t.StructElementTypes() {
|
||||
suboffsets := c.flattenAggregateTypeOffsets(field)
|
||||
offset := c.targetData.ElementOffset(t, fieldIndex)
|
||||
for i := range suboffsets {
|
||||
suboffsets[i] += offset
|
||||
}
|
||||
fields = append(fields, suboffsets...)
|
||||
}
|
||||
return fields
|
||||
default:
|
||||
return []uint64{0}
|
||||
}
|
||||
}
|
||||
|
||||
// Break down a struct into its elementary types for argument passing. The value
|
||||
// equivalent of flattenAggregateType
|
||||
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
|
||||
|
||||
+27
-180
@@ -4,7 +4,6 @@ package compiler
|
||||
// or pseudo-operations that are lowered during goroutine lowering.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
@@ -13,61 +12,58 @@ import (
|
||||
|
||||
// emitMakeChan returns a new channel value for the given channel type.
|
||||
func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
||||
chanType := c.getLLVMType(expr.Type())
|
||||
size := c.targetData.TypeAllocSize(chanType.ElementType())
|
||||
chanType := c.mod.GetTypeByName("runtime.channel")
|
||||
size := c.targetData.TypeAllocSize(chanType)
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc")
|
||||
ptr = c.builder.CreateBitCast(ptr, chanType, "chan")
|
||||
// Set the elementSize field
|
||||
elementSizePtr := c.builder.CreateGEP(ptr, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
}, "")
|
||||
elementSize := c.targetData.TypeAllocSize(c.getLLVMType(expr.Type().(*types.Chan).Elem()))
|
||||
if elementSize > 0xffff {
|
||||
return ptr, c.makeError(expr.Pos(), fmt.Sprintf("element size is %d bytes, which is bigger than the maximum of %d bytes", elementSize, 0xffff))
|
||||
}
|
||||
elementSizeValue := llvm.ConstInt(c.ctx.Int16Type(), elementSize, false)
|
||||
c.builder.CreateStore(elementSizeValue, elementSizePtr)
|
||||
ptr = c.builder.CreateBitCast(ptr, llvm.PointerType(chanType, 0), "chan")
|
||||
return ptr, nil
|
||||
}
|
||||
|
||||
// emitChanSend emits a pseudo chan send operation. It is lowered to the actual
|
||||
// channel send operation during goroutine lowering.
|
||||
func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) {
|
||||
valueType := c.getLLVMType(instr.X.Type())
|
||||
ch := c.getValue(frame, instr.Chan)
|
||||
chanValue := c.getValue(frame, instr.X)
|
||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false)
|
||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||
|
||||
// store value-to-send
|
||||
valueType := c.getLLVMType(instr.X.Type())
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value")
|
||||
c.builder.SetInsertPointBefore(coroutine.InstructionParent().Parent().EntryBasicBlock().FirstInstruction())
|
||||
valueAlloca := c.builder.CreateAlloca(valueType, "chan.value")
|
||||
c.builder.SetInsertPointBefore(coroutine)
|
||||
c.builder.SetInsertPointAtEnd(coroutine.InstructionParent())
|
||||
c.builder.CreateStore(chanValue, valueAlloca)
|
||||
valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr")
|
||||
|
||||
// Do the send.
|
||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast}, "")
|
||||
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
||||
|
||||
// End the lifetime of the alloca.
|
||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
// Make sure CoroSplit includes the alloca in the coroutine frame.
|
||||
// This is a bit dirty, but it works (at least in LLVM 8).
|
||||
valueSizeI64 := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(chanValue.Type()), false)
|
||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{valueSizeI64, valueAllocaCast}, "")
|
||||
}
|
||||
|
||||
// emitChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||
// actual channel receive operation during goroutine lowering.
|
||||
func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
|
||||
valueType := c.getLLVMType(unop.X.Type().(*types.Chan).Elem())
|
||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
||||
ch := c.getValue(frame, unop.X)
|
||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||
|
||||
// Allocate memory to receive into.
|
||||
valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value")
|
||||
c.builder.SetInsertPointBefore(coroutine.InstructionParent().Parent().EntryBasicBlock().FirstInstruction())
|
||||
valueAlloca := c.builder.CreateAlloca(valueType, "chan.value")
|
||||
c.builder.SetInsertPointBefore(coroutine)
|
||||
c.builder.SetInsertPointAtEnd(coroutine.InstructionParent())
|
||||
valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr")
|
||||
|
||||
// Do the receive.
|
||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast}, "")
|
||||
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
||||
received := c.builder.CreateLoad(valueAlloca, "chan.received")
|
||||
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||
|
||||
if unop.CommaOk {
|
||||
commaOk := c.createRuntimeCall("getTaskPromiseData", []llvm.Value{coroutine}, "chan.commaOk.wide")
|
||||
commaOk = c.builder.CreateTrunc(commaOk, c.ctx.Int1Type(), "chan.commaOk")
|
||||
@@ -82,157 +78,8 @@ func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
|
||||
|
||||
// emitChanClose closes the given channel.
|
||||
func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) {
|
||||
valueType := c.getLLVMType(param.Type().(*types.Chan).Elem())
|
||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
||||
ch := c.getValue(frame, param)
|
||||
c.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
||||
}
|
||||
|
||||
// emitSelect emits all IR necessary for a select statements. That's a
|
||||
// non-trivial amount of code because select is very complex to implement.
|
||||
func (c *Compiler) emitSelect(frame *Frame, expr *ssa.Select) llvm.Value {
|
||||
if len(expr.States) == 0 {
|
||||
// Shortcuts for some simple selects.
|
||||
llvmType := c.getLLVMType(expr.Type())
|
||||
if expr.Blocking {
|
||||
// Blocks forever:
|
||||
// select {}
|
||||
c.createRuntimeCall("deadlockStub", nil, "")
|
||||
return llvm.Undef(llvmType)
|
||||
} else {
|
||||
// No-op:
|
||||
// select {
|
||||
// default:
|
||||
// }
|
||||
retval := llvm.Undef(llvmType)
|
||||
retval = c.builder.CreateInsertValue(retval, llvm.ConstInt(c.intType, 0xffffffffffffffff, true), 0, "")
|
||||
return retval // {-1, false}
|
||||
}
|
||||
}
|
||||
|
||||
// This code create a (stack-allocated) slice containing all the select
|
||||
// cases and then calls runtime.chanSelect to perform the actual select
|
||||
// statement.
|
||||
// Simple selects (blocking and with just one case) are already transformed
|
||||
// into regular chan operations during SSA construction so we don't have to
|
||||
// optimize such small selects.
|
||||
|
||||
// Go through all the cases. Create the selectStates slice and and
|
||||
// determine the receive buffer size and alignment.
|
||||
recvbufSize := uint64(0)
|
||||
recvbufAlign := 0
|
||||
hasReceives := false
|
||||
var selectStates []llvm.Value
|
||||
chanSelectStateType := c.getLLVMRuntimeType("chanSelectState")
|
||||
for _, state := range expr.States {
|
||||
ch := c.getValue(frame, state.Chan)
|
||||
selectState := c.getZeroValue(chanSelectStateType)
|
||||
selectState = c.builder.CreateInsertValue(selectState, ch, 0, "")
|
||||
switch state.Dir {
|
||||
case types.RecvOnly:
|
||||
// Make sure the receive buffer is big enough and has the correct alignment.
|
||||
llvmType := c.getLLVMType(state.Chan.Type().(*types.Chan).Elem())
|
||||
if size := c.targetData.TypeAllocSize(llvmType); size > recvbufSize {
|
||||
recvbufSize = size
|
||||
}
|
||||
if align := c.targetData.ABITypeAlignment(llvmType); align > recvbufAlign {
|
||||
recvbufAlign = align
|
||||
}
|
||||
hasReceives = true
|
||||
case types.SendOnly:
|
||||
// Store this value in an alloca and put a pointer to this alloca
|
||||
// in the send state.
|
||||
sendValue := c.getValue(frame, state.Send)
|
||||
alloca := c.createEntryBlockAlloca(sendValue.Type(), "select.send.value")
|
||||
c.builder.CreateStore(sendValue, alloca)
|
||||
ptr := c.builder.CreateBitCast(alloca, c.i8ptrType, "")
|
||||
selectState = c.builder.CreateInsertValue(selectState, ptr, 1, "")
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
selectStates = append(selectStates, selectState)
|
||||
}
|
||||
|
||||
// Create a receive buffer, where the received value will be stored.
|
||||
recvbuf := llvm.Undef(c.i8ptrType)
|
||||
if hasReceives {
|
||||
allocaType := llvm.ArrayType(c.ctx.Int8Type(), int(recvbufSize))
|
||||
recvbufAlloca := c.builder.CreateAlloca(allocaType, "select.recvbuf.alloca")
|
||||
recvbufAlloca.SetAlignment(recvbufAlign)
|
||||
recvbuf = c.builder.CreateGEP(recvbufAlloca, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
}, "select.recvbuf")
|
||||
}
|
||||
|
||||
// Create the states slice (allocated on the stack).
|
||||
statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates))
|
||||
statesAlloca := c.builder.CreateAlloca(statesAllocaType, "select.states.alloca")
|
||||
for i, state := range selectStates {
|
||||
// Set each slice element to the appropriate channel.
|
||||
gep := c.builder.CreateGEP(statesAlloca, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false),
|
||||
}, "")
|
||||
c.builder.CreateStore(state, gep)
|
||||
}
|
||||
statesPtr := c.builder.CreateGEP(statesAlloca, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
}, "select.states")
|
||||
statesLen := llvm.ConstInt(c.uintptrType, uint64(len(selectStates)), false)
|
||||
|
||||
// Convert the 'blocking' flag on this select into a LLVM value.
|
||||
blockingInt := uint64(0)
|
||||
if expr.Blocking {
|
||||
blockingInt = 1
|
||||
}
|
||||
blockingValue := llvm.ConstInt(c.ctx.Int1Type(), blockingInt, false)
|
||||
|
||||
// Do the select in the runtime.
|
||||
results := c.createRuntimeCall("chanSelect", []llvm.Value{
|
||||
recvbuf,
|
||||
statesPtr, statesLen, statesLen, // []chanSelectState
|
||||
blockingValue,
|
||||
}, "")
|
||||
|
||||
// The result value does not include all the possible received values,
|
||||
// because we can't load them in advance. Instead, the *ssa.Extract
|
||||
// instruction will treat a *ssa.Select specially and load it there inline.
|
||||
// Store the receive alloca in a sidetable until we hit this extract
|
||||
// instruction.
|
||||
if frame.selectRecvBuf == nil {
|
||||
frame.selectRecvBuf = make(map[*ssa.Select]llvm.Value)
|
||||
}
|
||||
frame.selectRecvBuf[expr] = recvbuf
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// getChanSelectResult returns the special values from a *ssa.Extract expression
|
||||
// when extracting a value from a select statement (*ssa.Select). Because
|
||||
// *ssa.Select cannot load all values in advance, it does this later in the
|
||||
// *ssa.Extract expression.
|
||||
func (c *Compiler) getChanSelectResult(frame *Frame, expr *ssa.Extract) llvm.Value {
|
||||
if expr.Index == 0 {
|
||||
// index
|
||||
value := c.getValue(frame, expr.Tuple)
|
||||
index := c.builder.CreateExtractValue(value, expr.Index, "")
|
||||
if index.Type().IntTypeWidth() < c.intType.IntTypeWidth() {
|
||||
index = c.builder.CreateSExt(index, c.intType, "")
|
||||
}
|
||||
return index
|
||||
} else if expr.Index == 1 {
|
||||
// comma-ok
|
||||
value := c.getValue(frame, expr.Tuple)
|
||||
return c.builder.CreateExtractValue(value, expr.Index, "")
|
||||
} else {
|
||||
// Select statements are (index, ok, ...) where ... is a number of
|
||||
// received values, depending on how many receive statements there
|
||||
// are. They are all combined into one alloca (because only one
|
||||
// receive can proceed at a time) so we'll get that alloca, bitcast
|
||||
// it to the correct type, and dereference it.
|
||||
recvbuf := frame.selectRecvBuf[expr.Tuple.(*ssa.Select)]
|
||||
typ := llvm.PointerType(c.getLLVMType(expr.Type()), 0)
|
||||
ptr := c.builder.CreateBitCast(recvbuf, typ, "")
|
||||
return c.builder.CreateLoad(ptr, "")
|
||||
}
|
||||
c.createRuntimeCall("chanClose", []llvm.Value{ch, valueSize}, "")
|
||||
}
|
||||
|
||||
+162
-348
@@ -3,7 +3,6 @@ package compiler
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
@@ -27,33 +26,22 @@ func init() {
|
||||
llvm.InitializeAllAsmPrinters()
|
||||
}
|
||||
|
||||
// The TinyGo import path.
|
||||
const tinygoPath = "github.com/tinygo-org/tinygo"
|
||||
|
||||
// Configure the compiler.
|
||||
type Config struct {
|
||||
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
||||
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
|
||||
Features []string // LLVM CPU features
|
||||
GOOS string //
|
||||
GOARCH string //
|
||||
GC string // garbage collection strategy
|
||||
PanicStrategy string // panic strategy ("abort" or "trap")
|
||||
CFlags []string // cflags to pass to cgo
|
||||
LDFlags []string // ldflags to pass to cgo
|
||||
ClangHeaders string // Clang built-in header include path
|
||||
DumpSSA bool // dump Go SSA, for compiler debugging
|
||||
Debug bool // add debug symbols for gdb
|
||||
GOROOT string // GOROOT
|
||||
TINYGOROOT string // GOROOT for TinyGo
|
||||
GOPATH string // GOPATH, like `go env GOPATH`
|
||||
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
|
||||
TestConfig TestConfig
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
CompileTestBinary bool
|
||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
@@ -64,6 +52,7 @@ type Compiler struct {
|
||||
dibuilder *llvm.DIBuilder
|
||||
cu llvm.Metadata
|
||||
difiles map[string]llvm.Metadata
|
||||
ditypes map[string]llvm.Metadata
|
||||
machine llvm.TargetMachine
|
||||
targetData llvm.TargetData
|
||||
intType llvm.Type
|
||||
@@ -74,7 +63,6 @@ type Compiler struct {
|
||||
interfaceInvokeWrappers []interfaceInvokeWrapper
|
||||
ir *ir.Program
|
||||
diagnostics []error
|
||||
astComments map[string]*ast.CommentGroup
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
@@ -91,7 +79,6 @@ type Frame struct {
|
||||
deferFuncs map[*ir.Function]int
|
||||
deferInvokeFuncs map[string]int
|
||||
deferClosureFuncs map[*ir.Function]int
|
||||
selectRecvBuf map[*ssa.Select]llvm.Value
|
||||
}
|
||||
|
||||
type Phi struct {
|
||||
@@ -109,17 +96,14 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
|
||||
c := &Compiler{
|
||||
Config: config,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[string]llvm.Metadata),
|
||||
}
|
||||
|
||||
target, err := llvm.GetTargetFromTriple(config.Triple)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
features := ""
|
||||
if len(config.Features) > 0 {
|
||||
features = strings.Join(config.Features, `,`)
|
||||
}
|
||||
c.machine = target.CreateTargetMachine(config.Triple, config.CPU, features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault)
|
||||
c.machine = target.CreateTargetMachine(config.Triple, config.CPU, "", llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault)
|
||||
c.targetData = c.machine.CreateTargetData()
|
||||
|
||||
c.ctx = llvm.NewContext()
|
||||
@@ -167,10 +151,11 @@ func (c *Compiler) TargetData() llvm.TargetData {
|
||||
|
||||
// selectGC picks an appropriate GC strategy if none was provided.
|
||||
func (c *Compiler) selectGC() string {
|
||||
if c.GC != "" {
|
||||
return c.GC
|
||||
gc := c.GC
|
||||
if gc == "" {
|
||||
gc = "dumb"
|
||||
}
|
||||
return "conservative"
|
||||
return gc
|
||||
}
|
||||
|
||||
// Compile the given package path or .go file path. Return an error when this
|
||||
@@ -210,29 +195,22 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
Compiler: "gc", // must be one of the recognized compilers
|
||||
BuildTags: append([]string{"tinygo", "gc." + c.selectGC()}, c.BuildTags...),
|
||||
},
|
||||
OverlayPath: func(path string) string {
|
||||
// Return the (overlay) import path when it should be overlaid, and
|
||||
// "" if it should not.
|
||||
if strings.HasPrefix(path, tinygoPath+"/src/") {
|
||||
// Avoid issues with packages that are imported twice, one from
|
||||
// GOPATH and one from TINYGOPATH.
|
||||
path = path[len(tinygoPath+"/src/"):]
|
||||
}
|
||||
ShouldOverlay: func(path string) bool {
|
||||
switch path {
|
||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync", "testing":
|
||||
return path
|
||||
case "machine", "os", "reflect", "runtime", "sync":
|
||||
return true
|
||||
default:
|
||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||
return path
|
||||
return true
|
||||
} else if path == "syscall" {
|
||||
for _, tag := range c.BuildTags {
|
||||
if tag == "avr" || tag == "cortexm" || tag == "darwin" || tag == "riscv" {
|
||||
return path
|
||||
if tag == "avr" || tag == "cortexm" || tag == "darwin" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return false
|
||||
},
|
||||
TypeChecker: types.Config{
|
||||
Sizes: &StdSizes{
|
||||
@@ -241,12 +219,10 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
||||
},
|
||||
},
|
||||
Dir: wd,
|
||||
TINYGOROOT: c.TINYGOROOT,
|
||||
CFlags: c.CFlags,
|
||||
ClangHeaders: c.ClangHeaders,
|
||||
Dir: wd,
|
||||
TINYGOROOT: c.TINYGOROOT,
|
||||
CFlags: c.CFlags,
|
||||
}
|
||||
|
||||
if strings.HasSuffix(mainPath, ".go") {
|
||||
_, err = lprogram.ImportFile(mainPath)
|
||||
if err != nil {
|
||||
@@ -258,13 +234,12 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
return []error{err}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = lprogram.Import("runtime", "")
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
err = lprogram.Parse(c.TestConfig.CompileTestBinary)
|
||||
err = lprogram.Parse()
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
@@ -277,7 +252,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
// Initialize debug information.
|
||||
if c.Debug {
|
||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||
Language: llvm.DW_LANG_Go,
|
||||
File: mainPath,
|
||||
Dir: "",
|
||||
Producer: "TinyGo",
|
||||
@@ -287,7 +262,39 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
|
||||
var frames []*Frame
|
||||
|
||||
c.loadASTComments(lprogram)
|
||||
// Declare all named struct types.
|
||||
for _, t := range c.ir.NamedTypes {
|
||||
if named, ok := t.Type.Type().(*types.Named); ok {
|
||||
if _, ok := named.Underlying().(*types.Struct); ok {
|
||||
t.LLVMType = c.ctx.StructCreateNamed(named.Obj().Pkg().Path() + "." + named.Obj().Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define all named struct types.
|
||||
for _, t := range c.ir.NamedTypes {
|
||||
if named, ok := t.Type.Type().(*types.Named); ok {
|
||||
if st, ok := named.Underlying().(*types.Struct); ok {
|
||||
llvmType := c.getLLVMType(st)
|
||||
t.LLVMType.StructSetBody(llvmType.StructElementTypes(), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Declare all globals.
|
||||
for _, g := range c.ir.Globals {
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
llvmType := c.getLLVMType(typ)
|
||||
global := c.mod.NamedGlobal(g.LinkName())
|
||||
if global.IsNil() {
|
||||
global = llvm.AddGlobal(c.mod, llvmType, g.LinkName())
|
||||
}
|
||||
g.LLVMGlobal = global
|
||||
if !g.IsExtern() {
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetInitializer(c.getZeroValue(llvmType))
|
||||
}
|
||||
}
|
||||
|
||||
// Declare all functions.
|
||||
for _, f := range c.ir.Functions {
|
||||
@@ -361,15 +368,6 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
// See emitNilCheck in asserts.go.
|
||||
c.mod.NamedFunction("runtime.isnil").AddAttributeAtIndex(1, nocapture)
|
||||
|
||||
// This function is necessary for tracking pointers on the stack in a
|
||||
// portable way (see gc.go). Indicate to the optimizer that the only thing
|
||||
// we'll do is read the pointer.
|
||||
trackPointer := c.mod.NamedFunction("runtime.trackPointer")
|
||||
if !trackPointer.IsNil() {
|
||||
trackPointer.AddAttributeAtIndex(1, nocapture)
|
||||
trackPointer.AddAttributeAtIndex(1, readonly)
|
||||
}
|
||||
|
||||
// Memory copy operations do not capture pointers, even though some weird
|
||||
// pointer arithmetic is happening in the Go implementation.
|
||||
for _, fnName := range []string{"runtime.memcpy", "runtime.memmove"} {
|
||||
@@ -389,35 +387,12 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||
}),
|
||||
)
|
||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
c.ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
||||
llvm.GlobalContext().MDString("Dwarf Version"),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||
}),
|
||||
)
|
||||
c.dibuilder.Finalize()
|
||||
}
|
||||
|
||||
return c.diagnostics
|
||||
}
|
||||
|
||||
// getRuntimeType obtains a named type from the runtime package and returns it
|
||||
// as a Go type.
|
||||
func (c *Compiler) getRuntimeType(name string) types.Type {
|
||||
return c.ir.Program.ImportedPackage("runtime").Type(name).Type()
|
||||
}
|
||||
|
||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||
// it as a LLVM type, creating it if necessary. It is a shorthand for
|
||||
// getLLVMType(getRuntimeType(name)).
|
||||
func (c *Compiler) getLLVMRuntimeType(name string) llvm.Type {
|
||||
return c.getLLVMType(c.getRuntimeType(name))
|
||||
}
|
||||
|
||||
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
||||
// named struct types (or Go types implemented as named LLVM structs such as
|
||||
// strings) it also creates it first if necessary.
|
||||
func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
||||
switch typ := goType.(type) {
|
||||
case *types.Array:
|
||||
@@ -446,7 +421,7 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
||||
case types.Complex128:
|
||||
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
||||
case types.String, types.UntypedString:
|
||||
return c.getLLVMRuntimeType("_string")
|
||||
return c.mod.GetTypeByName("runtime._string")
|
||||
case types.Uintptr:
|
||||
return c.uintptrType
|
||||
case types.UnsafePointer:
|
||||
@@ -455,23 +430,16 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
||||
panic("unknown basic type: " + typ.String())
|
||||
}
|
||||
case *types.Chan:
|
||||
return llvm.PointerType(c.getLLVMRuntimeType("channel"), 0)
|
||||
return llvm.PointerType(c.mod.GetTypeByName("runtime.channel"), 0)
|
||||
case *types.Interface:
|
||||
return c.getLLVMRuntimeType("_interface")
|
||||
return c.mod.GetTypeByName("runtime._interface")
|
||||
case *types.Map:
|
||||
return llvm.PointerType(c.getLLVMRuntimeType("hashmap"), 0)
|
||||
return llvm.PointerType(c.mod.GetTypeByName("runtime.hashmap"), 0)
|
||||
case *types.Named:
|
||||
if st, ok := typ.Underlying().(*types.Struct); ok {
|
||||
// Structs are a special case. While other named types are ignored
|
||||
// in LLVM IR, named structs are implemented as named structs in
|
||||
// LLVM. This is because it is otherwise impossible to create
|
||||
// self-referencing types such as linked lists.
|
||||
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
|
||||
llvmType := c.mod.GetTypeByName(llvmName)
|
||||
if _, ok := typ.Underlying().(*types.Struct); ok {
|
||||
llvmType := c.mod.GetTypeByName(typ.Obj().Pkg().Path() + "." + typ.Obj().Name())
|
||||
if llvmType.IsNil() {
|
||||
llvmType = c.ctx.StructCreateNamed(llvmName)
|
||||
underlying := c.getLLVMType(st)
|
||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||
panic("underlying type not found: " + typ.Obj().Pkg().Path() + "." + typ.Obj().Name())
|
||||
}
|
||||
return llvmType
|
||||
}
|
||||
@@ -582,166 +550,39 @@ func isPointer(typ types.Type) bool {
|
||||
|
||||
// Get the DWARF type for this Go type.
|
||||
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
|
||||
llvmType := c.getLLVMType(typ)
|
||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||
switch typ := typ.(type) {
|
||||
case *types.Array:
|
||||
return c.dibuilder.CreateArrayType(llvm.DIArrayType{
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
ElementType: c.getDIType(typ.Elem()),
|
||||
Subscripts: []llvm.DISubrange{
|
||||
llvm.DISubrange{
|
||||
Lo: 0,
|
||||
Count: typ.Len(),
|
||||
},
|
||||
},
|
||||
})
|
||||
case *types.Basic:
|
||||
name := typ.String()
|
||||
if dityp, ok := c.ditypes[name]; ok {
|
||||
return dityp
|
||||
} else {
|
||||
llvmType := c.getLLVMType(typ)
|
||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||
var encoding llvm.DwarfTypeEncoding
|
||||
if typ.Info()&types.IsBoolean != 0 {
|
||||
encoding = llvm.DW_ATE_boolean
|
||||
} else if typ.Info()&types.IsFloat != 0 {
|
||||
encoding = llvm.DW_ATE_float
|
||||
} else if typ.Info()&types.IsComplex != 0 {
|
||||
encoding = llvm.DW_ATE_complex_float
|
||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||
encoding = llvm.DW_ATE_unsigned
|
||||
} else if typ.Info()&types.IsInteger != 0 {
|
||||
encoding = llvm.DW_ATE_signed
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
return c.dibuilder.CreatePointerType(llvm.DIPointerType{
|
||||
Name: "unsafe.Pointer",
|
||||
SizeInBits: c.targetData.TypeAllocSize(llvmType) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
AddressSpace: 0,
|
||||
})
|
||||
} else if typ.Info()&types.IsString != 0 {
|
||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||
Name: "string",
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
Elements: []llvm.Metadata{
|
||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: "ptr",
|
||||
SizeInBits: c.targetData.TypeAllocSize(c.i8ptrType) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.i8ptrType)) * 8,
|
||||
OffsetInBits: 0,
|
||||
Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])),
|
||||
}),
|
||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: "len",
|
||||
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
||||
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
||||
Type: c.getDIType(types.Typ[types.Uintptr]),
|
||||
}),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
panic("unknown basic type")
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
if typ.Info()&types.IsBoolean != 0 {
|
||||
encoding = llvm.DW_ATE_boolean
|
||||
} else if typ.Info()&types.IsFloat != 0 {
|
||||
encoding = llvm.DW_ATE_float
|
||||
} else if typ.Info()&types.IsComplex != 0 {
|
||||
encoding = llvm.DW_ATE_complex_float
|
||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||
encoding = llvm.DW_ATE_unsigned
|
||||
} else if typ.Info()&types.IsInteger != 0 {
|
||||
encoding = llvm.DW_ATE_signed
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
encoding = llvm.DW_ATE_address
|
||||
}
|
||||
case *types.Pointer:
|
||||
encoding = llvm.DW_ATE_address
|
||||
}
|
||||
return c.dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||
Name: typ.String(),
|
||||
// TODO: other types
|
||||
dityp = c.dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||
Name: name,
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
Encoding: encoding,
|
||||
})
|
||||
case *types.Chan:
|
||||
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
|
||||
case *types.Interface:
|
||||
return c.getDIType(c.ir.Program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
|
||||
case *types.Map:
|
||||
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
|
||||
case *types.Named:
|
||||
return c.dibuilder.CreateTypedef(llvm.DITypedef{
|
||||
Type: c.getDIType(typ.Underlying()),
|
||||
Name: typ.String(),
|
||||
})
|
||||
case *types.Pointer:
|
||||
return c.dibuilder.CreatePointerType(llvm.DIPointerType{
|
||||
Pointee: c.getDIType(typ.Elem()),
|
||||
SizeInBits: c.targetData.TypeAllocSize(llvmType) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
AddressSpace: 0,
|
||||
})
|
||||
case *types.Signature:
|
||||
// actually a closure
|
||||
fields := llvmType.StructElementTypes()
|
||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
Elements: []llvm.Metadata{
|
||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: "context",
|
||||
SizeInBits: c.targetData.TypeAllocSize(fields[1]) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[1])) * 8,
|
||||
OffsetInBits: 0,
|
||||
Type: c.getDIType(types.Typ[types.UnsafePointer]),
|
||||
}),
|
||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: "fn",
|
||||
SizeInBits: c.targetData.TypeAllocSize(fields[0]) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
|
||||
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
||||
Type: c.getDIType(types.Typ[types.UnsafePointer]),
|
||||
}),
|
||||
},
|
||||
})
|
||||
case *types.Slice:
|
||||
fields := llvmType.StructElementTypes()
|
||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||
Name: typ.String(),
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
Elements: []llvm.Metadata{
|
||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: "ptr",
|
||||
SizeInBits: c.targetData.TypeAllocSize(fields[0]) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
|
||||
OffsetInBits: 0,
|
||||
Type: c.getDIType(types.NewPointer(typ.Elem())),
|
||||
}),
|
||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: "len",
|
||||
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
||||
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
||||
Type: c.getDIType(types.Typ[types.Uintptr]),
|
||||
}),
|
||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: "cap",
|
||||
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
||||
OffsetInBits: c.targetData.ElementOffset(llvmType, 2) * 8,
|
||||
Type: c.getDIType(types.Typ[types.Uintptr]),
|
||||
}),
|
||||
},
|
||||
})
|
||||
case *types.Struct:
|
||||
elements := make([]llvm.Metadata, typ.NumFields())
|
||||
for i := range elements {
|
||||
field := typ.Field(i)
|
||||
fieldType := field.Type()
|
||||
if _, ok := fieldType.Underlying().(*types.Pointer); ok {
|
||||
// XXX hack to avoid recursive types
|
||||
fieldType = types.Typ[types.UnsafePointer]
|
||||
}
|
||||
llvmField := c.getLLVMType(fieldType)
|
||||
elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||
Name: field.Name(),
|
||||
SizeInBits: c.targetData.TypeAllocSize(llvmField) * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmField)) * 8,
|
||||
OffsetInBits: c.targetData.ElementOffset(llvmType, i) * 8,
|
||||
Type: c.getDIType(fieldType),
|
||||
})
|
||||
}
|
||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||
Elements: elements,
|
||||
})
|
||||
default:
|
||||
panic("unknown type while generating DWARF debug type: " + typ.String())
|
||||
c.ditypes[name] = dityp
|
||||
return dityp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,10 +688,6 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||
if c.DumpSSA {
|
||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||
}
|
||||
if !frame.fn.LLVMFn.IsDeclaration() {
|
||||
c.addError(frame.fn.Pos(), "function is already defined:"+frame.fn.LLVMFn.Name())
|
||||
return
|
||||
}
|
||||
if !frame.fn.IsExported() {
|
||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
||||
@@ -859,18 +696,6 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||
}
|
||||
|
||||
// Some functions have a pragma controlling the inlining level.
|
||||
switch frame.fn.Inline() {
|
||||
case ir.InlineHint:
|
||||
// Add LLVM inline hint to functions with //go:inline pragma.
|
||||
inline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
|
||||
frame.fn.LLVMFn.AddFunctionAttr(inline)
|
||||
case ir.InlineNone:
|
||||
// Add LLVM attribute to always avoid inlining this function.
|
||||
noinline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0)
|
||||
frame.fn.LLVMFn.AddFunctionAttr(noinline)
|
||||
}
|
||||
|
||||
// Add debug info, if needed.
|
||||
if c.Debug {
|
||||
if frame.fn.Synthetic == "package initializer" {
|
||||
@@ -908,30 +733,15 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
||||
// Add debug information to this parameter (if available)
|
||||
if c.Debug && frame.fn.Syntax() != nil {
|
||||
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
|
||||
diType := c.getDIType(param.Type())
|
||||
dbgParam := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
|
||||
c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
|
||||
Name: param.Name(),
|
||||
File: c.difiles[pos.Filename],
|
||||
Line: pos.Line,
|
||||
Type: diType,
|
||||
Type: c.getDIType(param.Type()),
|
||||
AlwaysPreserve: true,
|
||||
ArgNo: i + 1,
|
||||
})
|
||||
loc := c.builder.GetCurrentDebugLocation()
|
||||
if len(fields) == 1 {
|
||||
expr := c.dibuilder.CreateExpression(nil)
|
||||
c.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock)
|
||||
} else {
|
||||
fieldOffsets := c.expandFormalParamOffsets(llvmType)
|
||||
for i, field := range fields {
|
||||
expr := c.dibuilder.CreateExpression([]int64{
|
||||
0x1000, // DW_OP_LLVM_fragment
|
||||
int64(fieldOffsets[i]) * 8, // offset in bits
|
||||
int64(c.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
|
||||
})
|
||||
c.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
|
||||
}
|
||||
}
|
||||
// TODO: set the value of this parameter.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,9 +831,6 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
||||
frame.locals[instr] = llvm.Undef(c.getLLVMType(instr.Type()))
|
||||
} else {
|
||||
frame.locals[instr] = value
|
||||
if len(*instr.Referrers()) != 0 && c.needsStackObjects() {
|
||||
c.trackExpr(frame, instr, value)
|
||||
}
|
||||
}
|
||||
case *ssa.DebugRef:
|
||||
// ignore
|
||||
@@ -1100,12 +907,16 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
||||
case *ssa.Store:
|
||||
llvmAddr := c.getValue(frame, instr.Addr)
|
||||
llvmVal := c.getValue(frame, instr.Val)
|
||||
c.emitNilCheck(frame, llvmAddr, "store")
|
||||
if c.targetData.TypeAllocSize(llvmVal.Type()) == 0 {
|
||||
// nothing to store
|
||||
return
|
||||
}
|
||||
c.builder.CreateStore(llvmVal, llvmAddr)
|
||||
store := c.builder.CreateStore(llvmVal, llvmAddr)
|
||||
valType := instr.Addr.Type().Underlying().(*types.Pointer).Elem()
|
||||
if c.ir.IsVolatile(valType) {
|
||||
// Volatile store, for memory-mapped registers.
|
||||
store.SetVolatile(true)
|
||||
}
|
||||
default:
|
||||
c.addError(instr.Pos(), "unknown instruction: "+instr.String())
|
||||
}
|
||||
@@ -1306,22 +1117,17 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
||||
|
||||
// Try to call the function directly for trivially static calls.
|
||||
if fn := instr.StaticCallee(); fn != nil {
|
||||
name := fn.RelString(nil)
|
||||
switch {
|
||||
case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister":
|
||||
return c.emitReadRegister(name, instr.Args)
|
||||
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||
switch fn.RelString(nil) {
|
||||
case "device/arm.ReadRegister":
|
||||
return c.emitReadRegister(instr.Args)
|
||||
case "device/arm.Asm", "device/avr.Asm":
|
||||
return c.emitAsm(instr.Args)
|
||||
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||
case "device/arm.AsmFull", "device/avr.AsmFull":
|
||||
return c.emitAsmFull(frame, instr)
|
||||
case strings.HasPrefix(name, "device/arm.SVCall"):
|
||||
case "device/arm.SVCall0", "device/arm.SVCall1", "device/arm.SVCall2", "device/arm.SVCall3", "device/arm.SVCall4":
|
||||
return c.emitSVCall(frame, instr.Args)
|
||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||
case "syscall.Syscall", "syscall.Syscall6", "syscall.Syscall9":
|
||||
return c.emitSyscall(frame, instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
return c.emitVolatileLoad(frame, instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
return c.emitVolatileStore(frame, instr)
|
||||
}
|
||||
|
||||
targetFunc := c.ir.GetFunction(fn)
|
||||
@@ -1352,7 +1158,10 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
||||
value := c.getValue(frame, instr.Value)
|
||||
// This is a func value, which cannot be called directly. We have to
|
||||
// extract the function pointer and context first from the func value.
|
||||
funcPtr, context := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
||||
funcPtr, context, err := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
c.emitNilCheck(frame, funcPtr, "fpcall")
|
||||
return c.parseFunctionCall(frame, instr.Args, funcPtr, context, false), nil
|
||||
}
|
||||
@@ -1372,9 +1181,9 @@ func (c *Compiler) getValue(frame *Frame, expr ssa.Value) llvm.Value {
|
||||
}
|
||||
return c.createFuncValue(fn.LLVMFn, llvm.Undef(c.i8ptrType), fn.Signature)
|
||||
case *ssa.Global:
|
||||
value := c.getGlobal(expr)
|
||||
value := c.ir.GetGlobal(expr).LLVMGlobal
|
||||
if value.IsNil() {
|
||||
c.addError(expr.Pos(), "global not found: "+expr.RelString(nil))
|
||||
c.addError(expr.Pos(), "global not found: "+c.ir.GetGlobal(expr).LinkName())
|
||||
return llvm.Undef(c.getLLVMType(expr.Type()))
|
||||
}
|
||||
return value
|
||||
@@ -1399,6 +1208,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
switch expr := expr.(type) {
|
||||
case *ssa.Alloc:
|
||||
typ := c.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
|
||||
var buf llvm.Value
|
||||
if expr.Heap {
|
||||
size := c.targetData.TypeAllocSize(typ)
|
||||
// Calculate ^uintptr(0)
|
||||
@@ -1407,17 +1217,17 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
// Size would be truncated if truncated to uintptr.
|
||||
return llvm.Value{}, c.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size))
|
||||
}
|
||||
// TODO: escape analysis
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
buf := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, expr.Comment)
|
||||
buf = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, expr.Comment)
|
||||
buf = c.builder.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
|
||||
return buf, nil
|
||||
} else {
|
||||
buf := c.createEntryBlockAlloca(typ, expr.Comment)
|
||||
buf = c.builder.CreateAlloca(typ, expr.Comment)
|
||||
if c.targetData.TypeAllocSize(typ) != 0 {
|
||||
c.builder.CreateStore(c.getZeroValue(typ), buf) // zero-initialize var
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
return buf, nil
|
||||
case *ssa.BinOp:
|
||||
x := c.getValue(frame, expr.X)
|
||||
y := c.getValue(frame, expr.Y)
|
||||
@@ -1470,11 +1280,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
x := c.getValue(frame, expr.X)
|
||||
return c.parseConvert(expr.X.Type(), expr.Type(), x, expr.Pos())
|
||||
case *ssa.Extract:
|
||||
if _, ok := expr.Tuple.(*ssa.Select); ok {
|
||||
return c.getChanSelectResult(frame, expr), nil
|
||||
}
|
||||
value := c.getValue(frame, expr.Tuple)
|
||||
return c.builder.CreateExtractValue(value, expr.Index, ""), nil
|
||||
result := c.builder.CreateExtractValue(value, expr.Index, "")
|
||||
return result, nil
|
||||
case *ssa.Field:
|
||||
value := c.getValue(frame, expr.X)
|
||||
if s := expr.X.Type().Underlying().(*types.Struct); s.NumFields() > 2 && s.Field(0).Name() == "C union" {
|
||||
@@ -1482,12 +1290,10 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
// This could be done directly, but as this is a very infrequent
|
||||
// operation it's much easier to bitcast it through an alloca.
|
||||
resultType := c.getLLVMType(expr.Type())
|
||||
alloca, allocaPtr, allocaSize := c.createTemporaryAlloca(value.Type(), "union.alloca")
|
||||
alloca := c.builder.CreateAlloca(value.Type(), "")
|
||||
c.builder.CreateStore(value, alloca)
|
||||
bitcast := c.builder.CreateBitCast(alloca, llvm.PointerType(resultType, 0), "union.bitcast")
|
||||
result := c.builder.CreateLoad(bitcast, "union.result")
|
||||
c.emitLifetimeEnd(allocaPtr, allocaSize)
|
||||
return result, nil
|
||||
bitcast := c.builder.CreateBitCast(alloca, llvm.PointerType(resultType, 0), "")
|
||||
return c.builder.CreateLoad(bitcast, ""), nil
|
||||
}
|
||||
result := c.builder.CreateExtractValue(value, expr.Field, "")
|
||||
return result, nil
|
||||
@@ -1527,13 +1333,11 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
|
||||
// Can't load directly from array (as index is non-constant), so have to
|
||||
// do it using an alloca+gep+load.
|
||||
alloca, allocaPtr, allocaSize := c.createTemporaryAlloca(array.Type(), "index.alloca")
|
||||
alloca := c.builder.CreateAlloca(array.Type(), "index.alloca")
|
||||
c.builder.CreateStore(array, alloca)
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
ptr := c.builder.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep")
|
||||
result := c.builder.CreateLoad(ptr, "index.load")
|
||||
c.emitLifetimeEnd(allocaPtr, allocaSize)
|
||||
return result, nil
|
||||
return c.builder.CreateLoad(ptr, "index.load"), nil
|
||||
case *ssa.IndexAddr:
|
||||
val := c.getValue(frame, expr.X)
|
||||
index := c.getValue(frame, expr.Index)
|
||||
@@ -1612,7 +1416,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
return c.parseMakeClosure(frame, expr)
|
||||
case *ssa.MakeInterface:
|
||||
val := c.getValue(frame, expr.X)
|
||||
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
||||
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos())
|
||||
case *ssa.MakeMap:
|
||||
mapType := expr.Type().Underlying().(*types.Map)
|
||||
llvmKeyType := c.getLLVMType(mapType.Key().Underlying())
|
||||
@@ -1621,16 +1425,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
|
||||
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
|
||||
sizeHint := llvm.ConstInt(c.uintptrType, 8, false)
|
||||
if expr.Reserve != nil {
|
||||
sizeHint = c.getValue(frame, expr.Reserve)
|
||||
var err error
|
||||
sizeHint, err = c.parseConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
}
|
||||
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
||||
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize}, "")
|
||||
return hashmap, nil
|
||||
case *ssa.MakeSlice:
|
||||
sliceLen := c.getValue(frame, expr.Len)
|
||||
@@ -1653,6 +1448,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
c.emitSliceBoundsCheck(frame, maxSize, sliceLen, sliceCap, expr.Len.Type().(*types.Basic), expr.Cap.Type().(*types.Basic))
|
||||
|
||||
// Allocate the backing array.
|
||||
// TODO: escape analysis
|
||||
sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
@@ -1692,16 +1488,16 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
llvmKeyType := c.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Key())
|
||||
llvmValueType := c.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Elem())
|
||||
|
||||
mapKeyAlloca, mapKeyPtr, mapKeySize := c.createTemporaryAlloca(llvmKeyType, "range.key")
|
||||
mapValueAlloca, mapValuePtr, mapValueSize := c.createTemporaryAlloca(llvmValueType, "range.value")
|
||||
mapKeyAlloca := c.builder.CreateAlloca(llvmKeyType, "range.key")
|
||||
mapKeyPtr := c.builder.CreateBitCast(mapKeyAlloca, c.i8ptrType, "range.keyptr")
|
||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "range.value")
|
||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "range.valueptr")
|
||||
ok := c.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
|
||||
|
||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.Int1Type(), llvmKeyType, llvmValueType}, false))
|
||||
tuple = c.builder.CreateInsertValue(tuple, ok, 0, "")
|
||||
tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapKeyAlloca, ""), 1, "")
|
||||
tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapValueAlloca, ""), 2, "")
|
||||
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||
c.emitLifetimeEnd(mapValuePtr, mapValueSize)
|
||||
return tuple, nil
|
||||
}
|
||||
case *ssa.Phi:
|
||||
@@ -1712,17 +1508,35 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
var iteratorType llvm.Type
|
||||
switch typ := expr.X.Type().Underlying().(type) {
|
||||
case *types.Basic: // string
|
||||
iteratorType = c.getLLVMRuntimeType("stringIterator")
|
||||
iteratorType = c.mod.GetTypeByName("runtime.stringIterator")
|
||||
case *types.Map:
|
||||
iteratorType = c.getLLVMRuntimeType("hashmapIterator")
|
||||
iteratorType = c.mod.GetTypeByName("runtime.hashmapIterator")
|
||||
default:
|
||||
panic("unknown type in range: " + typ.String())
|
||||
}
|
||||
it, _, _ := c.createTemporaryAlloca(iteratorType, "range.it")
|
||||
it := c.builder.CreateAlloca(iteratorType, "range.it")
|
||||
c.builder.CreateStore(c.getZeroValue(iteratorType), it)
|
||||
return it, nil
|
||||
case *ssa.Select:
|
||||
return c.emitSelect(frame, expr), nil
|
||||
if len(expr.States) == 0 {
|
||||
// Shortcuts for some simple selects.
|
||||
llvmType := c.getLLVMType(expr.Type())
|
||||
if expr.Blocking {
|
||||
// Blocks forever:
|
||||
// select {}
|
||||
c.createRuntimeCall("deadlockStub", nil, "")
|
||||
return llvm.Undef(llvmType), nil
|
||||
} else {
|
||||
// No-op:
|
||||
// select {
|
||||
// default:
|
||||
// }
|
||||
retval := llvm.Undef(llvmType)
|
||||
retval = c.builder.CreateInsertValue(retval, llvm.ConstInt(c.intType, 0xffffffffffffffff, true), 0, "")
|
||||
return retval, nil // {-1, false}
|
||||
}
|
||||
}
|
||||
return llvm.Value{}, c.makeError(expr.Pos(), "unimplemented: "+expr.String())
|
||||
case *ssa.Slice:
|
||||
if expr.Max != nil {
|
||||
return llvm.Value{}, c.makeError(expr.Pos(), "todo: full slice expressions (with max): "+expr.Type().String())
|
||||
@@ -1856,7 +1670,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
|
||||
newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
|
||||
newLen := c.builder.CreateSub(high, low, "")
|
||||
str := llvm.Undef(c.getLLVMRuntimeType("_string"))
|
||||
str := llvm.Undef(c.mod.GetTypeByName("runtime._string"))
|
||||
str = c.builder.CreateInsertValue(str, newPtr, 0, "")
|
||||
str = c.builder.CreateInsertValue(str, newLen, 1, "")
|
||||
return str, nil
|
||||
@@ -2130,12 +1944,10 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "binop on interface: "+op.String())
|
||||
}
|
||||
case *types.Chan, *types.Map, *types.Pointer:
|
||||
case *types.Map, *types.Pointer:
|
||||
// Maps are in general not comparable, but can be compared against nil
|
||||
// (which is a nil pointer). This means they can be trivially compared
|
||||
// by treating them as a pointer.
|
||||
// Channels behave as pointers in that they are equal as long as they
|
||||
// are created with the same call to make or if both are nil.
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||
@@ -2235,7 +2047,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||
global.SetUnnamedAddr(true)
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
strPtr := c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
strObj := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._string"), []llvm.Value{strPtr, strLen})
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
if !expr.IsNil() {
|
||||
@@ -2288,7 +2100,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||
llvm.ConstInt(c.uintptrType, 0, false),
|
||||
llvm.ConstPointerNull(c.i8ptrType),
|
||||
}
|
||||
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||
return llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), fields)
|
||||
case *types.Pointer:
|
||||
if expr.Value != nil {
|
||||
panic("expected nil pointer constant")
|
||||
@@ -2471,10 +2283,8 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p
|
||||
switch elemType.Kind() {
|
||||
case types.Byte:
|
||||
return c.createRuntimeCall("stringToBytes", []llvm.Value{value}, ""), nil
|
||||
case types.Rune:
|
||||
return c.createRuntimeCall("stringToRunes", []llvm.Value{value}, ""), nil
|
||||
default:
|
||||
panic("unexpected type in string to slice conversion")
|
||||
return llvm.Value{}, c.makeError(pos, "todo: convert from string: "+elemType.String())
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -2500,7 +2310,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
||||
return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
|
||||
}
|
||||
case token.MUL: // *x, dereference pointer
|
||||
unop.X.Type().Underlying().(*types.Pointer).Elem()
|
||||
valType := unop.X.Type().Underlying().(*types.Pointer).Elem()
|
||||
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
||||
// zero-length data
|
||||
return c.getZeroValue(x.Type().ElementType()), nil
|
||||
@@ -2510,8 +2320,8 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
||||
// var C.add unsafe.Pointer
|
||||
// Instead of a load from the global, create a bitcast of the
|
||||
// function pointer itself.
|
||||
globalName := c.getGlobalInfo(unop.X.(*ssa.Global)).linkName
|
||||
name := globalName[:len(globalName)-len("$funcaddr")]
|
||||
global := c.ir.GetGlobal(unop.X.(*ssa.Global))
|
||||
name := global.LinkName()[:len(global.LinkName())-len("$funcaddr")]
|
||||
fn := c.mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
return llvm.Value{}, c.makeError(unop.Pos(), "cgo function not found: "+name)
|
||||
@@ -2520,6 +2330,10 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
||||
} else {
|
||||
c.emitNilCheck(frame, x, "deref")
|
||||
load := c.builder.CreateLoad(x, "")
|
||||
if c.ir.IsVolatile(valType) {
|
||||
// Volatile load, for memory-mapped registers.
|
||||
load.SetVolatile(true)
|
||||
}
|
||||
return load, nil
|
||||
}
|
||||
case token.XOR: // ^x, toggle all bits in integer
|
||||
|
||||
+4
-7
@@ -29,7 +29,7 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
||||
frame.deferClosureFuncs = make(map[*ir.Function]int)
|
||||
|
||||
// Create defer list pointer.
|
||||
deferType := llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)
|
||||
deferType := llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)
|
||||
frame.deferPtr = c.builder.CreateAlloca(deferType, "deferPtr")
|
||||
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
||||
}
|
||||
@@ -130,9 +130,6 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
||||
// Put this struct in an alloca.
|
||||
alloca := c.builder.CreateAlloca(deferFrameType, "defer.alloca")
|
||||
c.builder.CreateStore(deferFrame, alloca)
|
||||
if c.needsStackObjects() {
|
||||
c.trackPointer(alloca)
|
||||
}
|
||||
|
||||
// Push it on top of the linked list by replacing deferPtr.
|
||||
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||
@@ -203,7 +200,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
||||
}
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0), c.i8ptrType}
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0), c.i8ptrType}
|
||||
for _, arg := range callback.Args {
|
||||
valueTypes = append(valueTypes, c.getLLVMType(arg.Type()))
|
||||
}
|
||||
@@ -234,7 +231,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
||||
// Direct call.
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)}
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
||||
for _, param := range callback.Params {
|
||||
valueTypes = append(valueTypes, c.getLLVMType(param.Type()))
|
||||
}
|
||||
@@ -263,7 +260,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
||||
case *ssa.MakeClosure:
|
||||
// Get the real defer struct type and cast to it.
|
||||
fn := c.ir.GetFunction(callback.Fn.(*ssa.Function))
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)}
|
||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
||||
params := fn.Signature.Params()
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
valueTypes = append(valueTypes, c.getLLVMType(params.At(i).Type()))
|
||||
|
||||
@@ -51,7 +51,7 @@ func (c *Compiler) LowerFuncValues() {
|
||||
}
|
||||
|
||||
// Find all func values used in the program with their signatures.
|
||||
funcValueWithSignaturePtr := llvm.PointerType(c.getLLVMRuntimeType("funcValueWithSignature"), 0)
|
||||
funcValueWithSignaturePtr := llvm.PointerType(c.mod.GetTypeByName("runtime.funcValueWithSignature"), 0)
|
||||
signatures := map[string]*funcSignatureInfo{}
|
||||
for global := c.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
if global.Type() != funcValueWithSignaturePtr {
|
||||
@@ -154,17 +154,17 @@ func (c *Compiler) LowerFuncValues() {
|
||||
// What we'll do is transform the following:
|
||||
// rawPtr := runtime.getFuncPtr(fn)
|
||||
// if func.rawPtr == nil {
|
||||
// runtime.nilPanic()
|
||||
// runtime.nilpanic()
|
||||
// }
|
||||
// result := func.rawPtr(...args, func.context)
|
||||
// into this:
|
||||
// if false {
|
||||
// runtime.nilPanic()
|
||||
// runtime.nilpanic()
|
||||
// }
|
||||
// var result // Phi
|
||||
// switch fn.id {
|
||||
// case 0:
|
||||
// runtime.nilPanic()
|
||||
// runtime.nilpanic()
|
||||
// case 1:
|
||||
// result = call first implementation...
|
||||
// case 2:
|
||||
@@ -222,7 +222,7 @@ func (c *Compiler) LowerFuncValues() {
|
||||
// The 0 case, which is actually a nil check.
|
||||
nilBlock := llvm.InsertBasicBlock(nextBlock, "func.nil")
|
||||
c.builder.SetInsertPointAtEnd(nilBlock)
|
||||
c.createRuntimeCall("nilPanic", nil, "")
|
||||
c.createRuntimeCall("nilpanic", nil, "")
|
||||
c.builder.CreateUnreachable()
|
||||
sw.AddCase(llvm.ConstInt(c.uintptrType, 0, false), nilBlock)
|
||||
|
||||
|
||||
+3
-3
@@ -52,7 +52,7 @@ func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signa
|
||||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||
if funcValueWithSignatureGlobal.IsNil() {
|
||||
funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
|
||||
funcValueWithSignatureType := c.mod.GetTypeByName("runtime.funcValueWithSignature")
|
||||
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
|
||||
llvm.ConstPtrToInt(funcPtr, c.uintptrType),
|
||||
sigGlobal,
|
||||
@@ -103,7 +103,7 @@ func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
||||
|
||||
// decodeFuncValue extracts the context and the function pointer from this func
|
||||
// value. This may be an expensive operation.
|
||||
func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
||||
func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value, err error) {
|
||||
context = c.builder.CreateExtractValue(funcValue, 0, "")
|
||||
switch c.funcImplementation() {
|
||||
case funcValueDoubleword:
|
||||
@@ -126,7 +126,7 @@ func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type {
|
||||
rawPtr := c.getRawFuncType(typ)
|
||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
||||
case funcValueSwitch:
|
||||
return c.getLLVMRuntimeType("funcValue")
|
||||
return c.mod.GetTypeByName("runtime.funcValue")
|
||||
default:
|
||||
panic("unimplemented func value variant")
|
||||
}
|
||||
|
||||
-471
@@ -1,471 +0,0 @@
|
||||
package compiler
|
||||
|
||||
// This file provides IR transformations necessary for precise and portable
|
||||
// garbage collectors.
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// needsStackObjects returns true if the compiler should insert stack objects
|
||||
// that can be traced by the garbage collector.
|
||||
func (c *Compiler) needsStackObjects() bool {
|
||||
if c.selectGC() != "conservative" {
|
||||
return false
|
||||
}
|
||||
for _, tag := range c.BuildTags {
|
||||
if tag == "cortexm" || tag == "tinygo.riscv" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// trackExpr inserts pointer tracking intrinsics for the GC if the expression is
|
||||
// one of the expressions that need this.
|
||||
func (c *Compiler) trackExpr(frame *Frame, expr ssa.Value, value llvm.Value) {
|
||||
// There are uses of this expression, Make sure the pointers
|
||||
// are tracked during GC.
|
||||
switch expr := expr.(type) {
|
||||
case *ssa.Alloc, *ssa.MakeChan, *ssa.MakeMap:
|
||||
// These values are always of pointer type in IR.
|
||||
c.trackPointer(value)
|
||||
case *ssa.Call, *ssa.Convert, *ssa.MakeClosure, *ssa.MakeInterface, *ssa.MakeSlice, *ssa.Next:
|
||||
if !value.IsNil() {
|
||||
c.trackValue(value)
|
||||
}
|
||||
case *ssa.Select:
|
||||
if alloca, ok := frame.selectRecvBuf[expr]; ok {
|
||||
if alloca.IsAUndefValue().IsNil() {
|
||||
c.trackPointer(alloca)
|
||||
}
|
||||
}
|
||||
case *ssa.UnOp:
|
||||
switch expr.Op {
|
||||
case token.MUL:
|
||||
// Pointer dereference.
|
||||
c.trackValue(value)
|
||||
case token.ARROW:
|
||||
// Channel receive operator.
|
||||
// It's not necessary to look at commaOk here, because in that
|
||||
// case it's just an aggregate and trackValue will extract the
|
||||
// pointer in there (if there is one).
|
||||
c.trackValue(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trackValue locates pointers in a value (possibly an aggregate) and tracks the
|
||||
// individual pointers
|
||||
func (c *Compiler) trackValue(value llvm.Value) {
|
||||
typ := value.Type()
|
||||
switch typ.TypeKind() {
|
||||
case llvm.PointerTypeKind:
|
||||
c.trackPointer(value)
|
||||
case llvm.StructTypeKind:
|
||||
if !typeHasPointers(typ) {
|
||||
return
|
||||
}
|
||||
numElements := typ.StructElementTypesCount()
|
||||
for i := 0; i < numElements; i++ {
|
||||
subValue := c.builder.CreateExtractValue(value, i, "")
|
||||
c.trackValue(subValue)
|
||||
}
|
||||
case llvm.ArrayTypeKind:
|
||||
if !typeHasPointers(typ) {
|
||||
return
|
||||
}
|
||||
numElements := typ.ArrayLength()
|
||||
for i := 0; i < numElements; i++ {
|
||||
subValue := c.builder.CreateExtractValue(value, i, "")
|
||||
c.trackValue(subValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trackPointer creates a call to runtime.trackPointer, bitcasting the poitner
|
||||
// first if needed. The input value must be of LLVM pointer type.
|
||||
func (c *Compiler) trackPointer(value llvm.Value) {
|
||||
if value.Type() != c.i8ptrType {
|
||||
value = c.builder.CreateBitCast(value, c.i8ptrType, "")
|
||||
}
|
||||
c.createRuntimeCall("trackPointer", []llvm.Value{value}, "")
|
||||
}
|
||||
|
||||
// typeHasPointers returns whether this type is a pointer or contains pointers.
|
||||
// If the type is an aggregate type, it will check whether there is a pointer
|
||||
// inside.
|
||||
func typeHasPointers(t llvm.Type) bool {
|
||||
switch t.TypeKind() {
|
||||
case llvm.PointerTypeKind:
|
||||
return true
|
||||
case llvm.StructTypeKind:
|
||||
for _, subType := range t.StructElementTypes() {
|
||||
if typeHasPointers(subType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case llvm.ArrayTypeKind:
|
||||
if typeHasPointers(t.ElementType()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// makeGCStackSlots converts all calls to runtime.trackPointer to explicit
|
||||
// stores to stack slots that are scannable by the GC.
|
||||
func (c *Compiler) makeGCStackSlots() bool {
|
||||
// Check whether there are allocations at all.
|
||||
alloc := c.mod.NamedFunction("runtime.alloc")
|
||||
if alloc.IsNil() {
|
||||
// Nothing to. Make sure all remaining bits and pieces for stack
|
||||
// chains are neutralized.
|
||||
for _, call := range getUses(c.mod.NamedFunction("runtime.trackPointer")) {
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
stackChainStart := c.mod.NamedGlobal("runtime.stackChainStart")
|
||||
if !stackChainStart.IsNil() {
|
||||
stackChainStart.SetInitializer(c.getZeroValue(stackChainStart.Type().ElementType()))
|
||||
stackChainStart.SetGlobalConstant(true)
|
||||
}
|
||||
}
|
||||
|
||||
trackPointer := c.mod.NamedFunction("runtime.trackPointer")
|
||||
if trackPointer.IsNil() || trackPointer.FirstUse().IsNil() {
|
||||
return false // nothing to do
|
||||
}
|
||||
|
||||
// Look at *all* functions to see whether they are free of function pointer
|
||||
// calls.
|
||||
// This takes less than 5ms for ~100kB of WebAssembly but would perhaps be
|
||||
// faster when written in C++ (to avoid the CGo overhead).
|
||||
funcsWithFPCall := map[llvm.Value]struct{}{}
|
||||
n := 0
|
||||
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
n++
|
||||
if _, ok := funcsWithFPCall[fn]; ok {
|
||||
continue // already found
|
||||
}
|
||||
done := false
|
||||
for bb := fn.FirstBasicBlock(); !bb.IsNil() && !done; bb = llvm.NextBasicBlock(bb) {
|
||||
for call := bb.FirstInstruction(); !call.IsNil() && !done; call = llvm.NextInstruction(call) {
|
||||
if call.IsACallInst().IsNil() {
|
||||
continue // only looking at calls
|
||||
}
|
||||
called := call.CalledValue()
|
||||
if !called.IsAFunction().IsNil() {
|
||||
continue // only looking for function pointers
|
||||
}
|
||||
funcsWithFPCall[fn] = struct{}{}
|
||||
markParentFunctions(funcsWithFPCall, fn)
|
||||
done = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which functions need stack objects. Many leaf functions don't
|
||||
// need it: it only causes overhead for them.
|
||||
// Actually, in one test it was only able to eliminate stack object from 12%
|
||||
// of functions that had a call to runtime.trackPointer (8 out of 68
|
||||
// functions), so this optimization is not as big as it may seem.
|
||||
allocatingFunctions := map[llvm.Value]struct{}{} // set of allocating functions
|
||||
|
||||
// Work from runtime.alloc and trace all parents to check which functions do
|
||||
// a heap allocation (and thus which functions do not).
|
||||
markParentFunctions(allocatingFunctions, alloc)
|
||||
|
||||
// Also trace all functions that call a function pointer.
|
||||
for fn := range funcsWithFPCall {
|
||||
// Assume that functions that call a function pointer do a heap
|
||||
// allocation as a conservative guess because the called function might
|
||||
// do a heap allocation.
|
||||
allocatingFunctions[fn] = struct{}{}
|
||||
markParentFunctions(allocatingFunctions, fn)
|
||||
}
|
||||
|
||||
// Collect some variables used below in the loop.
|
||||
stackChainStart := c.mod.NamedGlobal("runtime.stackChainStart")
|
||||
if stackChainStart.IsNil() {
|
||||
panic("stack chain start not found!")
|
||||
}
|
||||
stackChainStartType := stackChainStart.Type().ElementType()
|
||||
stackChainStart.SetInitializer(c.getZeroValue(stackChainStartType))
|
||||
|
||||
// Iterate until runtime.trackPointer has no uses left.
|
||||
for use := trackPointer.FirstUse(); !use.IsNil(); use = trackPointer.FirstUse() {
|
||||
// Pick the first use of runtime.trackPointer.
|
||||
call := use.User()
|
||||
if call.IsACallInst().IsNil() {
|
||||
panic("expected runtime.trackPointer use to be a call")
|
||||
}
|
||||
|
||||
// Pick the parent function.
|
||||
fn := call.InstructionParent().Parent()
|
||||
|
||||
if _, ok := allocatingFunctions[fn]; !ok {
|
||||
// This function nor any of the functions it calls (recursively)
|
||||
// allocate anything from the heap, so it will not trigger a garbage
|
||||
// collection cycle. Thus, it does not need to track local pointer
|
||||
// values.
|
||||
// This is a useful optimization but not as big as you might guess,
|
||||
// as described above (it avoids stack objects for ~12% of
|
||||
// functions).
|
||||
call.EraseFromParentAsInstruction()
|
||||
continue
|
||||
}
|
||||
|
||||
// Find all calls to runtime.trackPointer in this function.
|
||||
var calls []llvm.Value
|
||||
var returns []llvm.Value
|
||||
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
switch inst.InstructionOpcode() {
|
||||
case llvm.Call:
|
||||
if inst.CalledValue() == trackPointer {
|
||||
calls = append(calls, inst)
|
||||
}
|
||||
case llvm.Ret:
|
||||
returns = append(returns, inst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine what to do with each call.
|
||||
var allocas, pointers []llvm.Value
|
||||
for _, call := range calls {
|
||||
ptr := call.Operand(0)
|
||||
call.EraseFromParentAsInstruction()
|
||||
if ptr.IsAInstruction().IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Some trivial optimizations.
|
||||
if ptr.IsAInstruction().IsNil() {
|
||||
continue
|
||||
}
|
||||
switch ptr.InstructionOpcode() {
|
||||
case llvm.PHI, llvm.GetElementPtr:
|
||||
// These values do not create new values: the values already
|
||||
// existed locally in this function so must have been tracked
|
||||
// already.
|
||||
continue
|
||||
case llvm.ExtractValue, llvm.BitCast:
|
||||
// These instructions do not create new values, but their
|
||||
// original value may not be tracked. So keep tracking them for
|
||||
// now.
|
||||
// With more analysis, it should be possible to optimize a
|
||||
// significant chunk of these away.
|
||||
case llvm.Call, llvm.Load, llvm.IntToPtr:
|
||||
// These create new values so must be stored locally. But
|
||||
// perhaps some of these can be fused when they actually refer
|
||||
// to the same value.
|
||||
default:
|
||||
// Ambiguous. These instructions are uncommon, but perhaps could
|
||||
// be optimized if needed.
|
||||
}
|
||||
|
||||
if !ptr.IsAAllocaInst().IsNil() {
|
||||
if typeHasPointers(ptr.Type().ElementType()) {
|
||||
allocas = append(allocas, ptr)
|
||||
}
|
||||
} else {
|
||||
pointers = append(pointers, ptr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(allocas) == 0 && len(pointers) == 0 {
|
||||
// This function does not need to keep track of stack pointers.
|
||||
continue
|
||||
}
|
||||
|
||||
// Determine the type of the required stack slot.
|
||||
fields := []llvm.Type{
|
||||
stackChainStartType, // Pointer to parent frame.
|
||||
c.uintptrType, // Number of elements in this frame.
|
||||
}
|
||||
for _, alloca := range allocas {
|
||||
fields = append(fields, alloca.Type().ElementType())
|
||||
}
|
||||
for _, ptr := range pointers {
|
||||
fields = append(fields, ptr.Type())
|
||||
}
|
||||
stackObjectType := c.ctx.StructType(fields, false)
|
||||
|
||||
// Create the stack object at the function entry.
|
||||
c.builder.SetInsertPointBefore(fn.EntryBasicBlock().FirstInstruction())
|
||||
stackObject := c.builder.CreateAlloca(stackObjectType, "gc.stackobject")
|
||||
initialStackObject := c.getZeroValue(stackObjectType)
|
||||
numSlots := (c.targetData.TypeAllocSize(stackObjectType) - c.targetData.TypeAllocSize(c.i8ptrType)*2) / uint64(c.targetData.ABITypeAlignment(c.uintptrType))
|
||||
numSlotsValue := llvm.ConstInt(c.uintptrType, numSlots, false)
|
||||
initialStackObject = llvm.ConstInsertValue(initialStackObject, numSlotsValue, []uint32{1})
|
||||
c.builder.CreateStore(initialStackObject, stackObject)
|
||||
|
||||
// Update stack start.
|
||||
parent := c.builder.CreateLoad(stackChainStart, "")
|
||||
gep := c.builder.CreateGEP(stackObject, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
}, "")
|
||||
c.builder.CreateStore(parent, gep)
|
||||
stackObjectCast := c.builder.CreateBitCast(stackObject, stackChainStartType, "")
|
||||
c.builder.CreateStore(stackObjectCast, stackChainStart)
|
||||
|
||||
// Replace all independent allocas with GEPs in the stack object.
|
||||
for i, alloca := range allocas {
|
||||
gep := c.builder.CreateGEP(stackObject, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(2+i), false),
|
||||
}, "")
|
||||
alloca.ReplaceAllUsesWith(gep)
|
||||
alloca.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
// Do a store to the stack object after each new pointer that is created.
|
||||
for i, ptr := range pointers {
|
||||
c.builder.SetInsertPointBefore(llvm.NextInstruction(ptr))
|
||||
gep := c.builder.CreateGEP(stackObject, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(2+len(allocas)+i), false),
|
||||
}, "")
|
||||
c.builder.CreateStore(ptr, gep)
|
||||
}
|
||||
|
||||
// Make sure this stack object is popped from the linked list of stack
|
||||
// objects at return.
|
||||
for _, ret := range returns {
|
||||
c.builder.SetInsertPointBefore(ret)
|
||||
c.builder.CreateStore(parent, stackChainStart)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Compiler) addGlobalsBitmap() bool {
|
||||
if c.mod.NamedGlobal("runtime.trackedGlobalsStart").IsNil() {
|
||||
return false // nothing to do: no GC in use
|
||||
}
|
||||
|
||||
var trackedGlobals []llvm.Value
|
||||
var trackedGlobalTypes []llvm.Type
|
||||
for global := c.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
if global.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
typ := global.Type().ElementType()
|
||||
ptrs := c.getPointerBitmap(typ, global.Name())
|
||||
if ptrs.BitLen() == 0 {
|
||||
continue
|
||||
}
|
||||
trackedGlobals = append(trackedGlobals, global)
|
||||
trackedGlobalTypes = append(trackedGlobalTypes, typ)
|
||||
}
|
||||
|
||||
globalsBundleType := c.ctx.StructType(trackedGlobalTypes, false)
|
||||
globalsBundle := llvm.AddGlobal(c.mod, globalsBundleType, "tinygo.trackedGlobals")
|
||||
globalsBundle.SetLinkage(llvm.InternalLinkage)
|
||||
globalsBundle.SetUnnamedAddr(true)
|
||||
initializer := llvm.Undef(globalsBundleType)
|
||||
for i, global := range trackedGlobals {
|
||||
initializer = llvm.ConstInsertValue(initializer, global.Initializer(), []uint32{uint32(i)})
|
||||
gep := llvm.ConstGEP(globalsBundle, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false),
|
||||
})
|
||||
global.ReplaceAllUsesWith(gep)
|
||||
global.EraseFromParentAsGlobal()
|
||||
}
|
||||
globalsBundle.SetInitializer(initializer)
|
||||
|
||||
trackedGlobalsStart := llvm.ConstPtrToInt(globalsBundle, c.uintptrType)
|
||||
c.mod.NamedGlobal("runtime.trackedGlobalsStart").SetInitializer(trackedGlobalsStart)
|
||||
|
||||
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
||||
trackedGlobalsLength := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(globalsBundleType)/uint64(alignment), false)
|
||||
c.mod.NamedGlobal("runtime.trackedGlobalsLength").SetInitializer(trackedGlobalsLength)
|
||||
|
||||
bitmapBytes := c.getPointerBitmap(globalsBundleType, "globals bundle").Bytes()
|
||||
bitmapValues := make([]llvm.Value, len(bitmapBytes))
|
||||
for i, b := range bitmapBytes {
|
||||
bitmapValues[len(bitmapBytes)-i-1] = llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false)
|
||||
}
|
||||
bitmapArray := llvm.ConstArray(llvm.ArrayType(c.ctx.Int8Type(), len(bitmapBytes)), bitmapValues)
|
||||
bitmapNew := llvm.AddGlobal(c.mod, bitmapArray.Type(), "runtime.trackedGlobalsBitmap.tmp")
|
||||
bitmapOld := c.mod.NamedGlobal("runtime.trackedGlobalsBitmap")
|
||||
bitmapOld.ReplaceAllUsesWith(bitmapNew)
|
||||
bitmapNew.SetInitializer(bitmapArray)
|
||||
bitmapNew.SetName("runtime.trackedGlobalsBitmap")
|
||||
|
||||
return true // the IR was changed
|
||||
}
|
||||
|
||||
func (c *Compiler) getPointerBitmap(typ llvm.Type, name string) *big.Int {
|
||||
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
||||
switch typ.TypeKind() {
|
||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
return big.NewInt(0)
|
||||
case llvm.PointerTypeKind:
|
||||
return big.NewInt(1)
|
||||
case llvm.StructTypeKind:
|
||||
ptrs := big.NewInt(0)
|
||||
for i, subtyp := range typ.StructElementTypes() {
|
||||
subptrs := c.getPointerBitmap(subtyp, name)
|
||||
if subptrs.BitLen() == 0 {
|
||||
continue
|
||||
}
|
||||
offset := c.targetData.ElementOffset(typ, i)
|
||||
if offset%uint64(alignment) != 0 {
|
||||
panic("precise GC: global contains unaligned pointer: " + name)
|
||||
}
|
||||
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
||||
ptrs.Or(ptrs, subptrs)
|
||||
}
|
||||
return ptrs
|
||||
case llvm.ArrayTypeKind:
|
||||
subtyp := typ.ElementType()
|
||||
subptrs := c.getPointerBitmap(subtyp, name)
|
||||
ptrs := big.NewInt(0)
|
||||
if subptrs.BitLen() == 0 {
|
||||
return ptrs
|
||||
}
|
||||
elementSize := c.targetData.TypeAllocSize(subtyp)
|
||||
for i := 0; i < typ.ArrayLength(); i++ {
|
||||
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
||||
ptrs.Or(ptrs, subptrs)
|
||||
}
|
||||
return ptrs
|
||||
default:
|
||||
panic("unknown type kind of global: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
// markParentFunctions traverses all parent function calls (recursively) and
|
||||
// adds them to the set of marked functions. It only considers function calls:
|
||||
// any other uses of such a function is ignored.
|
||||
func markParentFunctions(marked map[llvm.Value]struct{}, fn llvm.Value) {
|
||||
worklist := []llvm.Value{fn}
|
||||
for len(worklist) != 0 {
|
||||
fn := worklist[len(worklist)-1]
|
||||
worklist = worklist[:len(worklist)-1]
|
||||
for _, use := range getUses(fn) {
|
||||
if use.IsACallInst().IsNil() || use.CalledValue() != fn {
|
||||
// Not the parent function.
|
||||
continue
|
||||
}
|
||||
parent := use.InstructionParent().Parent()
|
||||
if _, ok := marked[parent]; !ok {
|
||||
marked[parent] = struct{}{}
|
||||
worklist = append(worklist, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,7 +321,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
|
||||
// Coroutine setup.
|
||||
c.builder.SetInsertPointBefore(f.EntryBasicBlock().FirstInstruction())
|
||||
taskState := c.builder.CreateAlloca(c.getLLVMRuntimeType("taskState"), "task.state")
|
||||
taskState := c.builder.CreateAlloca(c.mod.GetTypeByName("runtime.taskState"), "task.state")
|
||||
stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8")
|
||||
id := c.builder.CreateCall(coroIdFunc, []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
@@ -336,9 +336,6 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
size = c.builder.CreateZExt(size, c.uintptrType, "task.size.uintptr")
|
||||
}
|
||||
data := c.createRuntimeCall("alloc", []llvm.Value{size}, "task.data")
|
||||
if c.needsStackObjects() {
|
||||
c.trackPointer(data)
|
||||
}
|
||||
frame.taskHandle = c.builder.CreateCall(coroBeginFunc, []llvm.Value{id, data}, "task.handle")
|
||||
|
||||
// Modify async calls so this function suspends right after the child
|
||||
@@ -392,20 +389,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
|
||||
var parentHandle llvm.Value
|
||||
if f.Linkage() == llvm.ExternalLinkage {
|
||||
// Exported function.
|
||||
// Note that getTaskPromisePtr will panic if it is called with
|
||||
// a nil pointer, so blocking exported functions that try to
|
||||
// return anything will not work.
|
||||
parentHandle = llvm.ConstPointerNull(c.i8ptrType)
|
||||
} else {
|
||||
parentHandle = f.LastParam()
|
||||
if parentHandle.IsNil() || parentHandle.Name() != "parentHandle" {
|
||||
// sanity check
|
||||
panic("trying to make exported function async")
|
||||
}
|
||||
}
|
||||
parentHandle := f.LastParam()
|
||||
|
||||
// Store return values.
|
||||
switch inst.OperandsCount() {
|
||||
@@ -433,7 +417,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
// behavior somehow (with the unreachable instruction).
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false),
|
||||
}, "ret")
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
||||
@@ -504,7 +488,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
c.builder.SetInsertPointBefore(deadlockCall)
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false), // final suspend
|
||||
}, "")
|
||||
c.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
|
||||
c.builder.SetInsertPointBefore(deadlockCall)
|
||||
|
||||
+2
-11
@@ -18,19 +18,10 @@ import (
|
||||
// func ReadRegister(name string) uintptr
|
||||
//
|
||||
// The register name must be a constant, for example "sp".
|
||||
func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
|
||||
func (c *Compiler) emitReadRegister(args []ssa.Value) (llvm.Value, error) {
|
||||
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false)
|
||||
regname := constant.StringVal(args[0].(*ssa.Const).Value)
|
||||
var asm string
|
||||
switch name {
|
||||
case "device/arm.ReadRegister":
|
||||
asm = "mov $0, " + regname
|
||||
case "device/riscv.ReadRegister":
|
||||
asm = "mv $0, " + regname
|
||||
default:
|
||||
panic("unknown architecture")
|
||||
}
|
||||
target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0)
|
||||
target := llvm.InlineAsm(fnType, "mov $0, "+regname, "=r", false, false, 0)
|
||||
return c.builder.CreateCall(target, nil, ""), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -163,8 +163,8 @@ func (c *Compiler) LowerInterfaces() {
|
||||
// run runs the pass itself.
|
||||
func (p *lowerInterfacesPass) run() {
|
||||
// Collect all type codes.
|
||||
typecodeIDPtr := llvm.PointerType(p.getLLVMRuntimeType("typecodeID"), 0)
|
||||
typeInInterfacePtr := llvm.PointerType(p.getLLVMRuntimeType("typeInInterface"), 0)
|
||||
typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0)
|
||||
typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0)
|
||||
var typesInInterfaces []llvm.Value
|
||||
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
switch global.Type() {
|
||||
|
||||
+22
-16
@@ -22,23 +22,26 @@ import (
|
||||
// value field.
|
||||
//
|
||||
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) (llvm.Value, error) {
|
||||
itfValue := c.emitPointerPack([]llvm.Value{val})
|
||||
itfTypeCodeGlobal := c.getTypeCode(typ)
|
||||
itfMethodSetGlobal := c.getTypeMethodSet(typ)
|
||||
itfMethodSetGlobal, err := c.getTypeMethodSet(typ)
|
||||
if err != nil {
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
||||
if itfConcreteTypeGlobal.IsNil() {
|
||||
typeInInterface := c.getLLVMRuntimeType("typeInInterface")
|
||||
typeInInterface := c.mod.GetTypeByName("runtime.typeInInterface")
|
||||
itfConcreteTypeGlobal = llvm.AddGlobal(c.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
|
||||
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
|
||||
itfConcreteTypeGlobal.SetGlobalConstant(true)
|
||||
itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage)
|
||||
}
|
||||
itfTypeCode := c.builder.CreatePtrToInt(itfConcreteTypeGlobal, c.uintptrType, "")
|
||||
itf := llvm.Undef(c.getLLVMRuntimeType("_interface"))
|
||||
itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface"))
|
||||
itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "")
|
||||
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
|
||||
return itf
|
||||
return itf, nil
|
||||
}
|
||||
|
||||
// getTypeCode returns a reference to a type code.
|
||||
@@ -48,7 +51,7 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
|
||||
globalName := "type:" + getTypeCodeName(typ)
|
||||
global := c.mod.NamedGlobal(globalName)
|
||||
if global.IsNil() {
|
||||
global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName)
|
||||
global = llvm.AddGlobal(c.mod, c.mod.GetTypeByName("runtime.typecodeID"), globalName)
|
||||
global.SetGlobalConstant(true)
|
||||
}
|
||||
return global
|
||||
@@ -152,22 +155,22 @@ func getTypeCodeName(t types.Type) string {
|
||||
|
||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
||||
// method set should be unreferenced after the interface lowering pass.
|
||||
func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||
func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
|
||||
global := c.mod.NamedGlobal(typ.String() + "$methodset")
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
if !global.IsNil() {
|
||||
// the method set already exists
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero}), nil
|
||||
}
|
||||
|
||||
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
||||
if ms.Len() == 0 {
|
||||
// no methods, so can leave that one out
|
||||
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
|
||||
return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0)), nil
|
||||
}
|
||||
|
||||
methods := make([]llvm.Value, ms.Len())
|
||||
interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo")
|
||||
interfaceMethodInfoType := c.mod.GetTypeByName("runtime.interfaceMethodInfo")
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
method := ms.At(i)
|
||||
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
||||
@@ -176,7 +179,10 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||
// compiler error, so panic
|
||||
panic("cannot find function: " + f.LinkName())
|
||||
}
|
||||
fn := c.getInterfaceInvokeWrapper(f)
|
||||
fn, err := c.getInterfaceInvokeWrapper(f)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
|
||||
signatureGlobal,
|
||||
llvm.ConstPtrToInt(fn, c.uintptrType),
|
||||
@@ -189,7 +195,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||
global.SetInitializer(value)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetLinkage(llvm.PrivateLinkage)
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero}), nil
|
||||
}
|
||||
|
||||
// getInterfaceMethodSet returns a global variable with the method set of the
|
||||
@@ -359,12 +365,12 @@ type interfaceInvokeWrapper struct {
|
||||
// the underlying value, dereferences it, and calls the real method. This
|
||||
// wrapper is only needed when the interface value actually doesn't fit in a
|
||||
// pointer and a pointer to the value must be created.
|
||||
func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) (llvm.Value, error) {
|
||||
wrapperName := f.LinkName() + "$invoke"
|
||||
wrapper := c.mod.NamedFunction(wrapperName)
|
||||
if !wrapper.IsNil() {
|
||||
// Wrapper already created. Return it directly.
|
||||
return wrapper
|
||||
return wrapper, nil
|
||||
}
|
||||
|
||||
// Get the expanded receiver type.
|
||||
@@ -377,7 +383,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
// Casting a function signature to a different signature and calling it
|
||||
// with a receiver pointer bitcasted to *i8 (as done in calls on an
|
||||
// interface) is hopefully a safe (defined) operation.
|
||||
return f.LLVMFn
|
||||
return f.LLVMFn, nil
|
||||
}
|
||||
|
||||
// create wrapper function
|
||||
@@ -390,7 +396,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||
wrapper: wrapper,
|
||||
receiverType: receiverType,
|
||||
})
|
||||
return wrapper
|
||||
return wrapper, nil
|
||||
}
|
||||
|
||||
// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper,
|
||||
|
||||
@@ -22,54 +22,6 @@ func getUses(value llvm.Value) []llvm.Value {
|
||||
return uses
|
||||
}
|
||||
|
||||
// createEntryBlockAlloca creates a new alloca in the entry block, even though
|
||||
// the IR builder is located elsewhere. It assumes that the insert point is
|
||||
// at the end of the current block.
|
||||
func (c *Compiler) createEntryBlockAlloca(t llvm.Type, name string) llvm.Value {
|
||||
currentBlock := c.builder.GetInsertBlock()
|
||||
entryBlock := currentBlock.Parent().EntryBasicBlock()
|
||||
if entryBlock.FirstInstruction().IsNil() {
|
||||
c.builder.SetInsertPointAtEnd(entryBlock)
|
||||
} else {
|
||||
c.builder.SetInsertPointBefore(entryBlock.FirstInstruction())
|
||||
}
|
||||
alloca := c.builder.CreateAlloca(t, name)
|
||||
c.builder.SetInsertPointAtEnd(currentBlock)
|
||||
return alloca
|
||||
}
|
||||
|
||||
// createTemporaryAlloca creates a new alloca in the entry block and adds
|
||||
// lifetime start infromation in the IR signalling that the alloca won't be used
|
||||
// before this point.
|
||||
//
|
||||
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
||||
// end the lifetime using emitLifetimeEnd after you're done with it.
|
||||
func (c *Compiler) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||
alloca = c.createEntryBlockAlloca(t, name)
|
||||
bitcast = c.builder.CreateBitCast(alloca, c.i8ptrType, name+".bitcast")
|
||||
size = llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(t), false)
|
||||
c.builder.CreateCall(c.getLifetimeStartFunc(), []llvm.Value{size, bitcast}, "")
|
||||
return
|
||||
}
|
||||
|
||||
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
||||
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||
// createTemporaryAlloca.
|
||||
func (c *Compiler) emitLifetimeEnd(ptr, size llvm.Value) {
|
||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{size, ptr}, "")
|
||||
}
|
||||
|
||||
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
||||
// first if it doesn't exist yet.
|
||||
func (c *Compiler) getLifetimeStartFunc() llvm.Value {
|
||||
fn := c.mod.NamedFunction("llvm.lifetime.start.p0i8")
|
||||
if fn.IsNil() {
|
||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.ctx.Int64Type(), c.i8ptrType}, false)
|
||||
fn = llvm.AddFunction(c.mod, "llvm.lifetime.start.p0i8", fnType)
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
||||
// first if it doesn't exist yet.
|
||||
func (c *Compiler) getLifetimeEndFunc() llvm.Value {
|
||||
|
||||
+15
-31
@@ -11,13 +11,8 @@ import (
|
||||
|
||||
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||
llvmValueType := c.getLLVMType(valueType)
|
||||
|
||||
// Allocate the memory for the resulting type. Do not zero this memory: it
|
||||
// will be zeroed by the hashmap get implementation if the key is not
|
||||
// present in the map.
|
||||
mapValueAlloca, mapValuePtr, mapValueSize := c.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
||||
|
||||
// Do the lookup. How it is done depends on the key type.
|
||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||
var commaOkValue llvm.Value
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
@@ -25,24 +20,15 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
||||
commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "")
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
||||
// growth.
|
||||
mapKeyAlloca, mapKeyPtr, mapKeySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
c.builder.CreateStore(key, mapKeyAlloca)
|
||||
// Fetch the value from the hashmap.
|
||||
params := []llvm.Value{m, mapKeyPtr, mapValuePtr}
|
||||
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||
c.builder.CreateStore(key, keyAlloca)
|
||||
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||
params := []llvm.Value{m, keyPtr, mapValuePtr}
|
||||
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||
} else {
|
||||
// Not trivially comparable using memcmp.
|
||||
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
}
|
||||
|
||||
// Load the resulting value from the hashmap. The value is set to the zero
|
||||
// value if the key doesn't exist in the hashmap.
|
||||
mapValue := c.builder.CreateLoad(mapValueAlloca, "")
|
||||
c.emitLifetimeEnd(mapValuePtr, mapValueSize)
|
||||
|
||||
if commaOk {
|
||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false))
|
||||
tuple = c.builder.CreateInsertValue(tuple, mapValue, 0, "")
|
||||
@@ -54,8 +40,9 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
||||
}
|
||||
|
||||
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||
valueAlloca, valuePtr, valueSize := c.createTemporaryAlloca(value.Type(), "hashmap.value")
|
||||
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
||||
c.builder.CreateStore(value, valueAlloca)
|
||||
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
@@ -63,15 +50,14 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, p
|
||||
c.createRuntimeCall("hashmapStringSet", params, "")
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
keyAlloca, keyPtr, keySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||
c.builder.CreateStore(key, keyAlloca)
|
||||
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||
params := []llvm.Value{m, keyPtr, valuePtr}
|
||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
||||
c.emitLifetimeEnd(keyPtr, keySize)
|
||||
} else {
|
||||
c.addError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
c.addError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
}
|
||||
c.emitLifetimeEnd(valuePtr, valueSize)
|
||||
}
|
||||
|
||||
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
||||
@@ -82,14 +68,14 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke
|
||||
c.createRuntimeCall("hashmapStringDelete", params, "")
|
||||
return nil
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
keyAlloca, keyPtr, keySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||
c.builder.CreateStore(key, keyAlloca)
|
||||
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||
params := []llvm.Value{m, keyPtr}
|
||||
c.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||
c.emitLifetimeEnd(keyPtr, keySize)
|
||||
return nil
|
||||
} else {
|
||||
return c.makeError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
return c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +107,6 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
switch keyType := keyType.(type) {
|
||||
case *types.Basic:
|
||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
||||
case *types.Pointer:
|
||||
return true
|
||||
case *types.Struct:
|
||||
for i := 0; i < keyType.NumFields(); i++ {
|
||||
fieldType := keyType.Field(i).Type().Underlying()
|
||||
|
||||
@@ -114,14 +114,6 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
builder.Populate(modPasses)
|
||||
modPasses.Run(c.mod)
|
||||
|
||||
hasGCPass := c.addGlobalsBitmap()
|
||||
hasGCPass = c.makeGCStackSlots() || hasGCPass
|
||||
if hasGCPass {
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("GC pass caused a verification failure")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
package compiler
|
||||
|
||||
// This file manages symbols, that is, functions and globals. It reads their
|
||||
// pragmas, determines the link name, etc.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// globalInfo contains some information about a specific global. By default,
|
||||
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||
// some symbols this is different (due to //go:extern for example).
|
||||
type globalInfo struct {
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
}
|
||||
|
||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||
// program. In particular, they are required for //go:extern pragmas on globals.
|
||||
func (c *Compiler) loadASTComments(lprogram *loader.Program) {
|
||||
c.astComments = map[string]*ast.CommentGroup{}
|
||||
for _, pkgInfo := range lprogram.Sorted() {
|
||||
for _, file := range pkgInfo.Files {
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch decl.Tok {
|
||||
case token.VAR:
|
||||
if len(decl.Specs) != 1 {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
switch spec := spec.(type) {
|
||||
case *ast.ValueSpec: // decl.Tok == token.VAR
|
||||
for _, name := range spec.Names {
|
||||
id := pkgInfo.Pkg.Path() + "." + name.Name
|
||||
c.astComments[id] = decl.Doc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getGlobal returns a LLVM IR global value for a Go SSA global. It is added to
|
||||
// the LLVM IR if it has not been added already.
|
||||
func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value {
|
||||
info := c.getGlobalInfo(g)
|
||||
llvmGlobal := c.mod.NamedGlobal(info.linkName)
|
||||
if llvmGlobal.IsNil() {
|
||||
llvmType := c.getLLVMType(g.Type().(*types.Pointer).Elem())
|
||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||
if !info.extern {
|
||||
llvmGlobal.SetInitializer(c.getZeroValue(llvmType))
|
||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
}
|
||||
return llvmGlobal
|
||||
}
|
||||
|
||||
// getGlobalInfo returns some information about a specific global.
|
||||
func (c *Compiler) getGlobalInfo(g *ssa.Global) globalInfo {
|
||||
info := globalInfo{}
|
||||
if strings.HasPrefix(g.Name(), "C.") {
|
||||
// Created by CGo: such a name cannot be created by regular C code.
|
||||
info.linkName = g.Name()[2:]
|
||||
info.extern = true
|
||||
} else {
|
||||
// Pick the default linkName.
|
||||
info.linkName = g.RelString(nil)
|
||||
// Check for //go: pragmas, which may change the link name (among
|
||||
// others).
|
||||
doc := c.astComments[info.linkName]
|
||||
if doc != nil {
|
||||
info.parsePragmas(doc)
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// Parse //go: pragma comments from the source. In particular, it parses the
|
||||
// //go:extern pragma on globals.
|
||||
func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
||||
for _, comment := range doc.List {
|
||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(comment.Text)
|
||||
switch parts[0] {
|
||||
case "//go:extern":
|
||||
info.extern = true
|
||||
if len(parts) == 2 {
|
||||
info.linkName = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -4,6 +4,7 @@ package compiler
|
||||
// compiler builtins.
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
@@ -13,7 +14,7 @@ import (
|
||||
// emitSyscall emits an inline system call instruction, depending on the target
|
||||
// OS/arch.
|
||||
func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
|
||||
num := c.getValue(frame, call.Args[0])
|
||||
num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value)
|
||||
var syscallResult llvm.Value
|
||||
switch {
|
||||
case c.GOARCH == "amd64":
|
||||
@@ -28,12 +29,12 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||
// > All system classes enter the kernel via the syscall instruction.
|
||||
//
|
||||
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
||||
num = c.builder.CreateOr(num, llvm.ConstInt(c.uintptrType, 0x2000000, false), "")
|
||||
num += 0x2000000
|
||||
}
|
||||
// Sources:
|
||||
// https://stackoverflow.com/a/2538212
|
||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
||||
args := []llvm.Value{num}
|
||||
args := []llvm.Value{llvm.ConstInt(c.uintptrType, num, false)}
|
||||
argTypes := []llvm.Type{c.uintptrType}
|
||||
// Constraints will look something like:
|
||||
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
||||
@@ -80,7 +81,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
args = append(args, num)
|
||||
args = append(args, llvm.ConstInt(c.uintptrType, num, false))
|
||||
argTypes = append(argTypes, c.uintptrType)
|
||||
constraints += ",{r7}" // syscall number
|
||||
for i := len(call.Args) - 1; i < 4; i++ {
|
||||
@@ -110,7 +111,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
args = append(args, num)
|
||||
args = append(args, llvm.ConstInt(c.uintptrType, num, false))
|
||||
argTypes = append(argTypes, c.uintptrType)
|
||||
constraints += ",{x8}" // syscall number
|
||||
for i := len(call.Args) - 1; i < 8; i++ {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package compiler
|
||||
|
||||
// This file implements volatile loads/stores in runtime/volatile.LoadT and
|
||||
// runtime/volatile.StoreT as compiler builtins.
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
addr := c.getValue(frame, instr.Args[0])
|
||||
c.emitNilCheck(frame, addr, "deref")
|
||||
val := c.builder.CreateLoad(addr, "")
|
||||
val.SetVolatile(true)
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
addr := c.getValue(frame, instr.Args[0])
|
||||
val := c.getValue(frame, instr.Args[1])
|
||||
c.emitNilCheck(frame, addr, "deref")
|
||||
store := c.builder.CreateStore(val, addr)
|
||||
store.SetVolatile(true)
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
+4
-16
@@ -34,14 +34,11 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||
}
|
||||
// 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, _, _ = c.createTemporaryAlloca(packedType, "")
|
||||
packedAlloc = c.builder.CreateAlloca(packedType, "")
|
||||
} else {
|
||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
packedHeapAlloc = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "")
|
||||
if c.needsStackObjects() {
|
||||
c.trackPointer(packedHeapAlloc)
|
||||
}
|
||||
packedAlloc = c.builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
||||
}
|
||||
// Store all values in the alloca or heap pointer.
|
||||
@@ -57,11 +54,7 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
||||
if packedHeapAlloc.IsNil() {
|
||||
// Load value (as *i8) from the alloca.
|
||||
packedAlloc = c.builder.CreateBitCast(packedAlloc, llvm.PointerType(c.i8ptrType, 0), "")
|
||||
result := c.builder.CreateLoad(packedAlloc, "")
|
||||
packedPtr := c.builder.CreateBitCast(packedAlloc, c.i8ptrType, "")
|
||||
packedSize := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||
c.emitLifetimeEnd(packedPtr, packedSize)
|
||||
return result
|
||||
return c.builder.CreateLoad(packedAlloc, "")
|
||||
} else {
|
||||
// Get the original heap allocation pointer, which already is an *i8.
|
||||
return packedHeapAlloc
|
||||
@@ -73,7 +66,7 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
||||
packedType := c.ctx.StructType(valueTypes, false)
|
||||
|
||||
// Get a correctly-typed pointer to the packed data.
|
||||
var packedAlloc, packedRawAlloc llvm.Value
|
||||
var packedAlloc llvm.Value
|
||||
size := c.targetData.TypeAllocSize(packedType)
|
||||
if size == 0 {
|
||||
// No data to unpack.
|
||||
@@ -87,7 +80,7 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
||||
return []llvm.Value{c.builder.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
||||
}
|
||||
// Fallback: load it using an alloca.
|
||||
packedRawAlloc, _, _ = c.createTemporaryAlloca(llvm.PointerType(c.i8ptrType, 0), "unpack.raw.alloc")
|
||||
packedRawAlloc := c.builder.CreateAlloca(llvm.PointerType(c.i8ptrType, 0), "unpack.raw.alloc")
|
||||
packedRawValue := c.builder.CreateBitCast(ptr, llvm.PointerType(c.i8ptrType, 0), "unpack.raw.value")
|
||||
c.builder.CreateStore(packedRawValue, packedRawAlloc)
|
||||
packedAlloc = c.builder.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
||||
@@ -111,10 +104,5 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
||||
gep := c.builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
||||
values[i] = c.builder.CreateLoad(gep, "")
|
||||
}
|
||||
if !packedRawAlloc.IsNil() {
|
||||
allocPtr := c.builder.CreateBitCast(packedRawAlloc, c.i8ptrType, "")
|
||||
allocSize := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(c.uintptrType), false)
|
||||
c.emitLifetimeEnd(allocPtr, allocSize)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
module github.com/tinygo-org/tinygo
|
||||
|
||||
go 1.11
|
||||
|
||||
require (
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef
|
||||
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
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/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46 h1:wXG2bA8fO7Vv7lLk2PihFMTqmbT173Tje39oKzQ50Mo=
|
||||
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef h1:ymc9FeDom3RIEA3coKokSllBB1hRcMT0tZ1W3Jf9Ids=
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261 h1:rJS2Hga39YAnm7DE4qrPm6Dr/67EOojL0XPzvbEeBiw=
|
||||
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
+10
-51
@@ -176,20 +176,6 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
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.
|
||||
panic("unimplemented: bitcast of map")
|
||||
}
|
||||
value := fr.getLocal(operand).(*LocalValue)
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
|
||||
|
||||
@@ -198,25 +184,6 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||
predicate := inst.IntPredicate()
|
||||
if predicate == llvm.IntEQ && 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.
|
||||
lhsNil, ok1 := isPointerNil(lhs)
|
||||
rhsNil, ok2 := isPointerNil(rhs)
|
||||
if ok1 && ok2 {
|
||||
if lhsNil && rhsNil {
|
||||
// Both are nil, so this icmp is always evaluated to true.
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false)}
|
||||
continue
|
||||
}
|
||||
if lhsNil != rhsNil {
|
||||
// Only one of them is nil, 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
|
||||
@@ -380,10 +347,6 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
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 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.
|
||||
@@ -496,21 +459,17 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
}
|
||||
thenBB := inst.Operand(1)
|
||||
elseBB := inst.Operand(2)
|
||||
if !cond.IsAInstruction().IsNil() {
|
||||
if !cond.IsConstant() {
|
||||
return nil, nil, 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, 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:
|
||||
panic("branch was not true or false")
|
||||
} else {
|
||||
switch cond.ZExtValue() {
|
||||
case 0: // false
|
||||
return nil, []llvm.Value{thenBB}, nil // then
|
||||
case 1: // true
|
||||
return nil, []llvm.Value{elseBB}, nil // else
|
||||
default:
|
||||
panic("branch was not true or false")
|
||||
}
|
||||
}
|
||||
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
|
||||
// unconditional branch (goto)
|
||||
|
||||
@@ -18,6 +18,7 @@ type Eval struct {
|
||||
TargetData llvm.TargetData
|
||||
Debug bool
|
||||
builder llvm.Builder
|
||||
dibuilder *llvm.DIBuilder
|
||||
dirtyGlobals map[llvm.Value]struct{}
|
||||
sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results
|
||||
}
|
||||
@@ -37,6 +38,7 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
||||
dirtyGlobals: map[llvm.Value]struct{}{},
|
||||
}
|
||||
e.builder = mod.Context().NewBuilder()
|
||||
e.dibuilder = llvm.NewDIBuilder(mod)
|
||||
|
||||
initAll := mod.NamedFunction(name)
|
||||
bb := initAll.EntryBasicBlock()
|
||||
@@ -47,6 +49,7 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
||||
e.builder.SetInsertPointBefore(bb.FirstInstruction())
|
||||
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
|
||||
e.builder.SetInsertPointBefore(dummy)
|
||||
e.builder.SetInstDebugLocation(bb.FirstInstruction())
|
||||
var initCalls []llvm.Value
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if inst == dummy {
|
||||
|
||||
+1
-14
@@ -35,10 +35,6 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
||||
return &sideEffectResult{severity: sideEffectLimited}
|
||||
case "runtime.interfaceImplements":
|
||||
return &sideEffectResult{severity: sideEffectNone}
|
||||
case "runtime.trackPointer":
|
||||
return &sideEffectResult{severity: sideEffectNone}
|
||||
case "llvm.dbg.value":
|
||||
return &sideEffectResult{severity: sideEffectNone}
|
||||
}
|
||||
if e.sideEffectFuncs == nil {
|
||||
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
|
||||
@@ -111,16 +107,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
||||
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:
|
||||
case llvm.Load, llvm.Store:
|
||||
if inst.IsVolatile() {
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
}
|
||||
|
||||
@@ -94,29 +94,3 @@ func isScalar(t llvm.Type) bool {
|
||||
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.
|
||||
operand := v.Operand(0)
|
||||
if operand.IsConstant() {
|
||||
return operand.ZExtValue() == 0, true
|
||||
}
|
||||
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
|
||||
}
|
||||
return false, false // not valid
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package ir
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -22,18 +23,43 @@ type Program struct {
|
||||
mainPkg *ssa.Package
|
||||
Functions []*Function
|
||||
functionMap map[*ssa.Function]*Function
|
||||
Globals []*Global
|
||||
globalMap map[*ssa.Global]*Global
|
||||
comments map[string]*ast.CommentGroup
|
||||
NamedTypes []*NamedType
|
||||
}
|
||||
|
||||
// Function or method.
|
||||
type Function struct {
|
||||
*ssa.Function
|
||||
LLVMFn llvm.Value
|
||||
linkName string // go:linkname, go:export, go:interrupt
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
interrupt bool // go:interrupt
|
||||
inline InlineType // go:inline
|
||||
linkName string // go:linkname, go:export, go:interrupt
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
interrupt bool // go:interrupt
|
||||
}
|
||||
|
||||
// Global variable, possibly constant.
|
||||
type Global struct {
|
||||
*ssa.Global
|
||||
program *Program
|
||||
LLVMGlobal llvm.Value
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
}
|
||||
|
||||
// Type with a name and possibly methods.
|
||||
type NamedType struct {
|
||||
*ssa.Type
|
||||
LLVMType llvm.Type
|
||||
}
|
||||
|
||||
// Type that is at some point put in an interface.
|
||||
type TypeWithMethods struct {
|
||||
t types.Type
|
||||
Num int
|
||||
Methods map[string]*types.Selection
|
||||
}
|
||||
|
||||
// Interface type that is at some point used in a type assert (to check whether
|
||||
@@ -43,27 +69,37 @@ type Interface struct {
|
||||
Type *types.Interface
|
||||
}
|
||||
|
||||
type InlineType int
|
||||
|
||||
// How much to inline.
|
||||
const (
|
||||
// Default behavior. The compiler decides for itself whether any given
|
||||
// function will be inlined. Whether any function is inlined depends on the
|
||||
// optimization level.
|
||||
InlineDefault InlineType = iota
|
||||
|
||||
// Inline hint, just like the C inline keyword (signalled using
|
||||
// //go:inline). The compiler will be more likely to inline this function,
|
||||
// but it is not a guarantee.
|
||||
InlineHint
|
||||
|
||||
// Don't inline, just like the GCC noinline attribute. Signalled using
|
||||
// //go:noinline.
|
||||
InlineNone
|
||||
)
|
||||
|
||||
// Create and initialize a new *Program from a *ssa.Program.
|
||||
// Create and intialize a new *Program from a *ssa.Program.
|
||||
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||
comments := map[string]*ast.CommentGroup{}
|
||||
for _, pkgInfo := range lprogram.Sorted() {
|
||||
for _, file := range pkgInfo.Files {
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch decl.Tok {
|
||||
case token.TYPE, token.VAR:
|
||||
if len(decl.Specs) != 1 {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
switch spec := spec.(type) {
|
||||
case *ast.TypeSpec: // decl.Tok == token.TYPE
|
||||
id := pkgInfo.Pkg.Path() + "." + spec.Name.Name
|
||||
comments[id] = decl.Doc
|
||||
case *ast.ValueSpec: // decl.Tok == token.VAR
|
||||
for _, name := range spec.Names {
|
||||
id := pkgInfo.Pkg.Path() + "." + name.Name
|
||||
comments[id] = decl.Doc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
program := lprogram.LoadSSA()
|
||||
program.Build()
|
||||
|
||||
@@ -135,6 +171,8 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||
LoaderProgram: lprogram,
|
||||
mainPkg: mainPkg,
|
||||
functionMap: make(map[*ssa.Function]*Function),
|
||||
globalMap: make(map[*ssa.Global]*Global),
|
||||
comments: comments,
|
||||
}
|
||||
|
||||
for _, pkg := range packageList {
|
||||
@@ -159,6 +197,8 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||
case *ssa.Function:
|
||||
p.addFunction(member)
|
||||
case *ssa.Type:
|
||||
t := &NamedType{Type: member}
|
||||
p.NamedTypes = append(p.NamedTypes, t)
|
||||
methods := getAllMethods(pkg.Prog, member.Type())
|
||||
if !types.IsInterface(member.Type()) {
|
||||
// named type
|
||||
@@ -167,7 +207,13 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||
}
|
||||
}
|
||||
case *ssa.Global:
|
||||
// Ignore. Globals are not handled here.
|
||||
g := &Global{program: p, Global: member}
|
||||
doc := p.comments[g.RelString(nil)]
|
||||
if doc != nil {
|
||||
g.parsePragmas(doc)
|
||||
}
|
||||
p.Globals = append(p.Globals, g)
|
||||
p.globalMap[member] = g
|
||||
case *ssa.NamedConst:
|
||||
// Ignore: these are already resolved.
|
||||
default:
|
||||
@@ -201,6 +247,10 @@ func (p *Program) GetFunction(ssaFn *ssa.Function) *Function {
|
||||
return p.functionMap[ssaFn]
|
||||
}
|
||||
|
||||
func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global {
|
||||
return p.globalMap[ssaGlobal]
|
||||
}
|
||||
|
||||
func (p *Program) MainPkg() *ssa.Package {
|
||||
return p.mainPkg
|
||||
}
|
||||
@@ -229,10 +279,6 @@ func (f *Function) parsePragmas() {
|
||||
}
|
||||
f.linkName = parts[1]
|
||||
f.exported = true
|
||||
case "//go:inline":
|
||||
f.inline = InlineHint
|
||||
case "//go:noinline":
|
||||
f.inline = InlineNone
|
||||
case "//go:interrupt":
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
@@ -286,11 +332,6 @@ func (f *Function) IsInterrupt() bool {
|
||||
return f.interrupt
|
||||
}
|
||||
|
||||
// Return the inline directive of this function.
|
||||
func (f *Function) Inline() InlineType {
|
||||
return f.inline
|
||||
}
|
||||
|
||||
// Return the link name for this function.
|
||||
func (f *Function) LinkName() string {
|
||||
if f.linkName != "" {
|
||||
@@ -325,6 +366,72 @@ func (f *Function) CName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Parse //go: pragma comments from the source.
|
||||
func (g *Global) parsePragmas(doc *ast.CommentGroup) {
|
||||
for _, comment := range doc.List {
|
||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(comment.Text)
|
||||
switch parts[0] {
|
||||
case "//go:extern":
|
||||
g.extern = true
|
||||
if len(parts) == 2 {
|
||||
g.linkName = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the link name for this global.
|
||||
func (g *Global) LinkName() string {
|
||||
if g.linkName != "" {
|
||||
return g.linkName
|
||||
}
|
||||
if name := g.CName(); name != "" {
|
||||
return name
|
||||
}
|
||||
return g.RelString(nil)
|
||||
}
|
||||
|
||||
func (g *Global) IsExtern() bool {
|
||||
return g.extern || g.CName() != ""
|
||||
}
|
||||
|
||||
// Return the name of the C global if this is a CGo wrapper. Otherwise, return a
|
||||
// zero-length string.
|
||||
func (g *Global) CName() string {
|
||||
name := g.Name()
|
||||
if strings.HasPrefix(name, "C.") {
|
||||
// created by ../loader/cgo.go
|
||||
return name[2:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Return true if this named type is annotated with the //go:volatile pragma,
|
||||
// for volatile loads and stores.
|
||||
func (p *Program) IsVolatile(t types.Type) bool {
|
||||
if t, ok := t.(*types.Named); !ok {
|
||||
return false
|
||||
} else {
|
||||
if t.Obj().Pkg() == nil {
|
||||
return false
|
||||
}
|
||||
id := t.Obj().Pkg().Path() + "." + t.Obj().Name()
|
||||
doc := p.comments[id]
|
||||
if doc == nil {
|
||||
return false
|
||||
}
|
||||
for _, line := range doc.List {
|
||||
if strings.TrimSpace(line.Text) == "//go:volatile" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Get all methods of a type.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
ms := prog.MethodSets.MethodSet(typ)
|
||||
|
||||
+1
-1
Submodule lib/cmsis-svd updated: 5910f3d115...b6f0a65ac3
@@ -63,7 +63,6 @@ func Link(linker string, flags ...string) error {
|
||||
cmd := exec.Command(linker, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = sourceDir()
|
||||
return cmd.Run()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,5 @@ func Link(linker string, flags ...string) error {
|
||||
cmd := exec.Command(linker, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = sourceDir()
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
+521
@@ -0,0 +1,521 @@
|
||||
package loader
|
||||
|
||||
// This file extracts the `import "C"` statement from the source and modifies
|
||||
// the AST for Cgo. It does not use libclang directly (see libclang.go).
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// fileInfo holds all Cgo-related information of a given *ast.File.
|
||||
type fileInfo struct {
|
||||
*ast.File
|
||||
*Package
|
||||
filename string
|
||||
functions map[string]*functionInfo
|
||||
globals map[string]*globalInfo
|
||||
typedefs map[string]*typedefInfo
|
||||
elaboratedTypes map[string]ast.Expr
|
||||
importCPos token.Pos
|
||||
missingSymbols map[string]struct{}
|
||||
}
|
||||
|
||||
// functionInfo stores some information about a Cgo function found by libclang
|
||||
// and declared in the AST.
|
||||
type functionInfo struct {
|
||||
args []paramInfo
|
||||
results *ast.FieldList
|
||||
}
|
||||
|
||||
// paramInfo is a parameter of a Cgo function (see functionInfo).
|
||||
type paramInfo struct {
|
||||
name string
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// typedefInfo contains information about a single typedef in C.
|
||||
type typedefInfo struct {
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// globalInfo contains information about a declared global variable in C.
|
||||
type globalInfo struct {
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||
// in both languages. See addTypeAliases.
|
||||
var cgoAliases = map[string]string{
|
||||
"C.int8_t": "int8",
|
||||
"C.int16_t": "int16",
|
||||
"C.int32_t": "int32",
|
||||
"C.int64_t": "int64",
|
||||
"C.uint8_t": "uint8",
|
||||
"C.uint16_t": "uint16",
|
||||
"C.uint32_t": "uint32",
|
||||
"C.uint64_t": "uint64",
|
||||
"C.uintptr_t": "uintptr",
|
||||
}
|
||||
|
||||
// cgoBuiltinAliases are handled specially because they only exist on the Go
|
||||
// side of CGo, not on the CGo (they're prefixed with "_Cgo_" there).
|
||||
var cgoBuiltinAliases = map[string]struct{}{
|
||||
"char": struct{}{},
|
||||
"schar": struct{}{},
|
||||
"uchar": struct{}{},
|
||||
"short": struct{}{},
|
||||
"ushort": struct{}{},
|
||||
"int": struct{}{},
|
||||
"uint": struct{}{},
|
||||
"long": struct{}{},
|
||||
"ulong": struct{}{},
|
||||
"longlong": struct{}{},
|
||||
"ulonglong": struct{}{},
|
||||
}
|
||||
|
||||
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
||||
// somehow from C. This is done by adding some typedefs to get the size of each
|
||||
// type.
|
||||
const cgoTypes = `
|
||||
typedef char _Cgo_char;
|
||||
typedef signed char _Cgo_schar;
|
||||
typedef unsigned char _Cgo_uchar;
|
||||
typedef short _Cgo_short;
|
||||
typedef unsigned short _Cgo_ushort;
|
||||
typedef int _Cgo_int;
|
||||
typedef unsigned int _Cgo_uint;
|
||||
typedef long _Cgo_long;
|
||||
typedef unsigned long _Cgo_ulong;
|
||||
typedef long long _Cgo_longlong;
|
||||
typedef unsigned long long _Cgo_ulonglong;
|
||||
`
|
||||
|
||||
// processCgo extracts the `import "C"` statement from the AST, parses the
|
||||
// comment with libclang, and modifies the AST to use this information.
|
||||
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []error {
|
||||
info := &fileInfo{
|
||||
File: f,
|
||||
Package: p,
|
||||
filename: filename,
|
||||
functions: map[string]*functionInfo{},
|
||||
globals: map[string]*globalInfo{},
|
||||
typedefs: map[string]*typedefInfo{},
|
||||
elaboratedTypes: map[string]ast.Expr{},
|
||||
missingSymbols: map[string]struct{}{},
|
||||
}
|
||||
|
||||
// Find all C.* symbols.
|
||||
f = astutil.Apply(f, info.findMissingCGoNames, nil).(*ast.File)
|
||||
for name := range cgoBuiltinAliases {
|
||||
info.missingSymbols["_Cgo_"+name] = struct{}{}
|
||||
}
|
||||
|
||||
// Find `import "C"` statements in the file.
|
||||
for i := 0; i < len(f.Decls); i++ {
|
||||
decl := f.Decls[i]
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(genDecl.Specs) != 1 {
|
||||
continue
|
||||
}
|
||||
spec, ok := genDecl.Specs[0].(*ast.ImportSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
path, err := strconv.Unquote(spec.Path.Value)
|
||||
if err != nil {
|
||||
panic("could not parse import path: " + err.Error())
|
||||
}
|
||||
if path != "C" {
|
||||
continue
|
||||
}
|
||||
cgoComment := genDecl.Doc.Text()
|
||||
|
||||
// Stored for later use by generated functions, to use a somewhat sane
|
||||
// source location.
|
||||
info.importCPos = spec.Path.ValuePos
|
||||
|
||||
pos := info.fset.PositionFor(genDecl.Doc.Pos(), true)
|
||||
errs := info.parseFragment(cgoComment+cgoTypes, cflags, pos.Filename, pos.Line)
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
|
||||
// Remove this import declaration.
|
||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||
i--
|
||||
}
|
||||
|
||||
// Print the AST, for debugging.
|
||||
//ast.Print(p.fset, f)
|
||||
|
||||
// Declare functions found by libclang.
|
||||
info.addFuncDecls()
|
||||
|
||||
// Declare stub function pointer values found by libclang.
|
||||
info.addFuncPtrDecls()
|
||||
|
||||
// Declare globals found by libclang.
|
||||
info.addVarDecls()
|
||||
|
||||
// Forward C types to Go types (like C.uint32_t -> uint32).
|
||||
info.addTypeAliases()
|
||||
|
||||
// Add type declarations for C types, declared using typedef in C.
|
||||
info.addTypedefs()
|
||||
|
||||
// Add elaborated types for C structs and unions.
|
||||
info.addElaboratedTypes()
|
||||
|
||||
// Patch the AST to use the declared types and functions.
|
||||
f = astutil.Apply(f, info.walker, nil).(*ast.File)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addFuncDecls adds the C function declarations found by libclang in the
|
||||
// comment above the `import "C"` statement.
|
||||
func (info *fileInfo) addFuncDecls() {
|
||||
// TODO: replace all uses of importCPos with the real locations from
|
||||
// libclang.
|
||||
names := make([]string, 0, len(info.functions))
|
||||
for name := range info.functions {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
fn := info.functions[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: "C." + name,
|
||||
}
|
||||
args := make([]*ast.Field, len(fn.args))
|
||||
decl := &ast.FuncDecl{
|
||||
Name: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: info.importCPos,
|
||||
Params: &ast.FieldList{
|
||||
Opening: info.importCPos,
|
||||
List: args,
|
||||
Closing: info.importCPos,
|
||||
},
|
||||
Results: fn.results,
|
||||
},
|
||||
}
|
||||
obj.Decl = decl
|
||||
for i, arg := range fn.args {
|
||||
args[i] = &ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: arg.name,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: arg.name,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: arg.typeExpr,
|
||||
}
|
||||
}
|
||||
info.Decls = append(info.Decls, decl)
|
||||
}
|
||||
}
|
||||
|
||||
// addFuncPtrDecls creates stub declarations of function pointer values. These
|
||||
// values will later be replaced with the real values in the compiler.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// var (
|
||||
// C.add unsafe.Pointer
|
||||
// C.mul unsafe.Pointer
|
||||
// // ...
|
||||
// )
|
||||
func (info *fileInfo) addFuncPtrDecls() {
|
||||
if len(info.functions) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: info.importCPos,
|
||||
Tok: token.VAR,
|
||||
Lparen: info.importCPos,
|
||||
Rparen: info.importCPos,
|
||||
}
|
||||
names := make([]string, 0, len(info.functions))
|
||||
for name := range info.functions {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "C." + name + "$funcaddr",
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{&ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "C." + name + "$funcaddr",
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "unsafe",
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "Pointer",
|
||||
},
|
||||
},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
}
|
||||
info.Decls = append(info.Decls, gen)
|
||||
}
|
||||
|
||||
// addVarDecls declares external C globals in the Go source.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// var (
|
||||
// C.globalInt int
|
||||
// C.globalBool bool
|
||||
// // ...
|
||||
// )
|
||||
func (info *fileInfo) addVarDecls() {
|
||||
if len(info.globals) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: info.importCPos,
|
||||
Tok: token.VAR,
|
||||
Lparen: info.importCPos,
|
||||
Rparen: info.importCPos,
|
||||
}
|
||||
names := make([]string, 0, len(info.globals))
|
||||
for name := range info.globals {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
global := info.globals[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{&ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: global.typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
}
|
||||
info.Decls = append(info.Decls, gen)
|
||||
}
|
||||
|
||||
// addTypeAliases aliases some built-in Go types with their equivalent C types.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// type (
|
||||
// C.int8_t = int8
|
||||
// C.int16_t = int16
|
||||
// // ...
|
||||
// )
|
||||
func (info *fileInfo) addTypeAliases() {
|
||||
aliasKeys := make([]string, 0, len(cgoAliases))
|
||||
for key := range cgoAliases {
|
||||
aliasKeys = append(aliasKeys, key)
|
||||
}
|
||||
sort.Strings(aliasKeys)
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: info.importCPos,
|
||||
Tok: token.TYPE,
|
||||
Lparen: info.importCPos,
|
||||
Rparen: info.importCPos,
|
||||
}
|
||||
for _, typeName := range aliasKeys {
|
||||
goTypeName := cgoAliases[typeName]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Assign: info.importCPos,
|
||||
Type: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: goTypeName,
|
||||
},
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
}
|
||||
info.Decls = append(info.Decls, gen)
|
||||
}
|
||||
|
||||
func (info *fileInfo) addTypedefs() {
|
||||
if len(info.typedefs) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: info.importCPos,
|
||||
Tok: token.TYPE,
|
||||
}
|
||||
names := make([]string, 0, len(info.typedefs))
|
||||
for name := range info.typedefs {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
typedef := info.typedefs[name]
|
||||
typeName := "C." + name
|
||||
isAlias := true
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
typeName = "C." + name[len("_Cgo_"):]
|
||||
isAlias = false // C.short etc. should not be aliased to the equivalent Go type (not portable)
|
||||
}
|
||||
if _, ok := cgoAliases[typeName]; ok {
|
||||
// This is a type that also exists in Go (defined in stdint.h).
|
||||
continue
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typedef.typeExpr,
|
||||
}
|
||||
if isAlias {
|
||||
typeSpec.Assign = info.importCPos
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
}
|
||||
info.Decls = append(info.Decls, gen)
|
||||
}
|
||||
|
||||
// addElaboratedTypes adds C elaborated types as aliases. These are the "struct
|
||||
// foo" or "union foo" types, often used in a typedef.
|
||||
//
|
||||
// See also:
|
||||
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
||||
func (info *fileInfo) addElaboratedTypes() {
|
||||
if len(info.elaboratedTypes) == 0 {
|
||||
return
|
||||
}
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: info.importCPos,
|
||||
Tok: token.TYPE,
|
||||
}
|
||||
names := make([]string, 0, len(info.elaboratedTypes))
|
||||
for name := range info.elaboratedTypes {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
typ := info.elaboratedTypes[name]
|
||||
typeName := "C." + name
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typ,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
}
|
||||
info.Decls = append(info.Decls, gen)
|
||||
}
|
||||
|
||||
// findMissingCGoNames traverses the AST and finds all C.something names. Only
|
||||
// these symbols are extracted from the parsed C AST and converted to the Go
|
||||
// equivalent.
|
||||
func (info *fileInfo) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
||||
switch node := cursor.Node().(type) {
|
||||
case *ast.SelectorExpr:
|
||||
x, ok := node.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if x.Name == "C" {
|
||||
name := node.Sel.Name
|
||||
if _, ok := cgoBuiltinAliases[name]; ok {
|
||||
name = "_Cgo_" + name
|
||||
}
|
||||
info.missingSymbols[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
||||
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
||||
// used in the middle of a name) so in practice all C identifiers live in a
|
||||
// separate namespace (no _Cgo_ hacks like in gc).
|
||||
func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
|
||||
switch node := cursor.Node().(type) {
|
||||
case *ast.CallExpr:
|
||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
x, ok := fun.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := info.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
||||
node.Fun = &ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: "C." + fun.Sel.Name,
|
||||
}
|
||||
}
|
||||
case *ast.SelectorExpr:
|
||||
x, ok := node.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if x.Name == "C" {
|
||||
name := "C." + node.Sel.Name
|
||||
if _, ok := info.functions[node.Sel.Name]; ok {
|
||||
name += "$funcaddr"
|
||||
}
|
||||
cursor.Replace(&ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cgo
|
||||
package loader
|
||||
|
||||
// This file parses a fragment of C with libclang and stores the result for AST
|
||||
// modification. It does not touch the AST itself.
|
||||
@@ -47,20 +47,15 @@ CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
||||
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
||||
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
||||
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
||||
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||
long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
|
||||
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
|
||||
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
||||
|
||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// storedRefs stores references to types, used for clang_visitChildren.
|
||||
var storedRefs refMap
|
||||
// refMap stores references to types, used for clang_visitChildren.
|
||||
var refMap RefMap
|
||||
|
||||
var diagnosticSeverity = [...]string{
|
||||
C.CXDiagnostic_Ignored: "ignored",
|
||||
@@ -70,7 +65,7 @@ var diagnosticSeverity = [...]string{
|
||||
C.CXDiagnostic_Fatal: "fatal",
|
||||
}
|
||||
|
||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename string, posLine int) {
|
||||
func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilename string, posLine int) []error {
|
||||
index := C.clang_createIndex(0, 0)
|
||||
defer C.clang_disposeIndex(index)
|
||||
|
||||
@@ -106,7 +101,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
||||
filenameC,
|
||||
(**C.char)(cmdargsC), C.int(len(cflags)), // command line args
|
||||
&unsavedFile, 1, // unsaved files
|
||||
C.CXTranslationUnit_DetailedPreprocessingRecord,
|
||||
C.CXTranslationUnit_None,
|
||||
&unit)
|
||||
if errCode != 0 {
|
||||
panic("loader: failed to parse source with libclang")
|
||||
@@ -114,6 +109,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
||||
defer C.clang_disposeTranslationUnit(unit)
|
||||
|
||||
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
||||
errs := []error{}
|
||||
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||
@@ -125,12 +121,12 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
||||
filename := getString(libclangFilename)
|
||||
if filepath.IsAbs(filename) {
|
||||
// Relative paths for readability, like other Go parser errors.
|
||||
relpath, err := filepath.Rel(p.dir, filename)
|
||||
relpath, err := filepath.Rel(info.Program.Dir, filename)
|
||||
if err == nil {
|
||||
filename = relpath
|
||||
}
|
||||
}
|
||||
p.errors = append(p.errors, &scanner.Error{
|
||||
errs = append(errs, &scanner.Error{
|
||||
Pos: token.Position{
|
||||
Filename: filename,
|
||||
Offset: 0, // not provided by clang_getPresumedLocation
|
||||
@@ -150,24 +146,26 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
||||
addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j)))
|
||||
}
|
||||
}
|
||||
return
|
||||
return errs
|
||||
}
|
||||
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
ref := refMap.Put(info)
|
||||
defer refMap.Remove(ref)
|
||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//export tinygo_clang_globals_visitor
|
||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||
info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo)
|
||||
kind := C.tinygo_clang_getCursorKind(c)
|
||||
pos := p.getCursorPosition(c)
|
||||
pos := info.getCursorPosition(c)
|
||||
switch kind {
|
||||
case C.CXCursor_FunctionDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
if _, required := info.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
@@ -175,10 +173,8 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
return C.CXChildVisit_Continue // not supported
|
||||
}
|
||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||
fn := &functionInfo{
|
||||
pos: pos,
|
||||
}
|
||||
p.functions[name] = fn
|
||||
fn := &functionInfo{}
|
||||
info.functions[name] = fn
|
||||
for i := 0; i < numArgs; i++ {
|
||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||
@@ -188,7 +184,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
}
|
||||
fn.args = append(fn.args, paramInfo{
|
||||
name: argName,
|
||||
typeExpr: p.makeASTType(argType, pos),
|
||||
typeExpr: info.makeASTType(argType, pos),
|
||||
})
|
||||
}
|
||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||
@@ -196,7 +192,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
fn.results = &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Type: p.makeASTType(resultType, pos),
|
||||
Type: info.makeASTType(resultType, pos),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -204,106 +200,25 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
||||
case C.CXCursor_StructDecl:
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols["struct_"+name]; !required {
|
||||
if _, required := info.missingSymbols["struct_"+name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
p.makeASTType(typ, pos)
|
||||
info.makeASTType(typ, pos)
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typedefType := C.tinygo_clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
if _, required := info.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
p.makeASTType(typedefType, pos)
|
||||
info.makeASTType(typedefType, pos)
|
||||
case C.CXCursor_VarDecl:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
if _, required := info.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
cursorType := C.tinygo_clang_getCursorType(c)
|
||||
p.globals[name] = globalInfo{
|
||||
typeExpr: p.makeASTType(cursorType, pos),
|
||||
pos: pos,
|
||||
}
|
||||
case C.CXCursor_MacroDefinition:
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if _, required := p.missingSymbols[name]; !required {
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||
start := C.clang_getRangeStart(sourceRange)
|
||||
end := C.clang_getRangeEnd(sourceRange)
|
||||
var file, endFile C.CXFile
|
||||
var startOffset, endOffset C.unsigned
|
||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||
if file == nil {
|
||||
panic("could not find file where macro is defined")
|
||||
}
|
||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||
if file != endFile {
|
||||
panic("expected start and end location of a #define to be in the same file")
|
||||
}
|
||||
if startOffset > endOffset {
|
||||
panic("startOffset > endOffset")
|
||||
}
|
||||
|
||||
// read file contents and extract the relevant byte range
|
||||
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
|
||||
var size C.size_t
|
||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||
if endOffset >= C.uint(size) {
|
||||
panic("endOffset lies after end of file")
|
||||
}
|
||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||
if !strings.HasPrefix(source, name) {
|
||||
panic(fmt.Sprintf("expected #define value to start with %#v, got %#v", name, source))
|
||||
}
|
||||
value := strings.TrimSpace(source[len(name):])
|
||||
for len(value) != 0 && value[0] == '(' && value[len(value)-1] == ')' {
|
||||
value = strings.TrimSpace(value[1 : len(value)-1])
|
||||
}
|
||||
if len(value) == 0 {
|
||||
// Pretend it doesn't exist at all.
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
// For information about integer literals:
|
||||
// https://en.cppreference.com/w/cpp/language/integer_literal
|
||||
if value[0] == '"' {
|
||||
// string constant
|
||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.STRING, value}, pos}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
if value[0] == '\'' {
|
||||
// char constant
|
||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.CHAR, value}, pos}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
// assume it's a number (int or float)
|
||||
value = strings.Replace(value, "'", "", -1) // remove ' chars
|
||||
value = strings.TrimRight(value, "lu") // remove llu suffixes etc.
|
||||
// find the first non-number
|
||||
nonnum := byte(0)
|
||||
for i := 0; i < len(value); i++ {
|
||||
if value[i] < '0' || value[i] > '9' {
|
||||
nonnum = value[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
// determine number type based on the first non-number
|
||||
switch nonnum {
|
||||
case 0:
|
||||
// no non-number found, must be an integer
|
||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.INT, value}, pos}
|
||||
case 'x', 'X':
|
||||
// hex integer constant
|
||||
// TODO: may also be a floating point number per C++17.
|
||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.INT, value}, pos}
|
||||
case '.', 'e':
|
||||
// float constant
|
||||
value = strings.TrimRight(value, "fFlL")
|
||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.FLOAT, value}, pos}
|
||||
default:
|
||||
// unknown type, ignore
|
||||
info.globals[name] = &globalInfo{
|
||||
typeExpr: info.makeASTType(cursorType, pos),
|
||||
}
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
@@ -319,7 +234,7 @@ func getString(clangString C.CXString) (s string) {
|
||||
// getCursorPosition returns a usable token.Pos from a libclang cursor. If the
|
||||
// file for this cursor has not been seen before, it is read from libclang
|
||||
// (which already has the file in memory) and added to the token.FileSet.
|
||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||
func (info *fileInfo) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||
location := C.tinygo_clang_getCursorLocation(cursor)
|
||||
var file C.CXFile
|
||||
var line C.unsigned
|
||||
@@ -331,7 +246,7 @@ func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||
return token.NoPos
|
||||
}
|
||||
filename := getString(C.clang_getFileName(file))
|
||||
if _, ok := p.tokenFiles[filename]; !ok {
|
||||
if _, ok := info.tokenFiles[filename]; !ok {
|
||||
// File has not been seen before in this package, add line information
|
||||
// now by reading the file from libclang.
|
||||
tu := C.tinygo_clang_Cursor_getTranslationUnit(cursor)
|
||||
@@ -344,16 +259,16 @@ func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||
lines = append(lines, i+1)
|
||||
}
|
||||
}
|
||||
f := p.fset.AddFile(filename, -1, int(size))
|
||||
f := info.fset.AddFile(filename, -1, int(size))
|
||||
f.SetLines(lines)
|
||||
p.tokenFiles[filename] = f
|
||||
info.tokenFiles[filename] = f
|
||||
}
|
||||
return p.tokenFiles[filename].Pos(int(offset))
|
||||
return info.tokenFiles[filename].Pos(int(offset))
|
||||
}
|
||||
|
||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||
// it converts a libclang type to a type in the Go AST.
|
||||
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
var typeName string
|
||||
switch typ.kind {
|
||||
case C.CXType_Char_S, C.CXType_Char_U:
|
||||
@@ -414,7 +329,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
return &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: p.makeASTType(pointeeType, pos),
|
||||
X: info.makeASTType(pointeeType, pos),
|
||||
}
|
||||
case C.CXType_ConstantArray:
|
||||
return &ast.ArrayType{
|
||||
@@ -424,7 +339,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||
},
|
||||
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
||||
Elt: info.makeASTType(C.clang_getElementType(typ), pos),
|
||||
}
|
||||
case C.CXType_FunctionProto:
|
||||
// Be compatible with gc, which uses the *[0]byte type for function
|
||||
@@ -445,11 +360,11 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
case C.CXType_Typedef:
|
||||
name := getString(C.clang_getTypedefName(typ))
|
||||
if _, ok := p.typedefs[name]; !ok {
|
||||
p.typedefs[name] = nil // don't recurse
|
||||
if _, ok := info.typedefs[name]; !ok {
|
||||
info.typedefs[name] = nil // don't recurse
|
||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||
expr := p.makeASTType(underlyingType, pos)
|
||||
expr := info.makeASTType(underlyingType, pos)
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
expr := expr.(*ast.Ident)
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
@@ -491,9 +406,8 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
}
|
||||
}
|
||||
p.typedefs[name] = &typedefInfo{
|
||||
info.typedefs[name] = &typedefInfo{
|
||||
typeExpr: expr,
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
return &ast.Ident{
|
||||
@@ -504,95 +418,85 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
underlying := C.clang_Type_getNamedType(typ)
|
||||
switch underlying.kind {
|
||||
case C.CXType_Record:
|
||||
return p.makeASTType(underlying, pos)
|
||||
case C.CXType_Enum:
|
||||
return p.makeASTType(underlying, pos)
|
||||
return info.makeASTType(underlying, pos)
|
||||
default:
|
||||
panic("unknown elaborated type")
|
||||
}
|
||||
case C.CXType_Record:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
if name == "" {
|
||||
// Anonymous record, probably inside a typedef.
|
||||
typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
|
||||
if bitfieldList != nil {
|
||||
// This struct has bitfields, so we have to declare it as a
|
||||
// named type (for bitfield getters/setters to work).
|
||||
p.anonStructNum++
|
||||
cgoName := "struct_" + strconv.Itoa(p.anonStructNum)
|
||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||
typeExpr: typeExpr,
|
||||
pos: pos,
|
||||
bitfields: bitfieldList,
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
var cgoName string
|
||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||
case C.CXCursor_StructDecl:
|
||||
cgoName = "struct_" + name
|
||||
case C.CXCursor_UnionDecl:
|
||||
cgoName = "union_" + name
|
||||
default:
|
||||
panic("unknown record declaration")
|
||||
}
|
||||
if _, ok := info.elaboratedTypes[cgoName]; !ok {
|
||||
info.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||
fieldList := &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
}
|
||||
return typeExpr
|
||||
} else {
|
||||
var cgoName string
|
||||
ref := refMap.Put(struct {
|
||||
fieldList *ast.FieldList
|
||||
info *fileInfo
|
||||
}{fieldList, info})
|
||||
defer refMap.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||
case C.CXCursor_StructDecl:
|
||||
cgoName = "struct_" + name
|
||||
case C.CXCursor_UnionDecl:
|
||||
cgoName = "union_" + name
|
||||
default:
|
||||
panic("unknown record declaration")
|
||||
}
|
||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||
typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
|
||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||
typeExpr: typeExpr,
|
||||
pos: pos,
|
||||
bitfields: bitfieldList,
|
||||
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
case C.CXCursor_UnionDecl:
|
||||
if len(fieldList.List) > 1 {
|
||||
// Insert a special field at the front (of zero width) as a
|
||||
// marker that this is struct is actually a union. This is done
|
||||
// by giving the field a name that cannot be expressed directly
|
||||
// in Go.
|
||||
// Other parts of the compiler look at the first element in a
|
||||
// struct (of size > 2) to know whether this is a union.
|
||||
// Note that we don't have to insert it for single-element
|
||||
// unions as they're basically equivalent to a struct.
|
||||
unionMarker := &ast.Field{
|
||||
Type: &ast.StructType{
|
||||
Struct: pos,
|
||||
},
|
||||
}
|
||||
unionMarker.Names = []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C union",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C union",
|
||||
Decl: unionMarker,
|
||||
},
|
||||
},
|
||||
}
|
||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||
}
|
||||
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
case C.CXType_Enum:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
|
||||
if name == "" {
|
||||
// anonymous enum
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||
return p.makeASTType(underlying, pos)
|
||||
} else {
|
||||
// named enum
|
||||
if _, ok := p.enums[name]; !ok {
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||
p.enums[name] = enumInfo{
|
||||
typeExpr: p.makeASTType(underlying, pos),
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C.enum_" + name,
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
}
|
||||
if typeName == "" {
|
||||
// Report this as an error.
|
||||
spelling := getString(C.clang_getTypeSpelling(typ))
|
||||
p.errors = append(p.errors, scanner.Error{
|
||||
Pos: p.fset.PositionFor(pos, true),
|
||||
Msg: fmt.Sprintf("unknown C type: %v (libclang type kind %d)", spelling, typ.kind),
|
||||
})
|
||||
// Fallback, probably incorrect but at least the error points to an odd
|
||||
// type name.
|
||||
typeName = "C." + spelling
|
||||
typeName = "C." + getString(C.clang_getTypeSpelling(typ))
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
@@ -600,143 +504,25 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||
}
|
||||
}
|
||||
|
||||
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||
// a Go struct type. Unions are implemented by setting the first field to a
|
||||
// zero-lengt "C union" field, which cannot be written in Go directly.
|
||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) (*ast.StructType, []bitfieldInfo) {
|
||||
fieldList := &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
}
|
||||
var bitfieldList []bitfieldInfo
|
||||
inBitfield := false
|
||||
bitfieldNum := 0
|
||||
ref := storedRefs.Put(struct {
|
||||
fieldList *ast.FieldList
|
||||
pkg *cgoPackage
|
||||
inBitfield *bool
|
||||
bitfieldNum *int
|
||||
bitfieldList *[]bitfieldInfo
|
||||
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
|
||||
defer storedRefs.Remove(ref)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||
case C.CXCursor_StructDecl:
|
||||
return &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}, bitfieldList
|
||||
case C.CXCursor_UnionDecl:
|
||||
if bitfieldList != nil {
|
||||
// This is valid C... but please don't do this.
|
||||
p.errors = append(p.errors, scanner.Error{
|
||||
Pos: p.fset.PositionFor(pos, true),
|
||||
Msg: fmt.Sprintf("bitfield in a union is not supported"),
|
||||
})
|
||||
}
|
||||
if len(fieldList.List) > 1 {
|
||||
// Insert a special field at the front (of zero width) as a
|
||||
// marker that this is struct is actually a union. This is done
|
||||
// by giving the field a name that cannot be expressed directly
|
||||
// in Go.
|
||||
// Other parts of the compiler look at the first element in a
|
||||
// struct (of size > 2) to know whether this is a union.
|
||||
// Note that we don't have to insert it for single-element
|
||||
// unions as they're basically equivalent to a struct.
|
||||
unionMarker := &ast.Field{
|
||||
Type: &ast.StructType{
|
||||
Struct: pos,
|
||||
},
|
||||
}
|
||||
unionMarker.Names = []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C union",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C union",
|
||||
Decl: unionMarker,
|
||||
},
|
||||
},
|
||||
}
|
||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||
}
|
||||
return &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}, bitfieldList
|
||||
default:
|
||||
panic("unknown record declaration")
|
||||
}
|
||||
}
|
||||
|
||||
//export tinygo_clang_struct_visitor
|
||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||
fieldList *ast.FieldList
|
||||
pkg *cgoPackage
|
||||
inBitfield *bool
|
||||
bitfieldNum *int
|
||||
bitfieldList *[]bitfieldInfo
|
||||
passed := refMap.Get(unsafe.Pointer(client_data)).(struct {
|
||||
fieldList *ast.FieldList
|
||||
info *fileInfo
|
||||
})
|
||||
fieldList := passed.fieldList
|
||||
p := passed.pkg
|
||||
inBitfield := passed.inBitfield
|
||||
bitfieldNum := passed.bitfieldNum
|
||||
bitfieldList := passed.bitfieldList
|
||||
info := passed.info
|
||||
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
|
||||
panic("expected field inside cursor")
|
||||
}
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
if name == "" {
|
||||
// Assume this is a bitfield of 0 bits.
|
||||
// Warning: this is not necessarily true!
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
typ := C.tinygo_clang_getCursorType(c)
|
||||
pos := p.getCursorPosition(c)
|
||||
field := &ast.Field{
|
||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
||||
Type: info.makeASTType(typ, info.getCursorPosition(c)),
|
||||
}
|
||||
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
|
||||
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
|
||||
bitfieldOffset := offsetof % alignOf
|
||||
if bitfieldOffset != 0 {
|
||||
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
|
||||
panic("expected a bitfield")
|
||||
}
|
||||
if !*inBitfield {
|
||||
*bitfieldNum++
|
||||
}
|
||||
bitfieldName := "__bitfield_" + strconv.Itoa(*bitfieldNum)
|
||||
prevField := fieldList.List[len(fieldList.List)-1]
|
||||
if !*inBitfield {
|
||||
// The previous element also was a bitfield, but wasn't noticed
|
||||
// then. Add it now.
|
||||
*inBitfield = true
|
||||
*bitfieldList = append(*bitfieldList, bitfieldInfo{
|
||||
field: prevField,
|
||||
name: prevField.Names[0].Name,
|
||||
startBit: 0,
|
||||
pos: prevField.Names[0].NamePos,
|
||||
})
|
||||
prevField.Names[0].Name = bitfieldName
|
||||
prevField.Names[0].Obj.Name = bitfieldName
|
||||
}
|
||||
prevBitfield := &(*bitfieldList)[len(*bitfieldList)-1]
|
||||
prevBitfield.endBit = bitfieldOffset
|
||||
*bitfieldList = append(*bitfieldList, bitfieldInfo{
|
||||
field: prevField,
|
||||
name: name,
|
||||
startBit: bitfieldOffset,
|
||||
pos: pos,
|
||||
})
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
*inBitfield = false
|
||||
field.Names = []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: pos,
|
||||
NamePos: info.getCursorPosition(c),
|
||||
Name: name,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
@@ -748,16 +534,3 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
||||
fieldList.List = append(fieldList.List, field)
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
//export tinygo_clang_enum_visitor
|
||||
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||
pos := p.getCursorPosition(c)
|
||||
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||
p.constants[name] = constantInfo{
|
||||
expr: &ast.BasicLit{pos, token.INT, strconv.FormatInt(int64(value), 10)},
|
||||
pos: pos,
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build !byollvm
|
||||
|
||||
package cgo
|
||||
package loader
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-8/include
|
||||
@@ -49,22 +49,6 @@ CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
||||
return clang_getCursorLocation(c);
|
||||
}
|
||||
|
||||
CXSourceRange tinygo_clang_getCursorExtent(CXCursor c) {
|
||||
return clang_getCursorExtent(c);
|
||||
}
|
||||
|
||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor c) {
|
||||
return clang_Cursor_getTranslationUnit(c);
|
||||
}
|
||||
|
||||
long long tinygo_clang_getEnumConstantDeclValue(CXCursor c) {
|
||||
return clang_getEnumConstantDeclValue(c);
|
||||
}
|
||||
|
||||
CXType tinygo_clang_getEnumDeclIntegerType(CXCursor c) {
|
||||
return clang_getEnumDeclIntegerType(c);
|
||||
}
|
||||
|
||||
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
|
||||
return clang_Cursor_isBitField(c);
|
||||
}
|
||||
+41
-156
@@ -1,7 +1,6 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
@@ -11,36 +10,31 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/tinygo-org/tinygo/cgo"
|
||||
)
|
||||
|
||||
// Program holds all packages and some metadata about the program as a whole.
|
||||
type Program struct {
|
||||
mainPkg string
|
||||
Build *build.Context
|
||||
OverlayBuild *build.Context
|
||||
OverlayPath func(path string) string
|
||||
Packages map[string]*Package
|
||||
sorted []*Package
|
||||
fset *token.FileSet
|
||||
TypeChecker types.Config
|
||||
Dir string // current working directory (for error reporting)
|
||||
TINYGOROOT string // root of the TinyGo installation or root of the source code
|
||||
CFlags []string
|
||||
ClangHeaders string
|
||||
Build *build.Context
|
||||
OverlayBuild *build.Context
|
||||
ShouldOverlay func(path string) bool
|
||||
Packages map[string]*Package
|
||||
sorted []*Package
|
||||
fset *token.FileSet
|
||||
TypeChecker types.Config
|
||||
Dir string // current working directory (for error reporting)
|
||||
TINYGOROOT string // root of the TinyGo installation or root of the source code
|
||||
CFlags []string
|
||||
}
|
||||
|
||||
// Package holds a loaded package, its imports, and its parsed files.
|
||||
type Package struct {
|
||||
*Program
|
||||
*build.Package
|
||||
Imports map[string]*Package
|
||||
Importing bool
|
||||
Files []*ast.File
|
||||
Pkg *types.Package
|
||||
Imports map[string]*Package
|
||||
Importing bool
|
||||
Files []*ast.File
|
||||
tokenFiles map[string]*token.File
|
||||
Pkg *types.Package
|
||||
types.Info
|
||||
}
|
||||
|
||||
@@ -53,9 +47,8 @@ func (p *Program) Import(path, srcDir string) (*Package, error) {
|
||||
|
||||
// Load this package.
|
||||
ctx := p.Build
|
||||
if newPath := p.OverlayPath(path); newPath != "" {
|
||||
if p.ShouldOverlay(path) {
|
||||
ctx = p.OverlayBuild
|
||||
path = newPath
|
||||
}
|
||||
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
||||
if err != nil {
|
||||
@@ -68,11 +61,6 @@ func (p *Program) Import(path, srcDir string) (*Package, error) {
|
||||
p.sorted = nil // invalidate the sorted order of packages
|
||||
pkg := p.newPackage(buildPkg)
|
||||
p.Packages[buildPkg.ImportPath] = pkg
|
||||
|
||||
if p.mainPkg == "" {
|
||||
p.mainPkg = buildPkg.ImportPath
|
||||
}
|
||||
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
@@ -102,11 +90,6 @@ func (p *Program) ImportFile(path string) (*Package, error) {
|
||||
p.sorted = nil // invalidate the sorted order of packages
|
||||
pkg := p.newPackage(buildPkg)
|
||||
p.Packages[buildPkg.ImportPath] = pkg
|
||||
|
||||
if p.mainPkg == "" {
|
||||
p.mainPkg = buildPkg.ImportPath
|
||||
}
|
||||
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
@@ -124,6 +107,7 @@ func (p *Program) newPackage(pkg *build.Package) *Package {
|
||||
Scopes: make(map[ast.Node]*types.Scope),
|
||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||
},
|
||||
tokenFiles: map[string]*token.File{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,12 +169,10 @@ func (p *Program) sort() {
|
||||
// The returned error may be an Errors error, which contains a list of errors.
|
||||
//
|
||||
// Idempotent.
|
||||
func (p *Program) Parse(compileTestBinary bool) error {
|
||||
includeTests := compileTestBinary
|
||||
|
||||
func (p *Program) Parse() error {
|
||||
// Load all imports
|
||||
for _, pkg := range p.Sorted() {
|
||||
err := pkg.importRecursively(includeTests)
|
||||
err := pkg.importRecursively()
|
||||
if err != nil {
|
||||
if err, ok := err.(*ImportCycleError); ok {
|
||||
if pkg.ImportPath != err.Packages[0] {
|
||||
@@ -203,14 +185,7 @@ func (p *Program) Parse(compileTestBinary bool) error {
|
||||
|
||||
// Parse all packages.
|
||||
for _, pkg := range p.Sorted() {
|
||||
err := pkg.Parse(includeTests)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if compileTestBinary {
|
||||
err := p.SwapTestMain()
|
||||
err := pkg.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -227,83 +202,6 @@ func (p *Program) Parse(compileTestBinary bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Program) SwapTestMain() error {
|
||||
var tests []string
|
||||
|
||||
isTestFunc := func(f *ast.FuncDecl) bool {
|
||||
// TODO: improve signature check
|
||||
if strings.HasPrefix(f.Name.Name, "Test") && f.Name.Name != "TestMain" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
mainPkg := p.Packages[p.mainPkg]
|
||||
for _, f := range mainPkg.Files {
|
||||
for i, d := range f.Decls {
|
||||
switch v := d.(type) {
|
||||
case *ast.FuncDecl:
|
||||
if isTestFunc(v) {
|
||||
tests = append(tests, v.Name.Name)
|
||||
}
|
||||
if v.Name.Name == "main" {
|
||||
// Remove main
|
||||
if len(f.Decls) == 1 {
|
||||
f.Decls = make([]ast.Decl, 0)
|
||||
} else {
|
||||
f.Decls[i] = f.Decls[len(f.Decls)-1]
|
||||
f.Decls = f.Decls[:len(f.Decls)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Check if they defined a TestMain and call it instead of testing.TestMain
|
||||
const mainBody = `package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func main () {
|
||||
m := &testing.M{
|
||||
Tests: []testing.TestToCall{
|
||||
{{range .TestFunctions}}
|
||||
{Name: "{{.}}", Func: {{.}}},
|
||||
{{end}}
|
||||
},
|
||||
}
|
||||
|
||||
testing.TestMain(m)
|
||||
}
|
||||
`
|
||||
tmpl := template.Must(template.New("testmain").Parse(mainBody))
|
||||
b := bytes.Buffer{}
|
||||
tmplData := struct {
|
||||
TestFunctions []string
|
||||
}{
|
||||
TestFunctions: tests,
|
||||
}
|
||||
|
||||
err := tmpl.Execute(&b, tmplData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(p.mainPkg, "$testmain.go")
|
||||
|
||||
if p.fset == nil {
|
||||
p.fset = token.NewFileSet()
|
||||
}
|
||||
|
||||
newMain, err := parser.ParseFile(p.fset, path, b.Bytes(), parser.AllErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mainPkg.Files = append(mainPkg.Files, newMain)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseFile is a wrapper around parser.ParseFile.
|
||||
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||
if p.fset == nil {
|
||||
@@ -328,7 +226,7 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||
// Parse parses and typechecks this package.
|
||||
//
|
||||
// Idempotent.
|
||||
func (p *Package) Parse(includeTests bool) error {
|
||||
func (p *Package) Parse() error {
|
||||
if len(p.Files) != 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -342,7 +240,7 @@ func (p *Package) Parse(includeTests bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
files, err := p.parseFiles(includeTests)
|
||||
files, err := p.parseFiles()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -381,21 +279,11 @@ func (p *Package) Check() error {
|
||||
}
|
||||
|
||||
// parseFiles parses the loaded list of files and returns this list.
|
||||
func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
|
||||
func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
// TODO: do this concurrently.
|
||||
var files []*ast.File
|
||||
var fileErrs []error
|
||||
|
||||
var gofiles []string
|
||||
if includeTests {
|
||||
gofiles = make([]string, 0, len(p.GoFiles)+len(p.TestGoFiles))
|
||||
gofiles = append(gofiles, p.GoFiles...)
|
||||
gofiles = append(gofiles, p.TestGoFiles...)
|
||||
} else {
|
||||
gofiles = p.GoFiles
|
||||
}
|
||||
|
||||
for _, file := range gofiles {
|
||||
for _, file := range p.GoFiles {
|
||||
f, err := p.parseFile(filepath.Join(p.Package.Dir, file), parser.ParseComments)
|
||||
if err != nil {
|
||||
fileErrs = append(fileErrs, err)
|
||||
@@ -407,6 +295,16 @@ func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
|
||||
}
|
||||
files = append(files, f)
|
||||
}
|
||||
clangIncludes := ""
|
||||
if len(p.CgoFiles) != 0 {
|
||||
if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) {
|
||||
// Running from the source directory.
|
||||
clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
||||
} else {
|
||||
// Running from the installation directory.
|
||||
clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include")
|
||||
}
|
||||
}
|
||||
for _, file := range p.CgoFiles {
|
||||
path := filepath.Join(p.Package.Dir, file)
|
||||
f, err := p.parseFile(path, parser.ParseComments)
|
||||
@@ -414,23 +312,16 @@ func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
|
||||
fileErrs = append(fileErrs, err)
|
||||
continue
|
||||
}
|
||||
files = append(files, f)
|
||||
}
|
||||
if len(p.CgoFiles) != 0 {
|
||||
cflags := append(p.CFlags, "-I"+p.Package.Dir)
|
||||
if p.ClangHeaders != "" {
|
||||
cflags = append(cflags, "-I"+p.ClangHeaders)
|
||||
}
|
||||
generated, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
|
||||
errs := p.processCgo(path, f, append(p.CFlags, "-I"+p.Package.Dir, "-I"+clangIncludes))
|
||||
if errs != nil {
|
||||
fileErrs = append(fileErrs, errs...)
|
||||
continue
|
||||
}
|
||||
files = append(files, generated)
|
||||
files = append(files, f)
|
||||
}
|
||||
if len(fileErrs) != 0 {
|
||||
return nil, Errors{p, fileErrs}
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
@@ -451,17 +342,11 @@ func (p *Package) Import(to string) (*types.Package, error) {
|
||||
// importRecursively() on the imported packages as well.
|
||||
//
|
||||
// Idempotent.
|
||||
func (p *Package) importRecursively(includeTests bool) error {
|
||||
func (p *Package) importRecursively() error {
|
||||
p.Importing = true
|
||||
|
||||
imports := p.Package.Imports
|
||||
if includeTests {
|
||||
imports = append(imports, p.Package.TestImports...)
|
||||
}
|
||||
|
||||
for _, to := range imports {
|
||||
for _, to := range p.Package.Imports {
|
||||
if to == "C" {
|
||||
// Do CGo processing in a later stage.
|
||||
// Do Cgo processing in a later stage.
|
||||
continue
|
||||
}
|
||||
if _, ok := p.Imports[to]; ok {
|
||||
@@ -477,7 +362,7 @@ func (p *Package) importRecursively(includeTests bool) error {
|
||||
if importedPkg.Importing {
|
||||
return &ImportCycleError{[]string{p.ImportPath, importedPkg.ImportPath}, p.ImportPos[to]}
|
||||
}
|
||||
err = importedPkg.importRecursively(false)
|
||||
err = importedPkg.importRecursively()
|
||||
if err != nil {
|
||||
if err, ok := err.(*ImportCycleError); ok {
|
||||
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cgo
|
||||
package loader
|
||||
|
||||
import (
|
||||
"sync"
|
||||
@@ -8,17 +8,17 @@ import (
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
|
||||
// refMap is a convenient way to store opaque references that can be passed to
|
||||
// RefMap is a convenient way to store opaque references that can be passed to
|
||||
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
||||
// pointer but only a C pointer.
|
||||
type refMap struct {
|
||||
type RefMap struct {
|
||||
refs map[unsafe.Pointer]interface{}
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Put stores a value in the map. It can later be retrieved using Get. It must
|
||||
// be removed using Remove to avoid memory leaks.
|
||||
func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
||||
func (m *RefMap) Put(v interface{}) unsafe.Pointer {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
if m.refs == nil {
|
||||
@@ -31,14 +31,14 @@ func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
||||
|
||||
// Get returns a stored value previously inserted with Put. Use the same
|
||||
// reference as you got from Put.
|
||||
func (m *refMap) Get(ref unsafe.Pointer) interface{} {
|
||||
func (m *RefMap) Get(ref unsafe.Pointer) interface{} {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
return m.refs[ref]
|
||||
}
|
||||
|
||||
// Remove deletes a single reference from the map.
|
||||
func (m *refMap) Remove(ref unsafe.Pointer) {
|
||||
func (m *RefMap) Remove(ref unsafe.Pointer) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
delete(m.refs, ref)
|
||||
@@ -53,10 +53,7 @@ type BuildConfig struct {
|
||||
printSizes string
|
||||
cFlags []string
|
||||
ldFlags []string
|
||||
tags string
|
||||
wasmAbi string
|
||||
heapSize int64
|
||||
testConfig compiler.TestConfig
|
||||
}
|
||||
|
||||
// Helper function for Compiler object.
|
||||
@@ -83,38 +80,21 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
if goroot == "" {
|
||||
return errors.New("cannot locate $GOROOT, please set it manually")
|
||||
}
|
||||
tags := spec.BuildTags
|
||||
major, minor, err := getGorootVersion(goroot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
if major != 1 {
|
||||
return fmt.Errorf("expected major version 1, got go%d.%d", major, minor)
|
||||
}
|
||||
for i := 1; i <= minor; i++ {
|
||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||
}
|
||||
if extraTags := strings.Fields(config.tags); len(extraTags) != 0 {
|
||||
tags = append(tags, extraTags...)
|
||||
}
|
||||
compilerConfig := compiler.Config{
|
||||
Triple: spec.Triple,
|
||||
CPU: spec.CPU,
|
||||
Features: spec.Features,
|
||||
GOOS: spec.GOOS,
|
||||
GOARCH: spec.GOARCH,
|
||||
GC: config.gc,
|
||||
PanicStrategy: config.panicStrategy,
|
||||
CFlags: cflags,
|
||||
LDFlags: ldflags,
|
||||
ClangHeaders: getClangHeaderPath(root),
|
||||
Debug: config.debug,
|
||||
DumpSSA: config.dumpSSA,
|
||||
TINYGOROOT: root,
|
||||
GOROOT: goroot,
|
||||
GOPATH: getGopath(),
|
||||
BuildTags: tags,
|
||||
TestConfig: config.testConfig,
|
||||
BuildTags: spec.BuildTags,
|
||||
}
|
||||
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
||||
if err != nil {
|
||||
@@ -241,16 +221,10 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
// Prepare link command.
|
||||
executable := filepath.Join(dir, "main")
|
||||
tmppath := executable // final file
|
||||
ldflags = append(ldflags, "-o", executable, objfile, "-L", root)
|
||||
ldflags := append(ldflags, "-o", executable, objfile, "-L", root)
|
||||
if spec.RTLib == "compiler-rt" {
|
||||
ldflags = append(ldflags, librt)
|
||||
}
|
||||
if spec.GOARCH == "wasm" {
|
||||
// Round heap size to next multiple of 65536 (the WebAssembly page
|
||||
// size).
|
||||
heapSize := (config.heapSize + (65536 - 1)) &^ (65536 - 1)
|
||||
ldflags = append(ldflags, "--initial-memory="+strconv.FormatInt(heapSize, 10))
|
||||
}
|
||||
|
||||
// Compile extra files.
|
||||
for i, path := range spec.ExtraFiles {
|
||||
@@ -362,33 +336,6 @@ func Build(pkgName, outpath, target string, config *BuildConfig) error {
|
||||
})
|
||||
}
|
||||
|
||||
func Test(pkgName, target string, config *BuildConfig) error {
|
||||
spec, err := LoadTarget(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
spec.BuildTags = append(spec.BuildTags, "test")
|
||||
config.testConfig.CompileTestBinary = true
|
||||
return Compile(pkgName, ".elf", spec, config, func(tmppath string) error {
|
||||
cmd := exec.Command(tmppath)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
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", tmppath, err}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Flash(pkgName, target, port string, config *BuildConfig) error {
|
||||
spec, err := LoadTarget(target)
|
||||
if err != nil {
|
||||
@@ -547,30 +494,6 @@ func Run(pkgName, target string, config *BuildConfig) error {
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||
fmt.Fprintln(os.Stderr, "version:", version)
|
||||
@@ -578,7 +501,6 @@ func usage() {
|
||||
fmt.Fprintln(os.Stderr, "\ncommands:")
|
||||
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
|
||||
fmt.Fprintln(os.Stderr, " run: compile and run immediately")
|
||||
fmt.Fprintln(os.Stderr, " test: test packages")
|
||||
fmt.Fprintln(os.Stderr, " flash: compile and flash to the device")
|
||||
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
|
||||
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+cacheDir()+")")
|
||||
@@ -589,24 +511,23 @@ func usage() {
|
||||
|
||||
func handleCompilerError(err error) {
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *interp.Unsupported:
|
||||
if errUnsupported, ok := err.(*interp.Unsupported); ok {
|
||||
// hit an unknown/unsupported instruction
|
||||
fmt.Fprintln(os.Stderr, "unsupported instruction during init evaluation:")
|
||||
err.Inst.Dump()
|
||||
errUnsupported.Inst.Dump()
|
||||
fmt.Fprintln(os.Stderr)
|
||||
case types.Error:
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
case loader.Errors:
|
||||
fmt.Fprintln(os.Stderr, "#", err.Pkg.ImportPath)
|
||||
for _, err := range err.Errs {
|
||||
} else if errCompiler, ok := err.(types.Error); ok {
|
||||
fmt.Fprintln(os.Stderr, errCompiler)
|
||||
} else if errLoader, ok := err.(loader.Errors); ok {
|
||||
fmt.Fprintln(os.Stderr, "#", errLoader.Pkg.ImportPath)
|
||||
for _, err := range errLoader.Errs {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
case *multiError:
|
||||
for _, err := range err.Errs {
|
||||
} else if errMulti, ok := err.(*multiError); ok {
|
||||
for _, err := range errMulti.Errs {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
default:
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
@@ -616,12 +537,11 @@ func handleCompilerError(err error) {
|
||||
func main() {
|
||||
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, conservative)")
|
||||
gc := flag.String("gc", "", "garbage collector to use (none, dumb, marksweep)")
|
||||
panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)")
|
||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||
tags := flag.String("tags", "", "a space-separated list of extra build tags")
|
||||
target := flag.String("target", "", "LLVM target | .json file with TargetSpec")
|
||||
target := flag.String("target", "", "LLVM target")
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
@@ -629,7 +549,6 @@ func main() {
|
||||
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)")
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
@@ -647,7 +566,6 @@ func main() {
|
||||
dumpSSA: *dumpSSA,
|
||||
debug: !*nodebug,
|
||||
printSizes: *printSize,
|
||||
tags: *tags,
|
||||
wasmAbi: *wasmAbi,
|
||||
}
|
||||
|
||||
@@ -665,13 +583,6 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var err error
|
||||
if config.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)
|
||||
|
||||
switch command {
|
||||
@@ -681,11 +592,8 @@ func main() {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
pkgName := "."
|
||||
if flag.NArg() == 1 {
|
||||
pkgName = flag.Arg(0)
|
||||
} else if flag.NArg() > 1 {
|
||||
fmt.Fprintln(os.Stderr, "build only accepts a single positional argument: package name, but multiple were specified")
|
||||
if flag.NArg() != 1 {
|
||||
fmt.Fprintln(os.Stderr, "No package specified.")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -693,7 +601,7 @@ func main() {
|
||||
if target == "" && filepath.Ext(*outpath) == ".wasm" {
|
||||
target = "wasm"
|
||||
}
|
||||
err := Build(pkgName, *outpath, target, config)
|
||||
err := Build(flag.Arg(0), *outpath, target, config)
|
||||
handleCompilerError(err)
|
||||
case "build-builtins":
|
||||
// Note: this command is only meant to be used while making a release!
|
||||
@@ -735,17 +643,6 @@ func main() {
|
||||
}
|
||||
err := Run(flag.Arg(0), *target, config)
|
||||
handleCompilerError(err)
|
||||
case "test":
|
||||
pkgName := "."
|
||||
if flag.NArg() == 1 {
|
||||
pkgName = flag.Arg(0)
|
||||
} else if flag.NArg() > 1 {
|
||||
fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err := Test(pkgName, *target, config)
|
||||
handleCompilerError(err)
|
||||
case "clean":
|
||||
// remove cache directory
|
||||
dir := cacheDir()
|
||||
|
||||
+11
-9
@@ -30,7 +30,6 @@
|
||||
package arm
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -70,6 +69,9 @@ 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
|
||||
|
||||
//go:volatile
|
||||
type RegValue uint32
|
||||
|
||||
const (
|
||||
SCS_BASE = 0xE000E000
|
||||
NVIC_BASE = SCS_BASE + 0x0100
|
||||
@@ -80,24 +82,24 @@ const (
|
||||
// Source:
|
||||
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CIHIGCIF.html
|
||||
type NVIC_Type struct {
|
||||
ISER [8]volatile.Register32 // Interrupt Set-enable Registers
|
||||
ISER [8]RegValue // Interrupt Set-enable Registers
|
||||
_ [24]uint32
|
||||
ICER [8]volatile.Register32 // Interrupt Clear-enable Registers
|
||||
ICER [8]RegValue // Interrupt Clear-enable Registers
|
||||
_ [24]uint32
|
||||
ISPR [8]volatile.Register32 // Interrupt Set-pending Registers
|
||||
ISPR [8]RegValue // Interrupt Set-pending Registers
|
||||
_ [24]uint32
|
||||
ICPR [8]volatile.Register32 // Interrupt Clear-pending Registers
|
||||
ICPR [8]RegValue // Interrupt Clear-pending Registers
|
||||
_ [24]uint32
|
||||
IABR [8]volatile.Register32 // Interrupt Active Bit Registers
|
||||
IABR [8]RegValue // Interrupt Active Bit Registers
|
||||
_ [56]uint32
|
||||
IPR [60]volatile.Register32 // Interrupt Priority Registers
|
||||
IPR [60]RegValue // Interrupt Priority Registers
|
||||
}
|
||||
|
||||
var NVIC = (*NVIC_Type)(unsafe.Pointer(uintptr(NVIC_BASE)))
|
||||
|
||||
// Enable the given interrupt number.
|
||||
func EnableIRQ(irq uint32) {
|
||||
NVIC.ISER[irq>>5].Set(1 << (irq & 0x1F))
|
||||
NVIC.ISER[irq>>5] = 1 << (irq & 0x1F)
|
||||
}
|
||||
|
||||
// Set the priority of the given interrupt number.
|
||||
@@ -114,7 +116,7 @@ func SetPriority(irq uint32, priority uint32) {
|
||||
regpos := irq % 4
|
||||
mask := uint32(0xff) << (regpos * 8) // bits to clear
|
||||
priority = priority << (regpos * 8) // bits to set
|
||||
NVIC.IPR[regnum].Set((uint32(NVIC.IPR[regnum].Get()) &^ mask) | priority)
|
||||
NVIC.IPR[regnum] = RegValue((uint32(NVIC.IPR[regnum]) &^ mask) | priority)
|
||||
}
|
||||
|
||||
// DisableInterrupts disables all interrupts, and returns the old state.
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
.syntax unified
|
||||
|
||||
.section .text.HardFault_Handler
|
||||
.global HardFault_Handler
|
||||
.type HardFault_Handler, %function
|
||||
HardFault_Handler:
|
||||
// Put the old stack pointer in the first argument, for easy debugging. This
|
||||
// is especially useful on Cortex-M0, which supports far fewer debug
|
||||
// facilities.
|
||||
mov r0, sp
|
||||
|
||||
// Load the default stack pointer from address 0 so that we can call normal
|
||||
// functions again that expect a working stack. However, it will corrupt the
|
||||
// old stack so the function below must not attempt to recover from this
|
||||
// fault.
|
||||
movs r3, #0
|
||||
ldr r3, [r3]
|
||||
mov sp, r3
|
||||
|
||||
// Continue handling this error in Go.
|
||||
bl handleHardFault
|
||||
@@ -1,10 +0,0 @@
|
||||
package riscv
|
||||
|
||||
// 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)
|
||||
|
||||
// ReadRegister returns the contents of the specified register. The register
|
||||
// must be a processor register, reachable with the "mov" instruction.
|
||||
func ReadRegister(name string) uintptr
|
||||
@@ -1,13 +0,0 @@
|
||||
.section .init
|
||||
.global _start
|
||||
.type _start,@function
|
||||
|
||||
_start:
|
||||
// Workaround for missing support of the la pseudo-instruction in Clang 8:
|
||||
// https://reviews.llvm.org/D55325
|
||||
lui sp, %hi(_stack_top)
|
||||
addi sp, sp, %lo(_stack_top)
|
||||
// see https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
|
||||
lui gp, %hi(__global_pointer$)
|
||||
addi gp, gp, %lo(__global_pointer$)
|
||||
call main
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
func main() {
|
||||
machine.InitADC()
|
||||
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led := machine.GPIO{machine.LED}
|
||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
|
||||
sensor := machine.ADC{machine.ADC2}
|
||||
sensor.Configure()
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led := machine.GPIO{machine.LED}
|
||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
for {
|
||||
led.Low()
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
|
||||
@@ -16,8 +16,8 @@ func main() {
|
||||
}
|
||||
|
||||
func led1() {
|
||||
led := machine.LED1
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led := machine.GPIO{machine.LED}
|
||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
for {
|
||||
println("+")
|
||||
led.Low()
|
||||
@@ -30,8 +30,8 @@ func led1() {
|
||||
}
|
||||
|
||||
func led2() {
|
||||
led := machine.LED2
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led := machine.GPIO{machine.LED2}
|
||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
for {
|
||||
println(" +")
|
||||
led.Low()
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
|
||||
// This example assumes that the button is connected to pin 8. Change the value
|
||||
// below to use a different pin.
|
||||
const (
|
||||
led = machine.LED
|
||||
button = machine.Pin(8)
|
||||
)
|
||||
const buttonPin = 8
|
||||
|
||||
func main() {
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
led := machine.GPIO{machine.LED}
|
||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
|
||||
button := machine.GPIO{buttonPin}
|
||||
button.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT})
|
||||
|
||||
for {
|
||||
if button.Get() {
|
||||
|
||||
@@ -8,29 +8,29 @@ import (
|
||||
// This example assumes that you are using the pca10040 board
|
||||
|
||||
func main() {
|
||||
led1 := machine.LED1
|
||||
led1.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led1 := machine.GPIO{machine.LED1}
|
||||
led1.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
|
||||
led2 := machine.LED2
|
||||
led2.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led2 := machine.GPIO{machine.LED2}
|
||||
led2.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
|
||||
led3 := machine.LED3
|
||||
led3.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led3 := machine.GPIO{machine.LED3}
|
||||
led3.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
|
||||
led4 := machine.LED4
|
||||
led4.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led4 := machine.GPIO{machine.LED4}
|
||||
led4.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
|
||||
button1 := machine.BUTTON1
|
||||
button1.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
button1 := machine.GPIO{machine.BUTTON1}
|
||||
button1.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT_PULLUP})
|
||||
|
||||
button2 := machine.BUTTON2
|
||||
button2.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
button2 := machine.GPIO{machine.BUTTON2}
|
||||
button2.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT_PULLUP})
|
||||
|
||||
button3 := machine.BUTTON3
|
||||
button3.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
button3 := machine.GPIO{machine.BUTTON3}
|
||||
button3.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT_PULLUP})
|
||||
|
||||
button4 := machine.BUTTON4
|
||||
button4.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
button4 := machine.GPIO{machine.BUTTON4}
|
||||
button4.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT_PULLUP})
|
||||
|
||||
for {
|
||||
led1.Set(button1.Get())
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
// change these to test a different UART or pins if available
|
||||
var (
|
||||
uart = machine.UART0
|
||||
tx = machine.UART_TX_PIN
|
||||
rx = machine.UART_RX_PIN
|
||||
uart = machine.UART0
|
||||
tx uint8 = machine.UART_TX_PIN
|
||||
rx uint8 = machine.UART_RX_PIN
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Example using the i2s hardware interface on the Adafruit Circuit Playground Express
|
||||
// to read data from the onboard MEMS microphone.
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2S0.Configure(machine.I2SConfig{
|
||||
Mode: machine.I2SModePDM,
|
||||
ClockSource: machine.I2SClockSourceExternal,
|
||||
Stereo: true,
|
||||
})
|
||||
|
||||
data := make([]uint32, 64)
|
||||
|
||||
for {
|
||||
// get the next group of samples
|
||||
machine.I2S0.Read(data)
|
||||
|
||||
println("data", data[0], data[1], data[2], data[4], "...")
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,19 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// cs is the pin used for Chip Select (CS). Change to whatever is in use on your board.
|
||||
const cs = machine.Pin(3)
|
||||
// CS_PIN is the pin used for Chip Select (CS). Change to whatever is in use on your board.
|
||||
const CS_PIN = 3
|
||||
|
||||
var (
|
||||
tx []byte
|
||||
rx []byte
|
||||
val, result uint16
|
||||
cs machine.GPIO
|
||||
)
|
||||
|
||||
func main() {
|
||||
cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
cs = machine.GPIO{CS_PIN}
|
||||
cs.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
Frequency: 4000000,
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
// The LED matrix in the micro:bit is a multiplexed display: https://en.wikipedia.org/wiki/Multiplexed_display
|
||||
// Driver for easier control: https://github.com/tinygo-org/drivers/tree/master/microbitmatrix
|
||||
func main() {
|
||||
ledrow := machine.LED_ROW_1
|
||||
ledrow.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
ledcol := machine.LED_COL_1
|
||||
ledcol.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
ledrow := machine.GPIO{machine.LED_ROW_1}
|
||||
ledrow.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
ledcol := machine.GPIO{machine.LED_COL_1}
|
||||
ledcol.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
ledcol.Low()
|
||||
for {
|
||||
ledrow.Low()
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
export: clean wasm_exec
|
||||
tinygo build -o ./html/wasm.wasm -target wasm -no-debug ./export/wasm.go
|
||||
tinygo build -o ./html/wasm.wasm -target wasm ./export/wasm.go
|
||||
cp ./export/wasm.js ./html/
|
||||
cp ./export/index.html ./html/
|
||||
|
||||
callback: clean wasm_exec
|
||||
tinygo build -o ./html/wasm.wasm -target wasm ./callback/wasm.go
|
||||
cp ./callback/wasm.js ./html/
|
||||
cp ./callback/index.html ./html/
|
||||
|
||||
main: clean wasm_exec
|
||||
tinygo build -o ./html/wasm.wasm -target wasm -no-debug ./main/main.go
|
||||
tinygo build -o ./html/wasm.wasm -target wasm ./main/main.go
|
||||
cp ./main/index.html ./html/
|
||||
|
||||
wasm_exec:
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Go WebAssembly</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script src="wasm_exec.js" defer></script>
|
||||
<script src="wasm.js" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>WebAssembly</h1>
|
||||
<p>Add two numbers, using WebAssembly:</p>
|
||||
<input type="number" id="a" value="0" /> + <input type="number" id="b" value="0" /> = <input type="number" id="result" readonly />
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,27 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
var a, b int
|
||||
|
||||
func main() {
|
||||
document := js.Global().Get("document")
|
||||
document.Call("getElementById", "a").Set("oninput", updater(&a))
|
||||
document.Call("getElementById", "b").Set("oninput", updater(&b))
|
||||
update()
|
||||
}
|
||||
|
||||
func updater(n *int) js.Func {
|
||||
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
*n, _ = strconv.Atoi(this.Get("value").String())
|
||||
update()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func update() {
|
||||
js.Global().Get("document").Call("getElementById", "result").Set("value", a+b)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const WASM_URL = 'wasm.wasm';
|
||||
|
||||
var wasm;
|
||||
|
||||
function init() {
|
||||
const go = new Go();
|
||||
if ('instantiateStreaming' in WebAssembly) {
|
||||
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
})
|
||||
} else {
|
||||
fetch(WASM_URL).then(resp =>
|
||||
resp.arrayBuffer()
|
||||
).then(bytes =>
|
||||
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
@@ -1,24 +1,24 @@
|
||||
// +build arduino
|
||||
// +build avr,arduino
|
||||
|
||||
package machine
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
|
||||
// LED on the Arduino
|
||||
const LED Pin = 13
|
||||
const LED = 13
|
||||
|
||||
// ADC on the Arduino
|
||||
const (
|
||||
ADC0 Pin = 0
|
||||
ADC1 Pin = 1
|
||||
ADC2 Pin = 2
|
||||
ADC3 Pin = 3
|
||||
ADC4 Pin = 4 // Used by TWI for SDA
|
||||
ADC5 Pin = 5 // Used by TWI for SCL
|
||||
ADC0 = 0
|
||||
ADC1 = 1
|
||||
ADC2 = 2
|
||||
ADC3 = 3
|
||||
ADC4 = 4 // Used by TWI for SDA
|
||||
ADC5 = 5 // Used by TWI for SCL
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 1
|
||||
UART_RX_PIN Pin = 0
|
||||
UART_TX_PIN = 1
|
||||
UART_RX_PIN = 0
|
||||
)
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
// +build sam,atsamd21,arduino_nano33
|
||||
|
||||
// This contains the pin mappings for the Arduino Nano33 IoT board.
|
||||
//
|
||||
// For more information, see: https://store.arduino.cc/nano-33-iot
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
RX0 Pin = PB23 // UART2 RX
|
||||
TX1 Pin = PB22 // UART2 TX
|
||||
|
||||
D2 Pin = PB10 // PWM available
|
||||
D3 Pin = PB11 // PWM available
|
||||
D4 Pin = PA07
|
||||
D5 Pin = PA05 // PWM available
|
||||
D6 Pin = PA04 // PWM available
|
||||
D7 Pin = PA06
|
||||
|
||||
D8 Pin = PA18
|
||||
D9 Pin = PA20 // PWM available
|
||||
D10 Pin = PA21 // PWM available
|
||||
D11 Pin = PA16 // PWM available
|
||||
D12 Pin = PA19 // PWM available
|
||||
|
||||
D13 Pin = PA17
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 Pin = PA02 // ADC/AIN[0]
|
||||
A1 Pin = PB02 // ADC/AIN[10]
|
||||
A2 Pin = PA11 // ADC/AIN[19]
|
||||
A3 Pin = PA10 // ADC/AIN[18]
|
||||
A4 Pin = PB08 // ADC/AIN[2], SCL: SERCOM2/PAD[1]
|
||||
A5 Pin = PB09 // ADC/AIN[3], SDA: SERCOM2/PAD[1]
|
||||
A6 Pin = PA09 // ADC/AIN[17]
|
||||
A7 Pin = PB03 // ADC/AIN[11]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
)
|
||||
|
||||
// NINA-W102 Pins
|
||||
|
||||
const (
|
||||
NINA_MOSI Pin = PA12
|
||||
NINA_MISO Pin = PA13
|
||||
NINA_CS Pin = PA14
|
||||
NINA_SCK Pin = PA15
|
||||
NINA_GPIO0 Pin = PA27
|
||||
NINA_RESETN Pin = PA08
|
||||
NINA_ACK Pin = PA28
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN Pin = PA24
|
||||
USBCDC_DP_PIN Pin = PA25
|
||||
)
|
||||
|
||||
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM5_USART,
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOMAlt,
|
||||
IRQVal: sam.IRQ_SERCOM5,
|
||||
}
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN Pin = PA22
|
||||
UART_RX_PIN Pin = PA23
|
||||
)
|
||||
|
||||
//go:export SERCOM5_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
|
||||
var (
|
||||
UART2 = UART{Bus: sam.SERCOM3_USART,
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOMAlt,
|
||||
IRQVal: sam.IRQ_SERCOM3,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM3_IRQHandler
|
||||
func handleUART2() {
|
||||
// should reset IRQ
|
||||
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
|
||||
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = A4 // SDA: SERCOM4/PAD[1]
|
||||
SCL_PIN Pin = A5 // SCL: SERCOM4/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the Arduino Nano 33.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM4_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOMAlt}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = PB11 // SCK: SERCOM4/PAD[3]
|
||||
SPI0_MOSI_PIN Pin = PB10 // MOSI: SERCOM4/PAD[2]
|
||||
SPI0_MISO_PIN Pin = PA12 // MISO: SERCOM4/PAD[0]
|
||||
)
|
||||
|
||||
// SPI on the Arduino Nano 33.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM1_SPI}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN Pin = PA10
|
||||
I2S_SD_PIN Pin = PA08
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33.
|
||||
)
|
||||
|
||||
// I2S on the Arduino Nano 33.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build bluepill
|
||||
// +build stm32,bluepill
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ const (
|
||||
D8 = PB23
|
||||
D9 = PA06
|
||||
D10 = PA07
|
||||
D11 = NoPin // does not seem to exist
|
||||
D11 = 0xff // does not seem to exist
|
||||
D12 = PA02
|
||||
D13 = PA17 // PWM available
|
||||
)
|
||||
@@ -65,20 +65,6 @@ const (
|
||||
UART_RX_PIN = PB09 // PORTB
|
||||
)
|
||||
|
||||
// UART1 on the Circuit Playground Express.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART,
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOM,
|
||||
IRQVal: sam.IRQ_SERCOM1,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PB02 // I2C0 external
|
||||
@@ -94,12 +80,12 @@ var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM5_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
PinMode: GPIO_SERCOM}
|
||||
// internal device
|
||||
I2C1 = I2C{Bus: sam.SERCOM1_I2CM,
|
||||
SDA: SDA1_PIN,
|
||||
SCL: SCL1_PIN,
|
||||
PinMode: PinSERCOMAlt}
|
||||
PinMode: GPIO_SERCOM_ALT}
|
||||
)
|
||||
|
||||
// SPI pins (internal flash)
|
||||
@@ -113,15 +99,3 @@ const (
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN = PA10
|
||||
I2S_SD_PIN = PA08
|
||||
I2S_WS_PIN = NoPin // no WS, instead uses SCK to sync
|
||||
)
|
||||
|
||||
// I2S on the Circuit Playground Express.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build digispark
|
||||
// +build attiny85,digispark
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
LED Pin = 1
|
||||
LED = 1
|
||||
)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// +build hifive1b
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
P00 Pin = 0
|
||||
P01 Pin = 1
|
||||
P02 Pin = 2
|
||||
P03 Pin = 3
|
||||
P04 Pin = 4
|
||||
P05 Pin = 5
|
||||
P06 Pin = 6
|
||||
P07 Pin = 7
|
||||
P08 Pin = 8
|
||||
P09 Pin = 9
|
||||
P10 Pin = 10
|
||||
P11 Pin = 11
|
||||
P12 Pin = 12
|
||||
P13 Pin = 13
|
||||
P14 Pin = 14
|
||||
P15 Pin = 15
|
||||
P16 Pin = 16
|
||||
P17 Pin = 17
|
||||
P18 Pin = 18
|
||||
P19 Pin = 19
|
||||
P20 Pin = 20
|
||||
P21 Pin = 21
|
||||
P22 Pin = 22
|
||||
P23 Pin = 23
|
||||
P24 Pin = 24
|
||||
P25 Pin = 25
|
||||
P26 Pin = 26
|
||||
P27 Pin = 27
|
||||
P28 Pin = 28
|
||||
P29 Pin = 29
|
||||
P30 Pin = 30
|
||||
P31 Pin = 31
|
||||
)
|
||||
@@ -1,96 +0,0 @@
|
||||
// +build sam,atsamd21,feather_m0
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PA11 // UART0 RX
|
||||
D1 = PA10 // UART0 TX
|
||||
D2 = NoPin // does not seem to exist
|
||||
D3 = PA09
|
||||
D4 = PA08
|
||||
D5 = PA15 // PWM available
|
||||
D6 = PA20 // PWM available
|
||||
D7 = NoPin // does not seem to exist
|
||||
D8 = PA06
|
||||
D9 = PA07 // PWM available
|
||||
D10 = PA18 // can be used for PWM or UART1 TX
|
||||
D11 = PA16 // can be used for PWM or UART1 RX
|
||||
D12 = PA19 // PWM available
|
||||
D13 = PA17 // PWM available
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PB08 // ADC/AIN[2]
|
||||
A2 = PB09 // ADC/AIN[3]
|
||||
A3 = PA04 // ADC/AIN[4]
|
||||
A4 = PA05 // ADC/AIN[5]
|
||||
A5 = PB02 // ADC/AIN[10]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = D10
|
||||
UART_RX_PIN = D11
|
||||
)
|
||||
|
||||
// UART1 on the Feather M0.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART,
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOM,
|
||||
IRQVal: sam.IRQ_SERCOM1,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||
SCL_PIN = PA23 // SCL: SERCOM3/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the Feather M0.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM3_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PB11 // SCK: SERCOM4/PAD[3]
|
||||
SPI0_MOSI_PIN = PB10 // MOSI: SERCOM4/PAD[2]
|
||||
SPI0_MISO_PIN = PA12 // MISO: SERCOM4/PAD[0]
|
||||
)
|
||||
|
||||
// SPI on the Feather M0.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN = PA10
|
||||
I2S_SD_PIN = PA08
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Feather M0.
|
||||
)
|
||||
@@ -1,19 +0,0 @@
|
||||
// +build hifive1b
|
||||
|
||||
package machine
|
||||
|
||||
const (
|
||||
LED = LED1
|
||||
LED1 = LED_RED
|
||||
LED2 = LED_GREEN
|
||||
LED3 = LED_BLUE
|
||||
LED_RED = P22
|
||||
LED_GREEN = P19
|
||||
LED_BLUE = P21
|
||||
)
|
||||
|
||||
const (
|
||||
// TODO: figure out the pin numbers for these.
|
||||
UART_TX_PIN = NoPin
|
||||
UART_RX_PIN = NoPin
|
||||
)
|
||||
@@ -48,20 +48,6 @@ const (
|
||||
UART_RX_PIN = D11
|
||||
)
|
||||
|
||||
// UART1 on the ItsyBitsy M0.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART,
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOM,
|
||||
IRQVal: sam.IRQ_SERCOM1,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||
@@ -73,7 +59,7 @@ var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM3_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
PinMode: GPIO_SERCOM}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
@@ -87,15 +73,3 @@ const (
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN = PA10
|
||||
I2S_SD_PIN = PA08
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on ItsyBitsy M0.
|
||||
)
|
||||
|
||||
// I2S on the ItsyBitsy M0.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build microbit
|
||||
// +build nrf51,microbit
|
||||
|
||||
package machine
|
||||
|
||||
@@ -7,70 +7,70 @@ const HasLowFrequencyCrystal = false
|
||||
|
||||
// Buttons on the micro:bit (A and B)
|
||||
const (
|
||||
BUTTON Pin = BUTTONA
|
||||
BUTTONA Pin = 17
|
||||
BUTTONB Pin = 26
|
||||
BUTTON = BUTTONA
|
||||
BUTTONA = 17
|
||||
BUTTONB = 26
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 24
|
||||
UART_RX_PIN Pin = 25
|
||||
UART_TX_PIN = 24
|
||||
UART_RX_PIN = 25
|
||||
)
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC0 Pin = 3 // P0 on the board
|
||||
ADC1 Pin = 2 // P1 on the board
|
||||
ADC2 Pin = 1 // P2 on the board
|
||||
ADC0 = 3 // P0 on the board
|
||||
ADC1 = 2 // P1 on the board
|
||||
ADC2 = 1 // P2 on the board
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = 30 // P20 on the board
|
||||
SCL_PIN Pin = 0 // P19 on the board
|
||||
SDA_PIN = 30 // P20 on the board
|
||||
SCL_PIN = 0 // P19 on the board
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = 23 // P13 on the board
|
||||
SPI0_MOSI_PIN Pin = 21 // P15 on the board
|
||||
SPI0_MISO_PIN Pin = 22 // P14 on the board
|
||||
SPI0_SCK_PIN = 23 // P13 on the board
|
||||
SPI0_MOSI_PIN = 21 // P15 on the board
|
||||
SPI0_MISO_PIN = 22 // P14 on the board
|
||||
)
|
||||
|
||||
// GPIO/Analog pins
|
||||
const (
|
||||
P0 Pin = 3
|
||||
P1 Pin = 2
|
||||
P2 Pin = 1
|
||||
P3 Pin = 4
|
||||
P4 Pin = 5
|
||||
P5 Pin = 17
|
||||
P6 Pin = 12
|
||||
P7 Pin = 11
|
||||
P8 Pin = 18
|
||||
P9 Pin = 10
|
||||
P10 Pin = 6
|
||||
P11 Pin = 26
|
||||
P12 Pin = 20
|
||||
P13 Pin = 23
|
||||
P14 Pin = 22
|
||||
P15 Pin = 21
|
||||
P16 Pin = 16
|
||||
P0 = 3
|
||||
P1 = 2
|
||||
P2 = 1
|
||||
P3 = 4
|
||||
P4 = 5
|
||||
P5 = 17
|
||||
P6 = 12
|
||||
P7 = 11
|
||||
P8 = 18
|
||||
P9 = 10
|
||||
P10 = 6
|
||||
P11 = 26
|
||||
P12 = 20
|
||||
P13 = 23
|
||||
P14 = 22
|
||||
P15 = 21
|
||||
P16 = 16
|
||||
)
|
||||
|
||||
// LED matrix pins
|
||||
const (
|
||||
LED_COL_1 Pin = 4
|
||||
LED_COL_2 Pin = 5
|
||||
LED_COL_3 Pin = 6
|
||||
LED_COL_4 Pin = 7
|
||||
LED_COL_5 Pin = 8
|
||||
LED_COL_6 Pin = 9
|
||||
LED_COL_7 Pin = 10
|
||||
LED_COL_8 Pin = 11
|
||||
LED_COL_9 Pin = 12
|
||||
LED_ROW_1 Pin = 13
|
||||
LED_ROW_2 Pin = 14
|
||||
LED_ROW_3 Pin = 15
|
||||
LED_COL_1 = 4
|
||||
LED_COL_2 = 5
|
||||
LED_COL_3 = 6
|
||||
LED_COL_4 = 7
|
||||
LED_COL_5 = 8
|
||||
LED_COL_6 = 9
|
||||
LED_COL_7 = 10
|
||||
LED_COL_8 = 11
|
||||
LED_COL_9 = 12
|
||||
LED_ROW_1 = 13
|
||||
LED_ROW_2 = 14
|
||||
LED_ROW_3 = 15
|
||||
)
|
||||
|
||||
@@ -6,27 +6,27 @@ const HasLowFrequencyCrystal = true
|
||||
|
||||
// LEDs on the nrf52840-mdk (nRF52840 dev board)
|
||||
const (
|
||||
LED Pin = LED_GREEN
|
||||
LED_GREEN Pin = 22
|
||||
LED_RED Pin = 23
|
||||
LED_BLUE Pin = 24
|
||||
LED = LED_GREEN
|
||||
LED_GREEN = 22
|
||||
LED_RED = 23
|
||||
LED_BLUE = 24
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 20
|
||||
UART_RX_PIN Pin = 19
|
||||
UART_TX_PIN = 20
|
||||
UART_RX_PIN = 19
|
||||
)
|
||||
|
||||
// I2C pins (unused)
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
SCL_PIN = NoPin
|
||||
SDA_PIN = 0xff
|
||||
SCL_PIN = 0xff
|
||||
)
|
||||
|
||||
// SPI pins (unused)
|
||||
const (
|
||||
SPI0_SCK_PIN = NoPin
|
||||
SPI0_MOSI_PIN = NoPin
|
||||
SPI0_MISO_PIN = NoPin
|
||||
SPI0_SCK_PIN = 0
|
||||
SPI0_MOSI_PIN = 0
|
||||
SPI0_MISO_PIN = 0
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build pca10031
|
||||
// +build nrf51,pca10031
|
||||
|
||||
// pca10031 is a nrf51 based dongle, intended for use in wireless applications.
|
||||
//
|
||||
@@ -10,30 +10,30 @@ const HasLowFrequencyCrystal = true
|
||||
|
||||
// LED on the pca10031
|
||||
const (
|
||||
LED Pin = LED_RED
|
||||
LED1 Pin = LED_RED
|
||||
LED2 Pin = LED_GREEN
|
||||
LED3 Pin = LED_BLUE
|
||||
LED_RED Pin = 21
|
||||
LED_GREEN Pin = 22
|
||||
LED_BLUE Pin = 23
|
||||
LED = LED_RED
|
||||
LED1 = LED_RED
|
||||
LED2 = LED_GREEN
|
||||
LED3 = LED_BLUE
|
||||
LED_RED = 21
|
||||
LED_GREEN = 22
|
||||
LED_BLUE = 23
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 9
|
||||
UART_RX_PIN Pin = 11
|
||||
UART_TX_PIN = 9
|
||||
UART_RX_PIN = 11
|
||||
)
|
||||
|
||||
// I2C pins (disabled)
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
SCL_PIN = NoPin
|
||||
SDA_PIN = 0xff
|
||||
SCL_PIN = 0xff
|
||||
)
|
||||
|
||||
// SPI pins (unused)
|
||||
const (
|
||||
SPI0_SCK_PIN = NoPin
|
||||
SPI0_MOSI_PIN = NoPin
|
||||
SPI0_MISO_PIN = NoPin
|
||||
SPI0_SCK_PIN = 0
|
||||
SPI0_MOSI_PIN = 0
|
||||
SPI0_MISO_PIN = 0
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build pca10040
|
||||
// +build nrf,pca10040
|
||||
|
||||
package machine
|
||||
|
||||
@@ -7,47 +7,47 @@ const HasLowFrequencyCrystal = true
|
||||
|
||||
// LEDs on the PCA10040 (nRF52832 dev board)
|
||||
const (
|
||||
LED Pin = LED1
|
||||
LED1 Pin = 17
|
||||
LED2 Pin = 18
|
||||
LED3 Pin = 19
|
||||
LED4 Pin = 20
|
||||
LED = LED1
|
||||
LED1 = 17
|
||||
LED2 = 18
|
||||
LED3 = 19
|
||||
LED4 = 20
|
||||
)
|
||||
|
||||
// Buttons on the PCA10040 (nRF52832 dev board)
|
||||
const (
|
||||
BUTTON Pin = BUTTON1
|
||||
BUTTON1 Pin = 13
|
||||
BUTTON2 Pin = 14
|
||||
BUTTON3 Pin = 15
|
||||
BUTTON4 Pin = 16
|
||||
BUTTON = BUTTON1
|
||||
BUTTON1 = 13
|
||||
BUTTON2 = 14
|
||||
BUTTON3 = 15
|
||||
BUTTON4 = 16
|
||||
)
|
||||
|
||||
// UART pins for NRF52840-DK
|
||||
const (
|
||||
UART_TX_PIN Pin = 6
|
||||
UART_RX_PIN Pin = 8
|
||||
UART_TX_PIN = 6
|
||||
UART_RX_PIN = 8
|
||||
)
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC0 Pin = 3
|
||||
ADC1 Pin = 4
|
||||
ADC2 Pin = 28
|
||||
ADC3 Pin = 29
|
||||
ADC4 Pin = 30
|
||||
ADC5 Pin = 31
|
||||
ADC0 = 3
|
||||
ADC1 = 4
|
||||
ADC2 = 28
|
||||
ADC3 = 29
|
||||
ADC4 = 30
|
||||
ADC5 = 31
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = 26
|
||||
SCL_PIN Pin = 27
|
||||
SDA_PIN = 26
|
||||
SCL_PIN = 27
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = 25
|
||||
SPI0_MOSI_PIN Pin = 23
|
||||
SPI0_MISO_PIN Pin = 24
|
||||
SPI0_SCK_PIN = 25
|
||||
SPI0_MOSI_PIN = 23
|
||||
SPI0_MISO_PIN = 24
|
||||
)
|
||||
|
||||
@@ -6,47 +6,47 @@ const HasLowFrequencyCrystal = true
|
||||
|
||||
// LEDs on the pca10056
|
||||
const (
|
||||
LED Pin = LED1
|
||||
LED1 Pin = 13
|
||||
LED2 Pin = 14
|
||||
LED3 Pin = 15
|
||||
LED4 Pin = 16
|
||||
LED = LED1
|
||||
LED1 = 13
|
||||
LED2 = 14
|
||||
LED3 = 15
|
||||
LED4 = 16
|
||||
)
|
||||
|
||||
// Buttons on the pca10056
|
||||
const (
|
||||
BUTTON Pin = BUTTON1
|
||||
BUTTON1 Pin = 11
|
||||
BUTTON2 Pin = 12
|
||||
BUTTON3 Pin = 24
|
||||
BUTTON4 Pin = 25
|
||||
BUTTON = BUTTON1
|
||||
BUTTON1 = 11
|
||||
BUTTON2 = 12
|
||||
BUTTON3 = 24
|
||||
BUTTON4 = 25
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 6
|
||||
UART_RX_PIN Pin = 8
|
||||
UART_TX_PIN = 6
|
||||
UART_RX_PIN = 8
|
||||
)
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC0 Pin = 3
|
||||
ADC1 Pin = 4
|
||||
ADC2 Pin = 28
|
||||
ADC3 Pin = 29
|
||||
ADC4 Pin = 30
|
||||
ADC5 Pin = 31
|
||||
ADC0 = 3
|
||||
ADC1 = 4
|
||||
ADC2 = 28
|
||||
ADC3 = 29
|
||||
ADC4 = 30
|
||||
ADC5 = 31
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = 26 // P0.26
|
||||
SCL_PIN Pin = 27 // P0.27
|
||||
SDA_PIN = 26 // P0.26
|
||||
SCL_PIN = 27 // P0.27
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = 47 // P1.15
|
||||
SPI0_MOSI_PIN Pin = 45 // P1.13
|
||||
SPI0_MISO_PIN Pin = 46 // P1.14
|
||||
SPI0_SCK_PIN = 47 // P1.15
|
||||
SPI0_MOSI_PIN = 45 // P1.13
|
||||
SPI0_MISO_PIN = 46 // P1.14
|
||||
)
|
||||
|
||||
@@ -4,58 +4,39 @@ package machine
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
|
||||
// Pins on the reel board
|
||||
// LEDs on the reel board
|
||||
const (
|
||||
LED Pin = LED1
|
||||
LED1 Pin = LED_YELLOW
|
||||
LED2 Pin = LED_RED
|
||||
LED3 Pin = LED_GREEN
|
||||
LED4 Pin = LED_BLUE
|
||||
LED_RED Pin = 11
|
||||
LED_GREEN Pin = 12
|
||||
LED_BLUE Pin = 41
|
||||
LED_YELLOW Pin = 13
|
||||
EPD_BUSY_PIN Pin = 14
|
||||
EPD_RESET_PIN Pin = 15
|
||||
EPD_DC_PIN Pin = 16
|
||||
EPD_CS_PIN Pin = 17
|
||||
EPD_SCK_PIN Pin = 19
|
||||
EPD_MOSI_PIN Pin = 20
|
||||
POWER_SUPPLY_PIN Pin = 32
|
||||
LED = LED1
|
||||
LED1 = LED_YELLOW
|
||||
LED2 = LED_RED
|
||||
LED3 = LED_GREEN
|
||||
LED4 = LED_BLUE
|
||||
LED_RED = 11
|
||||
LED_GREEN = 12
|
||||
LED_BLUE = 41
|
||||
LED_YELLOW = 13
|
||||
)
|
||||
|
||||
// User "a" button on the reel board
|
||||
const (
|
||||
BUTTON Pin = 7
|
||||
BUTTON = 7
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 6
|
||||
UART_RX_PIN Pin = 8
|
||||
UART_TX_PIN = 6
|
||||
UART_RX_PIN = 8
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = 26
|
||||
SCL_PIN Pin = 27
|
||||
SDA_PIN = 26
|
||||
SCL_PIN = 27
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = 47
|
||||
SPI0_MOSI_PIN Pin = 45
|
||||
SPI0_MISO_PIN Pin = 46
|
||||
SPI0_SCK_PIN = 47
|
||||
SPI0_MOSI_PIN = 45
|
||||
SPI0_MISO_PIN = 46
|
||||
)
|
||||
|
||||
// PowerSupplyActive enables the supply voltages for nRF52840 and peripherals (true) or only for nRF52840 (false)
|
||||
// This controls the TPS610981 boost converter. You must turn the power supply active in order to use the EPD and
|
||||
// other onboard peripherals.
|
||||
func PowerSupplyActive(active bool) {
|
||||
POWER_SUPPLY_PIN.Configure(PinConfig{Mode: PinOutput})
|
||||
if active {
|
||||
POWER_SUPPLY_PIN.High()
|
||||
} else {
|
||||
POWER_SUPPLY_PIN.Low()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// +build bluepill stm32f4disco
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
|
||||
const (
|
||||
portA Pin = iota * 16
|
||||
portB
|
||||
portC
|
||||
portD
|
||||
portE
|
||||
portF
|
||||
portG
|
||||
portH
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build stm32f4disco
|
||||
// +build stm32,stm32f4disco
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
// +build sam,atsamd21,trinket_m0
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PA08 // PWM available
|
||||
D1 = PA02
|
||||
D2 = PA09 // PWM available
|
||||
D3 = PA07 // PWM available / UART0 RX
|
||||
D4 = PA06 // PWM available / UART0 TX
|
||||
D13 = PA10 // LED
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = D1
|
||||
A1 = D2
|
||||
A2 = D0
|
||||
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 = D4
|
||||
UART_RX_PIN = D3
|
||||
)
|
||||
|
||||
// UART1 on the Trinket M0.
|
||||
var (
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART,
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOM,
|
||||
IRQVal: sam.IRQ_SERCOM1,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D3
|
||||
SPI0_MOSI_PIN = D4
|
||||
SPI0_MISO_PIN = D2
|
||||
)
|
||||
|
||||
// SPI on the Trinket M0.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM0_SPI}
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = D0 // SDA
|
||||
SCL_PIN = D2 // SCL
|
||||
)
|
||||
|
||||
// I2C on the Trinket M0.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOMAlt}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN = PA10
|
||||
I2S_SD_PIN = PA08
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Trinket M0.
|
||||
)
|
||||
+11
-12
@@ -1,11 +1,10 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
//go:volatile
|
||||
type volatileByte byte
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
//
|
||||
@@ -13,9 +12,9 @@ const bufferSize = 128
|
||||
// members of a struct are not compiled correctly by TinyGo.
|
||||
// See https://github.com/tinygo-org/tinygo/issues/151 for details.
|
||||
type RingBuffer struct {
|
||||
rxbuffer [bufferSize]volatile.Register8
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
rxbuffer [bufferSize]volatileByte
|
||||
head volatileByte
|
||||
tail volatileByte
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
@@ -25,15 +24,15 @@ func NewRingBuffer() *RingBuffer {
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
return uint8(rb.head - rb.tail)
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer) Put(val byte) bool {
|
||||
if rb.Used() != bufferSize {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
rb.rxbuffer[rb.head.Get()%bufferSize].Set(val)
|
||||
rb.head++
|
||||
rb.rxbuffer[rb.head%bufferSize] = volatileByte(val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -43,8 +42,8 @@ func (rb *RingBuffer) Put(val byte) bool {
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer) Get() (byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize].Get(), true
|
||||
rb.tail++
|
||||
return byte(rb.rxbuffer[rb.tail%bufferSize]), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !stm32f4disco,!hifive1b
|
||||
// +build avr nrf sam stm32f103xx
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// +build sam
|
||||
|
||||
// This is the definition for I2S bus functions.
|
||||
// Actual implementations if available for any given hardware
|
||||
// are to be found in its the board definition.
|
||||
//
|
||||
// For more info about I2S, see: https://en.wikipedia.org/wiki/I%C2%B2S
|
||||
//
|
||||
|
||||
package machine
|
||||
|
||||
type I2SMode uint8
|
||||
type I2SStandard uint8
|
||||
type I2SClockSource uint8
|
||||
type I2SDataFormat uint8
|
||||
|
||||
const (
|
||||
I2SModeMaster I2SMode = iota
|
||||
I2SModeSlave
|
||||
I2SModePDM
|
||||
)
|
||||
|
||||
const (
|
||||
I2StandardPhilips I2SStandard = iota
|
||||
I2SStandardMSB
|
||||
I2SStandardLSB
|
||||
)
|
||||
|
||||
const (
|
||||
I2SClockSourceInternal I2SClockSource = iota
|
||||
I2SClockSourceExternal
|
||||
)
|
||||
|
||||
const (
|
||||
I2SDataFormatDefault I2SDataFormat = 0
|
||||
I2SDataFormat8bit = 8
|
||||
I2SDataFormat16bit = 16
|
||||
I2SDataFormat24bit = 24
|
||||
I2SDataFormat32bit = 32
|
||||
)
|
||||
|
||||
// All fields are optional and may not be required or used on a particular platform.
|
||||
type I2SConfig struct {
|
||||
SCK Pin
|
||||
WS Pin
|
||||
SD Pin
|
||||
Mode I2SMode
|
||||
Standard I2SStandard
|
||||
ClockSource I2SClockSource
|
||||
DataFormat I2SDataFormat
|
||||
AudioFrequency uint32
|
||||
MasterClockOutput bool
|
||||
Stereo bool
|
||||
}
|
||||
+9
-20
@@ -1,36 +1,25 @@
|
||||
package machine
|
||||
|
||||
type PinConfig struct {
|
||||
Mode PinMode
|
||||
type GPIOConfig struct {
|
||||
Mode GPIOMode
|
||||
}
|
||||
|
||||
// Pin is a single pin on a chip, which may be connected to other hardware
|
||||
// devices. It can either be used directly as GPIO pin or it can be used in
|
||||
// other peripherals like ADC, I2C, etc.
|
||||
type Pin int8
|
||||
type GPIO struct {
|
||||
Pin uint8
|
||||
}
|
||||
|
||||
// NoPin explicitly indicates "not a pin". Use this pin if you want to leave one
|
||||
// of the pins in a peripheral unconfigured (if supported by the hardware).
|
||||
const NoPin = Pin(-1)
|
||||
|
||||
// High sets this GPIO pin to high, assuming it has been configured as an output
|
||||
// pin. It is hardware dependent (and often undefined) what happens if you set a
|
||||
// pin to high that is not configured as an output pin.
|
||||
func (p Pin) High() {
|
||||
func (p GPIO) High() {
|
||||
p.Set(true)
|
||||
}
|
||||
|
||||
// Low sets this GPIO pin to low, assuming it has been configured as an output
|
||||
// pin. It is hardware dependent (and often undefined) what happens if you set a
|
||||
// pin to low that is not configured as an output pin.
|
||||
func (p Pin) Low() {
|
||||
func (p GPIO) Low() {
|
||||
p.Set(false)
|
||||
}
|
||||
|
||||
type PWM struct {
|
||||
Pin Pin
|
||||
Pin uint8
|
||||
}
|
||||
|
||||
type ADC struct {
|
||||
Pin Pin
|
||||
Pin uint8
|
||||
}
|
||||
|
||||
@@ -4,106 +4,105 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
// Configure sets the pin to input or output.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
if config.Mode == PinOutput { // set output bit
|
||||
if p < 8 {
|
||||
avr.DDRD.SetBits(1 << uint8(p))
|
||||
func (p GPIO) Configure(config GPIOConfig) {
|
||||
if config.Mode == GPIO_OUTPUT { // set output bit
|
||||
if p.Pin < 8 {
|
||||
*avr.DDRD |= 1 << p.Pin
|
||||
} else {
|
||||
avr.DDRB.SetBits(1 << uint8(p-8))
|
||||
*avr.DDRB |= 1 << (p.Pin - 8)
|
||||
}
|
||||
} else { // configure input: clear output bit
|
||||
if p < 8 {
|
||||
avr.DDRD.ClearBits(1 << uint8(p))
|
||||
if p.Pin < 8 {
|
||||
*avr.DDRD &^= 1 << p.Pin
|
||||
} else {
|
||||
avr.DDRB.ClearBits(1 << uint8(p-8))
|
||||
*avr.DDRB &^= 1 << (p.Pin - 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
if p < 8 {
|
||||
val := avr.PIND.Get() & (1 << uint8(p))
|
||||
func (p GPIO) Get() bool {
|
||||
if p.Pin < 8 {
|
||||
val := *avr.PIND & (1 << p.Pin)
|
||||
return (val > 0)
|
||||
} else {
|
||||
val := avr.PINB.Get() & (1 << uint8(p-8))
|
||||
val := *avr.PINB & (1 << (p.Pin - 8))
|
||||
return (val > 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
if p < 8 {
|
||||
return avr.PORTD, 1 << uint8(p)
|
||||
func (p GPIO) getPortMask() (*avr.RegValue, uint8) {
|
||||
if p.Pin < 8 {
|
||||
return avr.PORTD, 1 << p.Pin
|
||||
} else {
|
||||
return avr.PORTB, 1 << uint8(p-8)
|
||||
return avr.PORTB, 1 << (p.Pin - 8)
|
||||
}
|
||||
}
|
||||
|
||||
// InitPWM initializes the registers needed for PWM.
|
||||
func InitPWM() {
|
||||
// use waveform generation
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
||||
*avr.TCCR0A |= avr.TCCR0A_WGM00
|
||||
|
||||
// set timer 0 prescale factor to 64
|
||||
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
||||
*avr.TCCR0B |= avr.TCCR0B_CS01 | avr.TCCR0B_CS00
|
||||
|
||||
// set timer 1 prescale factor to 64
|
||||
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
||||
*avr.TCCR1B |= avr.TCCR1B_CS11
|
||||
|
||||
// put timer 1 in 8-bit phase correct pwm mode
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
||||
*avr.TCCR1A |= avr.TCCR1A_WGM10
|
||||
|
||||
// set timer 2 prescale factor to 64
|
||||
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
||||
*avr.TCCR2B |= avr.TCCR2B_CS22
|
||||
|
||||
// configure timer 2 for phase correct pwm (8-bit)
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
||||
*avr.TCCR2A |= avr.TCCR2A_WGM20
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
if pwm.Pin < 8 {
|
||||
avr.DDRD.SetBits(1 << uint8(pwm.Pin))
|
||||
*avr.DDRD |= 1 << pwm.Pin
|
||||
} else {
|
||||
avr.DDRB.SetBits(1 << uint8(pwm.Pin-8))
|
||||
*avr.DDRB |= 1 << (pwm.Pin - 8)
|
||||
}
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||
// 8-bit value ranging from 0 to 255.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
value8 := uint8(value >> 8)
|
||||
value8 := value >> 8
|
||||
switch pwm.Pin {
|
||||
case 3:
|
||||
// connect pwm to pin on timer 2, channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
avr.OCR2B.Set(value8) // set pwm duty
|
||||
*avr.TCCR2A |= avr.TCCR2A_COM2B1
|
||||
*avr.OCR2B = avr.RegValue(value8) // set pwm duty
|
||||
case 5:
|
||||
// connect pwm to pin on timer 0, channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
avr.OCR0B.Set(value8) // set pwm duty
|
||||
*avr.TCCR0A |= avr.TCCR0A_COM0B1
|
||||
*avr.OCR0B = avr.RegValue(value8) // set pwm duty
|
||||
case 6:
|
||||
// connect pwm to pin on timer 0, channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
avr.OCR0A.Set(value8) // set pwm duty
|
||||
*avr.TCCR0A |= avr.TCCR0A_COM0A1
|
||||
*avr.OCR0A = avr.RegValue(value8) // set pwm duty
|
||||
case 9:
|
||||
// connect pwm to pin on timer 1, channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
*avr.TCCR1A |= avr.TCCR1A_COM1A1
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1AL.Set(value8) // set pwm duty
|
||||
*avr.OCR1AL = avr.RegValue(value8) // set pwm duty
|
||||
case 10:
|
||||
// connect pwm to pin on timer 1, channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
*avr.TCCR1A |= avr.TCCR1A_COM1B1
|
||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||
avr.OCR1BL.Set(value8) // set pwm duty
|
||||
*avr.OCR1BL = avr.RegValue(value8) // set pwm duty
|
||||
case 11:
|
||||
// connect pwm to pin on timer 2, channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
avr.OCR2A.Set(value8) // set pwm duty
|
||||
*avr.TCCR2A |= avr.TCCR2A_COM2A1
|
||||
*avr.OCR2A = avr.RegValue(value8) // set pwm duty
|
||||
default:
|
||||
panic("Invalid PWM pin")
|
||||
}
|
||||
@@ -122,19 +121,19 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
}
|
||||
|
||||
// Activate internal pullups for twi.
|
||||
avr.PORTC.SetBits((avr.DIDR0_ADC4D | avr.DIDR0_ADC5D))
|
||||
*avr.PORTC |= (avr.DIDR0_ADC4D | avr.DIDR0_ADC5D)
|
||||
|
||||
// Initialize twi prescaler and bit rate.
|
||||
avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1))
|
||||
*avr.TWSR |= (avr.TWSR_TWPS0 | avr.TWSR_TWPS1)
|
||||
|
||||
// twi bit rate formula from atmega128 manual pg. 204:
|
||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||
// NOTE: TWBR should be 10 or higher for master mode.
|
||||
// It is 72 for a 16mhz board with 100kHz TWI
|
||||
avr.TWBR.Set(uint8(((CPU_FREQUENCY / config.Frequency) - 16) / 2))
|
||||
*avr.TWBR = avr.RegValue(((CPU_FREQUENCY / config.Frequency) - 16) / 2)
|
||||
|
||||
// Enable twi module.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||
*avr.TWCR = avr.TWCR_TWEN
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
@@ -163,10 +162,10 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
// start starts an I2C communication session.
|
||||
func (i2c I2C) start(address uint8, write bool) {
|
||||
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
||||
avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
|
||||
*avr.TWCR = (avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN)
|
||||
|
||||
// Wait till start condition is transmitted.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
||||
}
|
||||
|
||||
// Write 7-bit shifted peripheral address.
|
||||
@@ -180,36 +179,36 @@ func (i2c I2C) start(address uint8, write bool) {
|
||||
// stop ends an I2C communication session.
|
||||
func (i2c I2C) stop() {
|
||||
// Send stop condition.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
||||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
||||
|
||||
// Wait for stop condition to be executed on bus.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWSTO) {
|
||||
for (*avr.TWCR & avr.TWCR_TWSTO) == 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// writeByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) writeByte(data byte) {
|
||||
// Write data to register.
|
||||
avr.TWDR.Set(data)
|
||||
*avr.TWDR = avr.RegValue(data)
|
||||
|
||||
// Clear TWI interrupt flag and enable TWI.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT)
|
||||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT)
|
||||
|
||||
// Wait till data is transmitted.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// readByte reads a single byte from the I2C bus.
|
||||
func (i2c I2C) readByte() byte {
|
||||
// Clear TWI interrupt flag and enable TWI.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
||||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
||||
|
||||
// Wait till read request is transmitted.
|
||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
||||
}
|
||||
|
||||
return byte(avr.TWDR.Get())
|
||||
return byte(*avr.TWDR)
|
||||
}
|
||||
|
||||
// UART on the AVR.
|
||||
@@ -227,32 +226,32 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||
avr.UBRR0H.Set(uint8(ps >> 8))
|
||||
avr.UBRR0L.Set(uint8(ps & 0xff))
|
||||
*avr.UBRR0H = avr.RegValue(ps >> 8)
|
||||
*avr.UBRR0L = avr.RegValue(ps & 0xff)
|
||||
|
||||
// enable RX, TX and RX interrupt
|
||||
avr.UCSR0B.Set(avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0)
|
||||
*avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0
|
||||
|
||||
// 8-bits data
|
||||
avr.UCSR0C.Set(avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00)
|
||||
*avr.UCSR0C = avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
// Wait until UART buffer is not busy.
|
||||
for !avr.UCSR0A.HasBits(avr.UCSR0A_UDRE0) {
|
||||
for (*avr.UCSR0A & avr.UCSR0A_UDRE0) == 0 {
|
||||
}
|
||||
avr.UDR0.Set(c) // send char
|
||||
*avr.UDR0 = avr.RegValue(c) // send char
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:interrupt USART_RX_vect
|
||||
func handleUSART_RX() {
|
||||
// Read register to clear it.
|
||||
data := avr.UDR0.Get()
|
||||
data := *avr.UDR0
|
||||
|
||||
// Ensure no error.
|
||||
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||
if (*avr.UCSR0A & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 {
|
||||
// Put data from UDR register into buffer.
|
||||
UART0.Receive(byte(data))
|
||||
}
|
||||
|
||||
+1004
-669
File diff suppressed because it is too large
Load Diff
@@ -1,334 +0,0 @@
|
||||
// +build sam,atsamd21,atsamd21e18
|
||||
|
||||
// Peripheral abstraction layer for the atsamd21.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||
return &sam.PORT.OUTSET0.Reg, 1 << uint8(p)
|
||||
}
|
||||
|
||||
// Return the register and mask to disable a given port. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||
return &sam.PORT.OUTCLR0.Reg, 1 << uint8(p)
|
||||
}
|
||||
|
||||
// Set the pin to high or low.
|
||||
// Warning: only use this on an output pin!
|
||||
func (p Pin) Set(high bool) {
|
||||
if high {
|
||||
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||
} else {
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
return (sam.PORT.IN0.Get()>>uint8(p))&1 > 0
|
||||
}
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
switch config.Mode {
|
||||
case PinOutput:
|
||||
sam.PORT.DIRSET0.Set(1 << uint8(p))
|
||||
// output is also set to input enable so pin can read back its own value
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
|
||||
case PinInput:
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
|
||||
case PinInputPulldown:
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
|
||||
case PinInputPullup:
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
|
||||
case PinSERCOM:
|
||||
if uint8(p)&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)
|
||||
|
||||
case PinSERCOMAlt:
|
||||
if uint8(p)&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||
|
||||
case PinCom:
|
||||
if uint8(p)&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
case PinAnalog:
|
||||
if uint8(p)&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (p Pin) getPMux() uint8 {
|
||||
switch p >> 1 {
|
||||
case 0:
|
||||
return sam.PORT.PMUX0_0.Get()
|
||||
case 1:
|
||||
return sam.PORT.PMUX0_1.Get()
|
||||
case 2:
|
||||
return sam.PORT.PMUX0_2.Get()
|
||||
case 3:
|
||||
return sam.PORT.PMUX0_3.Get()
|
||||
case 4:
|
||||
return sam.PORT.PMUX0_4.Get()
|
||||
case 5:
|
||||
return sam.PORT.PMUX0_5.Get()
|
||||
case 6:
|
||||
return sam.PORT.PMUX0_6.Get()
|
||||
case 7:
|
||||
return sam.PORT.PMUX0_7.Get()
|
||||
case 8:
|
||||
return sam.PORT.PMUX0_8.Get()
|
||||
case 9:
|
||||
return sam.PORT.PMUX0_9.Get()
|
||||
case 10:
|
||||
return sam.PORT.PMUX0_10.Get()
|
||||
case 11:
|
||||
return sam.PORT.PMUX0_11.Get()
|
||||
case 12:
|
||||
return sam.PORT.PMUX0_12.Get()
|
||||
case 13:
|
||||
return sam.PORT.PMUX0_13.Get()
|
||||
case 14:
|
||||
return sam.PORT.PMUX0_14.Get()
|
||||
case 15:
|
||||
return sam.PORT.PMUX0_15.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (p Pin) setPMux(val uint8) {
|
||||
switch p >> 1 {
|
||||
case 0:
|
||||
sam.PORT.PMUX0_0.Set(val)
|
||||
case 1:
|
||||
sam.PORT.PMUX0_1.Set(val)
|
||||
case 2:
|
||||
sam.PORT.PMUX0_2.Set(val)
|
||||
case 3:
|
||||
sam.PORT.PMUX0_3.Set(val)
|
||||
case 4:
|
||||
sam.PORT.PMUX0_4.Set(val)
|
||||
case 5:
|
||||
sam.PORT.PMUX0_5.Set(val)
|
||||
case 6:
|
||||
sam.PORT.PMUX0_6.Set(val)
|
||||
case 7:
|
||||
sam.PORT.PMUX0_7.Set(val)
|
||||
case 8:
|
||||
sam.PORT.PMUX0_8.Set(val)
|
||||
case 9:
|
||||
sam.PORT.PMUX0_9.Set(val)
|
||||
case 10:
|
||||
sam.PORT.PMUX0_10.Set(val)
|
||||
case 11:
|
||||
sam.PORT.PMUX0_11.Set(val)
|
||||
case 12:
|
||||
sam.PORT.PMUX0_12.Set(val)
|
||||
case 13:
|
||||
sam.PORT.PMUX0_13.Set(val)
|
||||
case 14:
|
||||
sam.PORT.PMUX0_14.Set(val)
|
||||
case 15:
|
||||
sam.PORT.PMUX0_15.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (p Pin) getPinCfg() uint8 {
|
||||
switch p {
|
||||
case 0:
|
||||
return sam.PORT.PINCFG0_0.Get()
|
||||
case 1:
|
||||
return sam.PORT.PINCFG0_1.Get()
|
||||
case 2:
|
||||
return sam.PORT.PINCFG0_2.Get()
|
||||
case 3:
|
||||
return sam.PORT.PINCFG0_3.Get()
|
||||
case 4:
|
||||
return sam.PORT.PINCFG0_4.Get()
|
||||
case 5:
|
||||
return sam.PORT.PINCFG0_5.Get()
|
||||
case 6:
|
||||
return sam.PORT.PINCFG0_6.Get()
|
||||
case 7:
|
||||
return sam.PORT.PINCFG0_7.Get()
|
||||
case 8:
|
||||
return sam.PORT.PINCFG0_8.Get()
|
||||
case 9:
|
||||
return sam.PORT.PINCFG0_9.Get()
|
||||
case 10:
|
||||
return sam.PORT.PINCFG0_10.Get()
|
||||
case 11:
|
||||
return sam.PORT.PINCFG0_11.Get()
|
||||
case 12:
|
||||
return sam.PORT.PINCFG0_12.Get()
|
||||
case 13:
|
||||
return sam.PORT.PINCFG0_13.Get()
|
||||
case 14:
|
||||
return sam.PORT.PINCFG0_14.Get()
|
||||
case 15:
|
||||
return sam.PORT.PINCFG0_15.Get()
|
||||
case 16:
|
||||
return sam.PORT.PINCFG0_16.Get()
|
||||
case 17:
|
||||
return sam.PORT.PINCFG0_17.Get()
|
||||
case 18:
|
||||
return sam.PORT.PINCFG0_18.Get()
|
||||
case 19:
|
||||
return sam.PORT.PINCFG0_19.Get()
|
||||
case 20:
|
||||
return sam.PORT.PINCFG0_20.Get()
|
||||
case 21:
|
||||
return sam.PORT.PINCFG0_21.Get()
|
||||
case 22:
|
||||
return sam.PORT.PINCFG0_22.Get()
|
||||
case 23:
|
||||
return sam.PORT.PINCFG0_23.Get()
|
||||
case 24:
|
||||
return sam.PORT.PINCFG0_24.Get()
|
||||
case 25:
|
||||
return sam.PORT.PINCFG0_25.Get()
|
||||
case 26:
|
||||
return sam.PORT.PINCFG0_26.Get()
|
||||
case 27:
|
||||
return sam.PORT.PINCFG0_27.Get()
|
||||
case 28:
|
||||
return sam.PORT.PINCFG0_28.Get()
|
||||
case 29:
|
||||
return sam.PORT.PINCFG0_29.Get()
|
||||
case 30:
|
||||
return sam.PORT.PINCFG0_30.Get()
|
||||
case 31:
|
||||
return sam.PORT.PINCFG0_31.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (p Pin) setPinCfg(val uint8) {
|
||||
switch p {
|
||||
case 0:
|
||||
sam.PORT.PINCFG0_0.Set(val)
|
||||
case 1:
|
||||
sam.PORT.PINCFG0_1.Set(val)
|
||||
case 2:
|
||||
sam.PORT.PINCFG0_2.Set(val)
|
||||
case 3:
|
||||
sam.PORT.PINCFG0_3.Set(val)
|
||||
case 4:
|
||||
sam.PORT.PINCFG0_4.Set(val)
|
||||
case 5:
|
||||
sam.PORT.PINCFG0_5.Set(val)
|
||||
case 6:
|
||||
sam.PORT.PINCFG0_6.Set(val)
|
||||
case 7:
|
||||
sam.PORT.PINCFG0_7.Set(val)
|
||||
case 8:
|
||||
sam.PORT.PINCFG0_8.Set(val)
|
||||
case 9:
|
||||
sam.PORT.PINCFG0_9.Set(val)
|
||||
case 10:
|
||||
sam.PORT.PINCFG0_10.Set(val)
|
||||
case 11:
|
||||
sam.PORT.PINCFG0_11.Set(val)
|
||||
case 12:
|
||||
sam.PORT.PINCFG0_12.Set(val)
|
||||
case 13:
|
||||
sam.PORT.PINCFG0_13.Set(val)
|
||||
case 14:
|
||||
sam.PORT.PINCFG0_14.Set(val)
|
||||
case 15:
|
||||
sam.PORT.PINCFG0_15.Set(val)
|
||||
case 16:
|
||||
sam.PORT.PINCFG0_16.Set(val)
|
||||
case 17:
|
||||
sam.PORT.PINCFG0_17.Set(val)
|
||||
case 18:
|
||||
sam.PORT.PINCFG0_18.Set(val)
|
||||
case 19:
|
||||
sam.PORT.PINCFG0_19.Set(val)
|
||||
case 20:
|
||||
sam.PORT.PINCFG0_20.Set(val)
|
||||
case 21:
|
||||
sam.PORT.PINCFG0_21.Set(val)
|
||||
case 22:
|
||||
sam.PORT.PINCFG0_22.Set(val)
|
||||
case 23:
|
||||
sam.PORT.PINCFG0_23.Set(val)
|
||||
case 24:
|
||||
sam.PORT.PINCFG0_24.Set(val)
|
||||
case 25:
|
||||
sam.PORT.PINCFG0_25.Set(val)
|
||||
case 26:
|
||||
sam.PORT.PINCFG0_26.Set(val)
|
||||
case 27:
|
||||
sam.PORT.PINCFG0_27.Set(val)
|
||||
case 28:
|
||||
sam.PORT.PINCFG0_28.Set(val)
|
||||
case 29:
|
||||
sam.PORT.PINCFG0_29.Set(val)
|
||||
case 30:
|
||||
sam.PORT.PINCFG0_30.Set(val)
|
||||
case 31:
|
||||
sam.PORT.PINCFG0_31.Set(val)
|
||||
}
|
||||
}
|
||||
@@ -1,569 +0,0 @@
|
||||
// +build sam,atsamd21,atsamd21g18
|
||||
|
||||
// Peripheral abstraction layer for the atsamd21.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||
if p < 32 {
|
||||
return &sam.PORT.OUTSET0.Reg, 1 << uint8(p)
|
||||
} else {
|
||||
return &sam.PORT.OUTSET1.Reg, 1 << uint8(p-32)
|
||||
}
|
||||
}
|
||||
|
||||
// Return the register and mask to disable a given port. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||
if p < 32 {
|
||||
return &sam.PORT.OUTCLR0.Reg, 1 << uint8(p)
|
||||
} else {
|
||||
return &sam.PORT.OUTCLR1.Reg, 1 << uint8(p-32)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the pin to high or low.
|
||||
// Warning: only use this on an output pin!
|
||||
func (p Pin) Set(high bool) {
|
||||
if p < 32 {
|
||||
if high {
|
||||
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||
} else {
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||
}
|
||||
} else {
|
||||
if high {
|
||||
sam.PORT.OUTSET1.Set(1 << uint8(p-32))
|
||||
} else {
|
||||
sam.PORT.OUTCLR1.Set(1 << uint8(p-32))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
if p < 32 {
|
||||
return (sam.PORT.IN0.Get()>>uint8(p))&1 > 0
|
||||
} else {
|
||||
return (sam.PORT.IN1.Get()>>(uint8(p)-32))&1 > 0
|
||||
}
|
||||
}
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
switch config.Mode {
|
||||
case PinOutput:
|
||||
if p < 32 {
|
||||
sam.PORT.DIRSET0.Set(1 << uint8(p))
|
||||
// output is also set to input enable so pin can read back its own value
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
} else {
|
||||
sam.PORT.DIRSET1.Set(1 << uint8(p-32))
|
||||
// output is also set to input enable so pin can read back its own value
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
}
|
||||
|
||||
case PinInput:
|
||||
if p < 32 {
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
} else {
|
||||
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
}
|
||||
|
||||
case PinInputPulldown:
|
||||
if p < 32 {
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
} else {
|
||||
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||
sam.PORT.OUTCLR1.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
}
|
||||
|
||||
case PinInputPullup:
|
||||
if p < 32 {
|
||||
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
} else {
|
||||
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||
sam.PORT.OUTSET1.Set(1<<uint8(p) - 32)
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||
}
|
||||
|
||||
case PinSERCOM:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)
|
||||
|
||||
case PinSERCOMAlt:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||
|
||||
case PinCom:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
case PinAnalog:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (p Pin) getPMux() uint8 {
|
||||
switch uint8(p) >> 1 {
|
||||
case 0:
|
||||
return sam.PORT.PMUX0_0.Get()
|
||||
case 1:
|
||||
return sam.PORT.PMUX0_1.Get()
|
||||
case 2:
|
||||
return sam.PORT.PMUX0_2.Get()
|
||||
case 3:
|
||||
return sam.PORT.PMUX0_3.Get()
|
||||
case 4:
|
||||
return sam.PORT.PMUX0_4.Get()
|
||||
case 5:
|
||||
return sam.PORT.PMUX0_5.Get()
|
||||
case 6:
|
||||
return sam.PORT.PMUX0_6.Get()
|
||||
case 7:
|
||||
return sam.PORT.PMUX0_7.Get()
|
||||
case 8:
|
||||
return sam.PORT.PMUX0_8.Get()
|
||||
case 9:
|
||||
return sam.PORT.PMUX0_9.Get()
|
||||
case 10:
|
||||
return sam.PORT.PMUX0_10.Get()
|
||||
case 11:
|
||||
return sam.PORT.PMUX0_11.Get()
|
||||
case 12:
|
||||
return sam.PORT.PMUX0_12.Get()
|
||||
case 13:
|
||||
return sam.PORT.PMUX0_13.Get()
|
||||
case 14:
|
||||
return sam.PORT.PMUX0_14.Get()
|
||||
case 15:
|
||||
return sam.PORT.PMUX0_15.Get()
|
||||
case 16:
|
||||
return uint8(sam.PORT.PMUX1_0.Get()>>0) & 0xff
|
||||
case 17:
|
||||
return uint8(sam.PORT.PMUX1_0.Get()>>8) & 0xff
|
||||
case 18:
|
||||
return uint8(sam.PORT.PMUX1_0.Get()>>16) & 0xff
|
||||
case 19:
|
||||
return uint8(sam.PORT.PMUX1_0.Get()>>24) & 0xff
|
||||
case 20:
|
||||
return uint8(sam.PORT.PMUX1_4.Get()>>0) & 0xff
|
||||
case 21:
|
||||
return uint8(sam.PORT.PMUX1_4.Get()>>8) & 0xff
|
||||
case 22:
|
||||
return uint8(sam.PORT.PMUX1_4.Get()>>16) & 0xff
|
||||
case 23:
|
||||
return uint8(sam.PORT.PMUX1_4.Get()>>24) & 0xff
|
||||
case 24:
|
||||
return uint8(sam.PORT.PMUX1_8.Get()>>0) & 0xff
|
||||
case 25:
|
||||
return uint8(sam.PORT.PMUX1_8.Get()>>8) & 0xff
|
||||
case 26:
|
||||
return uint8(sam.PORT.PMUX1_8.Get()>>16) & 0xff
|
||||
case 27:
|
||||
return uint8(sam.PORT.PMUX1_8.Get()>>24) & 0xff
|
||||
case 28:
|
||||
return uint8(sam.PORT.PMUX1_12.Get()>>0) & 0xff
|
||||
case 29:
|
||||
return uint8(sam.PORT.PMUX1_12.Get()>>8) & 0xff
|
||||
case 30:
|
||||
return uint8(sam.PORT.PMUX1_12.Get()>>16) & 0xff
|
||||
case 31:
|
||||
return uint8(sam.PORT.PMUX1_12.Get()>>24) & 0xff
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (p Pin) setPMux(val uint8) {
|
||||
switch uint8(p) >> 1 {
|
||||
case 0:
|
||||
sam.PORT.PMUX0_0.Set(val)
|
||||
case 1:
|
||||
sam.PORT.PMUX0_1.Set(val)
|
||||
case 2:
|
||||
sam.PORT.PMUX0_2.Set(val)
|
||||
case 3:
|
||||
sam.PORT.PMUX0_3.Set(val)
|
||||
case 4:
|
||||
sam.PORT.PMUX0_4.Set(val)
|
||||
case 5:
|
||||
sam.PORT.PMUX0_5.Set(val)
|
||||
case 6:
|
||||
sam.PORT.PMUX0_6.Set(val)
|
||||
case 7:
|
||||
sam.PORT.PMUX0_7.Set(val)
|
||||
case 8:
|
||||
sam.PORT.PMUX0_8.Set(val)
|
||||
case 9:
|
||||
sam.PORT.PMUX0_9.Set(val)
|
||||
case 10:
|
||||
sam.PORT.PMUX0_10.Set(val)
|
||||
case 11:
|
||||
sam.PORT.PMUX0_11.Set(val)
|
||||
case 12:
|
||||
sam.PORT.PMUX0_12.Set(val)
|
||||
case 13:
|
||||
sam.PORT.PMUX0_13.Set(val)
|
||||
case 14:
|
||||
sam.PORT.PMUX0_14.Set(val)
|
||||
case 15:
|
||||
sam.PORT.PMUX0_15.Set(val)
|
||||
case 16:
|
||||
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 17:
|
||||
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 18:
|
||||
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 19:
|
||||
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 20:
|
||||
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 21:
|
||||
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 22:
|
||||
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 23:
|
||||
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 24:
|
||||
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 25:
|
||||
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 26:
|
||||
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 27:
|
||||
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 28:
|
||||
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 29:
|
||||
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 30:
|
||||
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 31:
|
||||
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
}
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (p Pin) getPinCfg() uint8 {
|
||||
switch p {
|
||||
case 0:
|
||||
return sam.PORT.PINCFG0_0.Get()
|
||||
case 1:
|
||||
return sam.PORT.PINCFG0_1.Get()
|
||||
case 2:
|
||||
return sam.PORT.PINCFG0_2.Get()
|
||||
case 3:
|
||||
return sam.PORT.PINCFG0_3.Get()
|
||||
case 4:
|
||||
return sam.PORT.PINCFG0_4.Get()
|
||||
case 5:
|
||||
return sam.PORT.PINCFG0_5.Get()
|
||||
case 6:
|
||||
return sam.PORT.PINCFG0_6.Get()
|
||||
case 7:
|
||||
return sam.PORT.PINCFG0_7.Get()
|
||||
case 8:
|
||||
return sam.PORT.PINCFG0_8.Get()
|
||||
case 9:
|
||||
return sam.PORT.PINCFG0_9.Get()
|
||||
case 10:
|
||||
return sam.PORT.PINCFG0_10.Get()
|
||||
case 11:
|
||||
return sam.PORT.PINCFG0_11.Get()
|
||||
case 12:
|
||||
return sam.PORT.PINCFG0_12.Get()
|
||||
case 13:
|
||||
return sam.PORT.PINCFG0_13.Get()
|
||||
case 14:
|
||||
return sam.PORT.PINCFG0_14.Get()
|
||||
case 15:
|
||||
return sam.PORT.PINCFG0_15.Get()
|
||||
case 16:
|
||||
return sam.PORT.PINCFG0_16.Get()
|
||||
case 17:
|
||||
return sam.PORT.PINCFG0_17.Get()
|
||||
case 18:
|
||||
return sam.PORT.PINCFG0_18.Get()
|
||||
case 19:
|
||||
return sam.PORT.PINCFG0_19.Get()
|
||||
case 20:
|
||||
return sam.PORT.PINCFG0_20.Get()
|
||||
case 21:
|
||||
return sam.PORT.PINCFG0_21.Get()
|
||||
case 22:
|
||||
return sam.PORT.PINCFG0_22.Get()
|
||||
case 23:
|
||||
return sam.PORT.PINCFG0_23.Get()
|
||||
case 24:
|
||||
return sam.PORT.PINCFG0_24.Get()
|
||||
case 25:
|
||||
return sam.PORT.PINCFG0_25.Get()
|
||||
case 26:
|
||||
return sam.PORT.PINCFG0_26.Get()
|
||||
case 27:
|
||||
return sam.PORT.PINCFG0_27.Get()
|
||||
case 28:
|
||||
return sam.PORT.PINCFG0_28.Get()
|
||||
case 29:
|
||||
return sam.PORT.PINCFG0_29.Get()
|
||||
case 30:
|
||||
return sam.PORT.PINCFG0_30.Get()
|
||||
case 31:
|
||||
return sam.PORT.PINCFG0_31.Get()
|
||||
case 32: // PB00
|
||||
return uint8(sam.PORT.PINCFG1_0.Get()>>0) & 0xff
|
||||
case 33: // PB01
|
||||
return uint8(sam.PORT.PINCFG1_0.Get()>>8) & 0xff
|
||||
case 34: // PB02
|
||||
return uint8(sam.PORT.PINCFG1_0.Get()>>16) & 0xff
|
||||
case 35: // PB03
|
||||
return uint8(sam.PORT.PINCFG1_0.Get()>>24) & 0xff
|
||||
case 37: // PB04
|
||||
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
|
||||
case 38: // PB05
|
||||
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
|
||||
case 39: // PB06
|
||||
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
|
||||
case 40: // PB07
|
||||
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
|
||||
case 41: // PB08
|
||||
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
|
||||
case 42: // PB09
|
||||
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
|
||||
case 43: // PB10
|
||||
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
|
||||
case 44: // PB11
|
||||
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
|
||||
case 45: // PB12
|
||||
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
|
||||
case 46: // PB13
|
||||
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
|
||||
case 47: // PB14
|
||||
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
|
||||
case 48: // PB15
|
||||
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
|
||||
case 49: // PB16
|
||||
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
|
||||
case 50: // PB17
|
||||
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
|
||||
case 51: // PB18
|
||||
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
|
||||
case 52: // PB19
|
||||
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
|
||||
case 53: // PB20
|
||||
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
|
||||
case 54: // PB21
|
||||
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
|
||||
case 55: // PB22
|
||||
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
|
||||
case 56: // PB23
|
||||
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
|
||||
case 57: // PB24
|
||||
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
|
||||
case 58: // PB25
|
||||
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
|
||||
case 59: // PB26
|
||||
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
|
||||
case 60: // PB27
|
||||
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
|
||||
case 61: // PB28
|
||||
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
|
||||
case 62: // PB29
|
||||
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
|
||||
case 63: // PB30
|
||||
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
|
||||
case 64: // PB31
|
||||
return uint8(sam.PORT.PINCFG1_28.Get()>>24) & 0xff
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (p Pin) setPinCfg(val uint8) {
|
||||
switch p {
|
||||
case 0:
|
||||
sam.PORT.PINCFG0_0.Set(val)
|
||||
case 1:
|
||||
sam.PORT.PINCFG0_1.Set(val)
|
||||
case 2:
|
||||
sam.PORT.PINCFG0_2.Set(val)
|
||||
case 3:
|
||||
sam.PORT.PINCFG0_3.Set(val)
|
||||
case 4:
|
||||
sam.PORT.PINCFG0_4.Set(val)
|
||||
case 5:
|
||||
sam.PORT.PINCFG0_5.Set(val)
|
||||
case 6:
|
||||
sam.PORT.PINCFG0_6.Set(val)
|
||||
case 7:
|
||||
sam.PORT.PINCFG0_7.Set(val)
|
||||
case 8:
|
||||
sam.PORT.PINCFG0_8.Set(val)
|
||||
case 9:
|
||||
sam.PORT.PINCFG0_9.Set(val)
|
||||
case 10:
|
||||
sam.PORT.PINCFG0_10.Set(val)
|
||||
case 11:
|
||||
sam.PORT.PINCFG0_11.Set(val)
|
||||
case 12:
|
||||
sam.PORT.PINCFG0_12.Set(val)
|
||||
case 13:
|
||||
sam.PORT.PINCFG0_13.Set(val)
|
||||
case 14:
|
||||
sam.PORT.PINCFG0_14.Set(val)
|
||||
case 15:
|
||||
sam.PORT.PINCFG0_15.Set(val)
|
||||
case 16:
|
||||
sam.PORT.PINCFG0_16.Set(val)
|
||||
case 17:
|
||||
sam.PORT.PINCFG0_17.Set(val)
|
||||
case 18:
|
||||
sam.PORT.PINCFG0_18.Set(val)
|
||||
case 19:
|
||||
sam.PORT.PINCFG0_19.Set(val)
|
||||
case 20:
|
||||
sam.PORT.PINCFG0_20.Set(val)
|
||||
case 21:
|
||||
sam.PORT.PINCFG0_21.Set(val)
|
||||
case 22:
|
||||
sam.PORT.PINCFG0_22.Set(val)
|
||||
case 23:
|
||||
sam.PORT.PINCFG0_23.Set(val)
|
||||
case 24:
|
||||
sam.PORT.PINCFG0_24.Set(val)
|
||||
case 25:
|
||||
sam.PORT.PINCFG0_25.Set(val)
|
||||
case 26:
|
||||
sam.PORT.PINCFG0_26.Set(val)
|
||||
case 27:
|
||||
sam.PORT.PINCFG0_27.Set(val)
|
||||
case 28:
|
||||
sam.PORT.PINCFG0_28.Set(val)
|
||||
case 29:
|
||||
sam.PORT.PINCFG0_29.Set(val)
|
||||
case 30:
|
||||
sam.PORT.PINCFG0_30.Set(val)
|
||||
case 31:
|
||||
sam.PORT.PINCFG0_31.Set(val)
|
||||
case 32: // PB00
|
||||
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 33: // PB01
|
||||
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 34: // PB02
|
||||
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 35: // PB03
|
||||
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 36: // PB04
|
||||
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 37: // PB05
|
||||
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 38: // PB06
|
||||
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 39: // PB07
|
||||
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 40: // PB08
|
||||
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 41: // PB09
|
||||
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 42: // PB10
|
||||
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 43: // PB11
|
||||
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 44: // PB12
|
||||
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 45: // PB13
|
||||
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 46: // PB14
|
||||
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 47: // PB15
|
||||
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 48: // PB16
|
||||
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 49: // PB17
|
||||
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 50: // PB18
|
||||
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 51: // PB19
|
||||
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 52: // PB20
|
||||
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 53: // PB21
|
||||
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 54: // PB22
|
||||
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 55: // PB23
|
||||
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 56: // PB24
|
||||
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 57: // PB25
|
||||
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 58: // PB26
|
||||
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 59: // PB27
|
||||
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
case 60: // PB28
|
||||
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||
case 61: // PB29
|
||||
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||
case 62: // PB30
|
||||
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||
case 63: // PB31
|
||||
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||
}
|
||||
}
|
||||
@@ -4,25 +4,24 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
// Configure sets the pin to input or output.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
if config.Mode == PinOutput { // set output bit
|
||||
avr.DDRB.SetBits(1 << uint8(p))
|
||||
func (p GPIO) Configure(config GPIOConfig) {
|
||||
if config.Mode == GPIO_OUTPUT { // set output bit
|
||||
*avr.DDRB |= 1 << p.Pin
|
||||
} else { // configure input: clear output bit
|
||||
avr.DDRB.ClearBits(1 << uint8(p))
|
||||
*avr.DDRB &^= 1 << p.Pin
|
||||
}
|
||||
}
|
||||
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
return avr.PORTB, 1 << uint8(p)
|
||||
func (p GPIO) getPortMask() (*avr.RegValue, uint8) {
|
||||
return avr.PORTB, 1 << p.Pin
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
val := avr.PINB.Get() & (1 << uint8(p))
|
||||
func (p GPIO) Get() bool {
|
||||
val := *avr.PINB & (1 << p.Pin)
|
||||
return (val > 0)
|
||||
}
|
||||
|
||||
|
||||
+18
-17
@@ -4,24 +4,23 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
type GPIOMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinOutput
|
||||
GPIO_INPUT = iota
|
||||
GPIO_OUTPUT
|
||||
)
|
||||
|
||||
// Set changes the value of the GPIO pin. The pin must be configured as output.
|
||||
func (p Pin) Set(value bool) {
|
||||
func (p GPIO) Set(value bool) {
|
||||
if value { // set bits
|
||||
port, mask := p.PortMaskSet()
|
||||
port.Set(mask)
|
||||
*port = mask
|
||||
} else { // clear bits
|
||||
port, mask := p.PortMaskClear()
|
||||
port.Set(mask)
|
||||
*port = mask
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +30,9 @@ func (p Pin) Set(value bool) {
|
||||
// Warning: there are no separate pin set/clear registers on the AVR. The
|
||||
// returned mask is only valid as long as no other pin in the same port has been
|
||||
// changed.
|
||||
func (p Pin) PortMaskSet() (*volatile.Register8, uint8) {
|
||||
func (p GPIO) PortMaskSet() (*avr.RegValue, avr.RegValue) {
|
||||
port, mask := p.getPortMask()
|
||||
return port, port.Get() | mask
|
||||
return port, *port | avr.RegValue(mask)
|
||||
}
|
||||
|
||||
// Return the register and mask to disable a given port. This can be used to
|
||||
@@ -42,18 +41,18 @@ func (p Pin) PortMaskSet() (*volatile.Register8, uint8) {
|
||||
// Warning: there are no separate pin set/clear registers on the AVR. The
|
||||
// returned mask is only valid as long as no other pin in the same port has been
|
||||
// changed.
|
||||
func (p Pin) PortMaskClear() (*volatile.Register8, uint8) {
|
||||
func (p GPIO) PortMaskClear() (*avr.RegValue, avr.RegValue) {
|
||||
port, mask := p.getPortMask()
|
||||
return port, port.Get() &^ mask
|
||||
return port, *port &^ avr.RegValue(mask)
|
||||
}
|
||||
|
||||
// InitADC initializes the registers needed for ADC.
|
||||
func InitADC() {
|
||||
// set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz.
|
||||
avr.ADCSRA.SetBits(avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
|
||||
*avr.ADCSRA |= (avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
|
||||
|
||||
// enable a2d conversions
|
||||
avr.ADCSRA.SetBits(avr.ADCSRA_ADEN)
|
||||
*avr.ADCSRA |= avr.ADCSRA_ADEN
|
||||
}
|
||||
|
||||
// Configure configures a ADCPin to be able to be used to read data.
|
||||
@@ -69,16 +68,18 @@ func (a ADC) Get() uint16 {
|
||||
// set the ADLAR bit (left-adjusted result) to get a value scaled to 16
|
||||
// bits. This has the same effect as shifting the return value left by 6
|
||||
// bits.
|
||||
avr.ADMUX.Set(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (uint8(a.Pin) & 0x07))
|
||||
*avr.ADMUX = avr.RegValue(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (a.Pin & 0x07))
|
||||
|
||||
// start the conversion
|
||||
avr.ADCSRA.SetBits(avr.ADCSRA_ADSC)
|
||||
*avr.ADCSRA |= avr.ADCSRA_ADSC
|
||||
|
||||
// ADSC is cleared when the conversion finishes
|
||||
for ok := true; ok; ok = avr.ADCSRA.HasBits(avr.ADCSRA_ADSC) {
|
||||
for ok := true; ok; ok = (*avr.ADCSRA & avr.ADCSRA_ADSC) > 0 {
|
||||
}
|
||||
|
||||
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
|
||||
low := uint16(*avr.ADCL)
|
||||
high := uint16(*avr.ADCH)
|
||||
return uint16(low) | uint16(high<<8)
|
||||
}
|
||||
|
||||
// I2C on AVR.
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// +build !avr,!nrf,!sam,!stm32
|
||||
|
||||
package machine
|
||||
|
||||
// Dummy machine package, filled with no-ops.
|
||||
|
||||
type GPIOMode uint8
|
||||
|
||||
const (
|
||||
GPIO_INPUT = iota
|
||||
GPIO_OUTPUT
|
||||
)
|
||||
|
||||
// Fake LED numbers, for testing.
|
||||
var (
|
||||
LED uint8 = LED1
|
||||
LED1 uint8 = 0
|
||||
LED2 uint8 = 0
|
||||
LED3 uint8 = 0
|
||||
LED4 uint8 = 0
|
||||
)
|
||||
|
||||
// Fake button numbers, for testing.
|
||||
var (
|
||||
BUTTON uint8 = BUTTON1
|
||||
BUTTON1 uint8 = 5
|
||||
BUTTON2 uint8 = 6
|
||||
BUTTON3 uint8 = 7
|
||||
BUTTON4 uint8 = 8
|
||||
)
|
||||
|
||||
// Fake SPI interfaces, for testing.
|
||||
var (
|
||||
SPI0 = SPI{0}
|
||||
)
|
||||
|
||||
var (
|
||||
GPIOConfigure func(pin uint8, config GPIOConfig)
|
||||
GPIOSet func(pin uint8, value bool)
|
||||
GPIOGet func(pin uint8) bool
|
||||
SPIConfigure func(bus uint8, sck uint8, mosi uint8, miso uint8)
|
||||
SPITransfer func(bus uint8, w uint8) uint8
|
||||
)
|
||||
|
||||
func (p GPIO) Configure(config GPIOConfig) {
|
||||
if GPIOConfigure != nil {
|
||||
GPIOConfigure(p.Pin, config)
|
||||
}
|
||||
}
|
||||
|
||||
func (p GPIO) Set(value bool) {
|
||||
if GPIOSet != nil {
|
||||
GPIOSet(p.Pin, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (p GPIO) Get() bool {
|
||||
if GPIOGet != nil {
|
||||
return GPIOGet(p.Pin)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type SPI struct {
|
||||
Bus uint8
|
||||
}
|
||||
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
SCK uint8
|
||||
MOSI uint8
|
||||
MISO uint8
|
||||
Mode uint8
|
||||
}
|
||||
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
if SPIConfigure != nil {
|
||||
SPIConfigure(spi.Bus, config.SCK, config.MOSI, config.MISO)
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
if SPITransfer != nil {
|
||||
return SPITransfer(spi.Bus, w), nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// +build fe310
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sifive"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinOutput
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
sifive.GPIO0.INPUT_EN.SetBits(1 << uint8(p))
|
||||
if config.Mode == PinOutput {
|
||||
sifive.GPIO0.OUTPUT_EN.SetBits(1 << uint8(p))
|
||||
}
|
||||
}
|
||||
|
||||
// Set the pin to high or low.
|
||||
func (p Pin) Set(high bool) {
|
||||
if high {
|
||||
sifive.GPIO0.PORT.SetBits(1 << uint8(p))
|
||||
} else {
|
||||
sifive.GPIO0.PORT.ClearBits(1 << uint8(p))
|
||||
}
|
||||
}
|
||||
|
||||
type UART struct {
|
||||
Bus *sifive.UART_Type
|
||||
Buffer *RingBuffer
|
||||
}
|
||||
|
||||
var (
|
||||
UART0 = UART{Bus: sifive.UART0, Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
func (uart UART) Configure(config UARTConfig) {
|
||||
// Assuming a 16Mhz Crystal (which is Y1 on the HiFive1), the divisor for a
|
||||
// 115200 baud rate is 138.
|
||||
sifive.UART0.DIV.Set(138)
|
||||
sifive.UART0.TXCTRL.Set(sifive.UART_TXCTRL_ENABLE)
|
||||
}
|
||||
|
||||
func (uart UART) WriteByte(c byte) {
|
||||
for sifive.UART0.TXDATA.Get()&sifive.UART_TXDATA_FULL != 0 {
|
||||
}
|
||||
|
||||
sifive.UART0.TXDATA.Set(uint32(c))
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
// +build !avr,!nrf,!sam,!sifive,!stm32
|
||||
|
||||
package machine
|
||||
|
||||
// Dummy machine package that calls out to external functions.
|
||||
|
||||
var (
|
||||
SPI0 = SPI{0}
|
||||
I2C0 = I2C{0}
|
||||
UART0 = UART{0}
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinOutput
|
||||
PinInputPullup
|
||||
PinInputPulldown
|
||||
)
|
||||
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
gpioConfigure(p, config)
|
||||
}
|
||||
|
||||
func (p Pin) Set(value bool) {
|
||||
gpioSet(p, value)
|
||||
}
|
||||
|
||||
func (p Pin) Get() bool {
|
||||
return gpioGet(p)
|
||||
}
|
||||
|
||||
//go:export __tinygo_gpio_configure
|
||||
func gpioConfigure(pin Pin, config PinConfig)
|
||||
|
||||
//go:export __tinygo_gpio_set
|
||||
func gpioSet(pin Pin, value bool)
|
||||
|
||||
//go:export __tinygo_gpio_get
|
||||
func gpioGet(pin Pin) bool
|
||||
|
||||
type SPI struct {
|
||||
Bus uint8
|
||||
}
|
||||
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
SCK Pin
|
||||
MOSI Pin
|
||||
MISO Pin
|
||||
Mode uint8
|
||||
}
|
||||
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
spiConfigure(spi.Bus, config.SCK, config.MOSI, config.MISO)
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
return spiTransfer(spi.Bus, w), nil
|
||||
}
|
||||
|
||||
//go:export __tinygo_spi_configure
|
||||
func spiConfigure(bus uint8, sck Pin, mosi Pin, miso Pin)
|
||||
|
||||
//go:export __tinygo_spi_transfer
|
||||
func spiTransfer(bus uint8, w uint8) uint8
|
||||
|
||||
// InitADC enables support for ADC peripherals.
|
||||
func InitADC() {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
// Configure configures an ADC pin to be able to be used to read data.
|
||||
func (adc ADC) Configure() {
|
||||
}
|
||||
|
||||
// Get reads the current analog value from this ADC peripheral.
|
||||
func (adc ADC) Get() uint16 {
|
||||
return adcRead(adc.Pin)
|
||||
}
|
||||
|
||||
//go:export __tinygo_adc_read
|
||||
func adcRead(pin Pin) uint16
|
||||
|
||||
// InitPWM enables support for PWM peripherals.
|
||||
func InitPWM() {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
pwmSet(pwm.Pin, value)
|
||||
}
|
||||
|
||||
//go:export __tinygo_pwm_set
|
||||
func pwmSet(pin Pin, value uint16)
|
||||
|
||||
// I2C is a generic implementation of the Inter-IC communication protocol.
|
||||
type I2C struct {
|
||||
Bus uint8
|
||||
}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
SCL Pin
|
||||
SDA Pin
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) {
|
||||
i2cConfigure(i2c.Bus, config.SCL, config.SDA)
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
i2cTransfer(i2c.Bus, &w[0], len(w), &r[0], len(r))
|
||||
// TODO: do something with the returned error code.
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:export __tinygo_i2c_configure
|
||||
func i2cConfigure(bus uint8, scl Pin, sda Pin)
|
||||
|
||||
//go:export __tinygo_i2c_transfer
|
||||
func i2cTransfer(bus uint8, w *byte, wlen int, r *byte, rlen int) int
|
||||
|
||||
type UART struct {
|
||||
Bus uint8
|
||||
}
|
||||
|
||||
type UARTConfig struct {
|
||||
BaudRate uint32
|
||||
TX Pin
|
||||
RX Pin
|
||||
}
|
||||
|
||||
// Configure the UART.
|
||||
func (uart UART) Configure(config UARTConfig) {
|
||||
uartConfigure(uart.Bus, config.TX, config.RX)
|
||||
}
|
||||
|
||||
// Read from the UART.
|
||||
func (uart UART) Read(data []byte) (n int, err error) {
|
||||
return uartRead(uart.Bus, &data[0], len(data)), nil
|
||||
}
|
||||
|
||||
// Write to the UART.
|
||||
func (uart UART) Write(data []byte) (n int, err error) {
|
||||
return uartWrite(uart.Bus, &data[0], len(data)), nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
func (uart UART) Buffered() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the UART.
|
||||
func (uart UART) ReadByte() (byte, error) {
|
||||
var b byte
|
||||
uartRead(uart.Bus, &b, 1)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the UART.
|
||||
func (uart UART) WriteByte(b byte) error {
|
||||
uartWrite(uart.Bus, &b, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:export __tinygo_uart_configure
|
||||
func uartConfigure(bus uint8, tx Pin, rx Pin)
|
||||
|
||||
//go:export __tinygo_uart_read
|
||||
func uartRead(bus uint8, buf *byte, bufLen int) int
|
||||
|
||||
//go:export __tinygo_uart_write
|
||||
func uartWrite(bus uint8, buf *byte, bufLen int) int
|
||||
+81
-70
@@ -7,51 +7,51 @@ import (
|
||||
"device/nrf"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
type GPIOMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||
PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||
PinInputPulldown PinMode = PinOutput | (nrf.GPIO_PIN_CNF_PULL_Pulldown << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||
PinOutput PinMode = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||
GPIO_INPUT = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||
GPIO_INPUT_PULLUP = GPIO_INPUT | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||
GPIO_INPUT_PULLDOWN = GPIO_INPUT | (nrf.GPIO_PIN_CNF_PULL_Pulldown << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||
GPIO_OUTPUT = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
func (p GPIO) Configure(config GPIOConfig) {
|
||||
cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled
|
||||
port, pin := p.getPortPin()
|
||||
port.PIN_CNF[pin].Set(uint32(cfg))
|
||||
port.PIN_CNF[pin] = nrf.RegValue(cfg)
|
||||
}
|
||||
|
||||
// Set the pin to high or low.
|
||||
// Warning: only use this on an output pin!
|
||||
func (p Pin) Set(high bool) {
|
||||
func (p GPIO) Set(high bool) {
|
||||
port, pin := p.getPortPin()
|
||||
if high {
|
||||
port.OUTSET.Set(1 << pin)
|
||||
port.OUTSET = 1 << pin
|
||||
} else {
|
||||
port.OUTCLR.Set(1 << pin)
|
||||
port.OUTCLR = 1 << pin
|
||||
}
|
||||
}
|
||||
|
||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||
func (p GPIO) PortMaskSet() (*uint32, uint32) {
|
||||
port, pin := p.getPortPin()
|
||||
return &port.OUTSET.Reg, 1 << pin
|
||||
return (*uint32)(&port.OUTSET), 1 << pin
|
||||
}
|
||||
|
||||
// Return the register and mask to disable a given port. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||
func (p GPIO) PortMaskClear() (*uint32, uint32) {
|
||||
port, pin := p.getPortPin()
|
||||
return &port.OUTCLR.Reg, 1 << pin
|
||||
return (*uint32)(&port.OUTCLR), 1 << pin
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p Pin) Get() bool {
|
||||
func (p GPIO) Get() bool {
|
||||
port, pin := p.getPortPin()
|
||||
return (port.IN.Get()>>pin)&1 != 0
|
||||
return (port.IN>>pin)&1 != 0
|
||||
}
|
||||
|
||||
// UART on the NRF.
|
||||
@@ -77,10 +77,10 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
// Set TX and RX pins from board.
|
||||
uart.setPins(UART_TX_PIN, UART_RX_PIN)
|
||||
|
||||
nrf.UART0.ENABLE.Set(nrf.UART_ENABLE_ENABLE_Enabled)
|
||||
nrf.UART0.TASKS_STARTTX.Set(1)
|
||||
nrf.UART0.TASKS_STARTRX.Set(1)
|
||||
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
||||
nrf.UART0.ENABLE = nrf.UART_ENABLE_ENABLE_Enabled
|
||||
nrf.UART0.TASKS_STARTTX = 1
|
||||
nrf.UART0.TASKS_STARTRX = 1
|
||||
nrf.UART0.INTENSET = nrf.UART_INTENSET_RXDRDY_Msk
|
||||
|
||||
// Enable RX IRQ.
|
||||
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
||||
@@ -99,22 +99,22 @@ func (uart UART) SetBaudRate(br uint32) {
|
||||
// https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values/2046#2046
|
||||
rate := uint32((uint64(br/400)*uint64(400*0xffffffff/16000000) + 0x800) & 0xffffff000)
|
||||
|
||||
nrf.UART0.BAUDRATE.Set(rate)
|
||||
nrf.UART0.BAUDRATE = nrf.RegValue(rate)
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
nrf.UART0.EVENTS_TXDRDY.Set(0)
|
||||
nrf.UART0.TXD.Set(uint32(c))
|
||||
for nrf.UART0.EVENTS_TXDRDY.Get() == 0 {
|
||||
nrf.UART0.EVENTS_TXDRDY = 0
|
||||
nrf.UART0.TXD = nrf.RegValue(c)
|
||||
for nrf.UART0.EVENTS_TXDRDY == 0 {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart UART) handleInterrupt() {
|
||||
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
|
||||
uart.Receive(byte(nrf.UART0.RXD.Get()))
|
||||
nrf.UART0.EVENTS_RXDRDY.Set(0x0)
|
||||
if nrf.UART0.EVENTS_RXDRDY != 0 {
|
||||
uart.Receive(byte(nrf.UART0.RXD))
|
||||
nrf.UART0.EVENTS_RXDRDY = 0x0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +132,8 @@ var (
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
SCL Pin
|
||||
SDA Pin
|
||||
SCL uint8
|
||||
SDA uint8
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
@@ -149,27 +149,27 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
}
|
||||
|
||||
// do config
|
||||
sclPort, sclPin := config.SCL.getPortPin()
|
||||
sclPort.PIN_CNF[sclPin].Set((nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
||||
sclPort, sclPin := GPIO{config.SCL}.getPortPin()
|
||||
sclPort.PIN_CNF[sclPin] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
||||
(nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) |
|
||||
(nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) |
|
||||
(nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) |
|
||||
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos))
|
||||
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos)
|
||||
|
||||
sdaPort, sdaPin := config.SDA.getPortPin()
|
||||
sdaPort.PIN_CNF[sdaPin].Set((nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
||||
sdaPort, sdaPin := GPIO{config.SDA}.getPortPin()
|
||||
sdaPort.PIN_CNF[sdaPin] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
||||
(nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) |
|
||||
(nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) |
|
||||
(nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) |
|
||||
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos))
|
||||
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos)
|
||||
|
||||
if config.Frequency == TWI_FREQ_400KHZ {
|
||||
i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K400)
|
||||
i2c.Bus.FREQUENCY = nrf.TWI_FREQUENCY_FREQUENCY_K400
|
||||
} else {
|
||||
i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K100)
|
||||
i2c.Bus.FREQUENCY = nrf.TWI_FREQUENCY_FREQUENCY_K100
|
||||
}
|
||||
|
||||
i2c.Bus.ENABLE.Set(nrf.TWI_ENABLE_ENABLE_Enabled)
|
||||
i2c.Bus.ENABLE = nrf.TWI_ENABLE_ENABLE_Enabled
|
||||
i2c.setPins(config.SCL, config.SDA)
|
||||
}
|
||||
|
||||
@@ -177,28 +177,29 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
i2c.Bus.ADDRESS.Set(uint32(addr))
|
||||
i2c.Bus.ADDRESS = nrf.RegValue(addr)
|
||||
if len(w) != 0 {
|
||||
i2c.Bus.TASKS_STARTTX.Set(1) // start transmission for writing
|
||||
i2c.Bus.TASKS_STARTTX = 1 // start transmission for writing
|
||||
for _, b := range w {
|
||||
i2c.writeByte(b)
|
||||
}
|
||||
}
|
||||
if len(r) != 0 {
|
||||
// To trigger suspend task when a byte is received
|
||||
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND)
|
||||
i2c.Bus.TASKS_STARTRX.Set(1) // re-start transmission for reading
|
||||
for i := range r { // read each char
|
||||
i2c.Bus.TASKS_STARTRX = 1 // re-start transmission for reading
|
||||
for i := range r { // read each char
|
||||
if i+1 == len(r) {
|
||||
// To trigger stop task when last byte is received, set before resume task.
|
||||
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_STOP)
|
||||
// The 'stop' signal must be sent before reading back the last
|
||||
// byte, so that it will be sent by the I2C peripheral right
|
||||
// after the last byte has been read.
|
||||
r[i] = i2c.readLastByte()
|
||||
} else {
|
||||
r[i] = i2c.readByte()
|
||||
}
|
||||
i2c.Bus.TASKS_RESUME.Set(1) // re-start transmission for reading
|
||||
r[i] = i2c.readByte()
|
||||
}
|
||||
} else {
|
||||
// Nothing to read back. Stop the transmission.
|
||||
i2c.signalStop()
|
||||
}
|
||||
i2c.signalStop()
|
||||
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND_Disabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -206,26 +207,36 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
// it must generate a stop condition after the next character is retrieved when
|
||||
// reading.
|
||||
func (i2c I2C) signalStop() {
|
||||
i2c.Bus.TASKS_STOP.Set(1)
|
||||
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
|
||||
i2c.Bus.TASKS_STOP = 1
|
||||
for i2c.Bus.EVENTS_STOPPED == 0 {
|
||||
}
|
||||
i2c.Bus.EVENTS_STOPPED.Set(0)
|
||||
i2c.Bus.EVENTS_STOPPED = 0
|
||||
}
|
||||
|
||||
// writeByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) writeByte(data byte) {
|
||||
i2c.Bus.TXD.Set(uint32(data))
|
||||
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
|
||||
i2c.Bus.TXD = nrf.RegValue(data)
|
||||
for i2c.Bus.EVENTS_TXDSENT == 0 {
|
||||
}
|
||||
i2c.Bus.EVENTS_TXDSENT.Set(0)
|
||||
i2c.Bus.EVENTS_TXDSENT = 0
|
||||
}
|
||||
|
||||
// readByte reads a single byte from the I2C bus.
|
||||
func (i2c I2C) readByte() byte {
|
||||
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
|
||||
for i2c.Bus.EVENTS_RXDREADY == 0 {
|
||||
}
|
||||
i2c.Bus.EVENTS_RXDREADY.Set(0)
|
||||
return byte(i2c.Bus.RXD.Get())
|
||||
i2c.Bus.EVENTS_RXDREADY = 0
|
||||
return byte(i2c.Bus.RXD)
|
||||
}
|
||||
|
||||
// readLastByte reads a single byte from the I2C bus, sending a stop signal
|
||||
// after it has been read.
|
||||
func (i2c I2C) readLastByte() byte {
|
||||
for i2c.Bus.EVENTS_RXDREADY == 0 {
|
||||
}
|
||||
i2c.Bus.EVENTS_RXDREADY = 0
|
||||
i2c.signalStop() // signal 'stop' now, so it is sent when reading RXD
|
||||
return byte(i2c.Bus.RXD)
|
||||
}
|
||||
|
||||
// SPI on the NRF.
|
||||
@@ -242,9 +253,9 @@ var (
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
SCK Pin
|
||||
MOSI Pin
|
||||
MISO Pin
|
||||
SCK uint8
|
||||
MOSI uint8
|
||||
MISO uint8
|
||||
LSBFirst bool
|
||||
Mode uint8
|
||||
}
|
||||
@@ -252,7 +263,7 @@ type SPIConfig struct {
|
||||
// Configure is intended to setup the SPI interface.
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
// Disable bus to configure it
|
||||
spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Disabled)
|
||||
spi.Bus.ENABLE = nrf.SPI_ENABLE_ENABLE_Disabled
|
||||
|
||||
// set frequency
|
||||
var freq uint32
|
||||
@@ -275,7 +286,7 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
default:
|
||||
freq = nrf.SPI_FREQUENCY_FREQUENCY_K500
|
||||
}
|
||||
spi.Bus.FREQUENCY.Set(freq)
|
||||
spi.Bus.FREQUENCY = nrf.RegValue(freq)
|
||||
|
||||
var conf uint32
|
||||
|
||||
@@ -302,22 +313,22 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos)
|
||||
conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos)
|
||||
}
|
||||
spi.Bus.CONFIG.Set(conf)
|
||||
spi.Bus.CONFIG = nrf.RegValue(conf)
|
||||
|
||||
// set pins
|
||||
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
||||
|
||||
// Re-enable bus now that it is configured.
|
||||
spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Enabled)
|
||||
spi.Bus.ENABLE = nrf.SPI_ENABLE_ENABLE_Enabled
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
spi.Bus.TXD.Set(uint32(w))
|
||||
for spi.Bus.EVENTS_READY.Get() == 0 {
|
||||
spi.Bus.TXD = nrf.RegValue(w)
|
||||
for spi.Bus.EVENTS_READY == 0 {
|
||||
}
|
||||
r := spi.Bus.RXD.Get()
|
||||
spi.Bus.EVENTS_READY.Set(0)
|
||||
r := spi.Bus.RXD
|
||||
spi.Bus.EVENTS_READY = 0
|
||||
|
||||
// TODO: handle SPI errors
|
||||
return byte(r), nil
|
||||
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
const CPU_FREQUENCY = 16000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
return nrf.GPIO, uint32(p)
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
return nrf.GPIO, p.Pin
|
||||
}
|
||||
|
||||
func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSELTXD.Set(uint32(tx))
|
||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
func (uart UART) setPins(tx, rx uint32) {
|
||||
nrf.UART0.PSELTXD = nrf.RegValue(tx)
|
||||
nrf.UART0.PSELRXD = nrf.RegValue(rx)
|
||||
}
|
||||
|
||||
//go:export UART0_IRQHandler
|
||||
@@ -23,13 +23,13 @@ func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
func (i2c I2C) setPins(scl, sda uint8) {
|
||||
i2c.Bus.PSELSCL = nrf.RegValue(scl)
|
||||
i2c.Bus.PSELSDA = nrf.RegValue(sda)
|
||||
}
|
||||
|
||||
// SPI
|
||||
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||
func (spi SPI) setPins(sck, mosi, miso uint8) {
|
||||
if sck == 0 {
|
||||
sck = SPI0_SCK_PIN
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||
if miso == 0 {
|
||||
miso = SPI0_MISO_PIN
|
||||
}
|
||||
spi.Bus.PSELSCK.Set(uint32(sck))
|
||||
spi.Bus.PSELMOSI.Set(uint32(mosi))
|
||||
spi.Bus.PSELMISO.Set(uint32(miso))
|
||||
spi.Bus.PSELSCK = nrf.RegValue(sck)
|
||||
spi.Bus.PSELMOSI = nrf.RegValue(mosi)
|
||||
spi.Bus.PSELMISO = nrf.RegValue(miso)
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ import (
|
||||
const CPU_FREQUENCY = 64000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
return nrf.P0, uint32(p)
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
return nrf.P0, p.Pin
|
||||
}
|
||||
|
||||
func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSELTXD.Set(uint32(tx))
|
||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
func (uart UART) setPins(tx, rx uint32) {
|
||||
nrf.UART0.PSELTXD = nrf.RegValue(tx)
|
||||
nrf.UART0.PSELRXD = nrf.RegValue(rx)
|
||||
}
|
||||
|
||||
//go:export UARTE0_UART0_IRQHandler
|
||||
@@ -24,13 +24,13 @@ func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
func (i2c I2C) setPins(scl, sda uint8) {
|
||||
i2c.Bus.PSELSCL = nrf.RegValue(scl)
|
||||
i2c.Bus.PSELSDA = nrf.RegValue(sda)
|
||||
}
|
||||
|
||||
// SPI
|
||||
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||
func (spi SPI) setPins(sck, mosi, miso uint8) {
|
||||
if sck == 0 {
|
||||
sck = SPI0_SCK_PIN
|
||||
}
|
||||
@@ -40,9 +40,9 @@ func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||
if miso == 0 {
|
||||
miso = SPI0_MISO_PIN
|
||||
}
|
||||
spi.Bus.PSEL.SCK.Set(uint32(sck))
|
||||
spi.Bus.PSEL.MOSI.Set(uint32(mosi))
|
||||
spi.Bus.PSEL.MISO.Set(uint32(miso))
|
||||
spi.Bus.PSEL.SCK = nrf.RegValue(sck)
|
||||
spi.Bus.PSEL.MOSI = nrf.RegValue(mosi)
|
||||
spi.Bus.PSEL.MISO = nrf.RegValue(miso)
|
||||
}
|
||||
|
||||
// InitADC initializes the registers needed for ADC.
|
||||
@@ -89,53 +89,53 @@ func (a ADC) Get() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit)
|
||||
nrf.SAADC.RESOLUTION = nrf.SAADC_RESOLUTION_VAL_12bit
|
||||
|
||||
// Enable ADC.
|
||||
nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||
nrf.SAADC.ENABLE = (nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||
for i := 0; i < 8; i++ {
|
||||
nrf.SAADC.CH[i].PSELN.Set(nrf.SAADC_CH_PSELP_PSELP_NC)
|
||||
nrf.SAADC.CH[i].PSELP.Set(nrf.SAADC_CH_PSELP_PSELP_NC)
|
||||
nrf.SAADC.CH[i].PSELN = nrf.SAADC_CH_PSELP_PSELP_NC
|
||||
nrf.SAADC.CH[i].PSELP = nrf.SAADC_CH_PSELP_PSELP_NC
|
||||
}
|
||||
|
||||
// Configure ADC.
|
||||
nrf.SAADC.CH[0].CONFIG.Set(((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) |
|
||||
nrf.SAADC.CH[0].CONFIG = ((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk))
|
||||
((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk)
|
||||
|
||||
// Set pin to read.
|
||||
nrf.SAADC.CH[0].PSELN.Set(pwmPin)
|
||||
nrf.SAADC.CH[0].PSELP.Set(pwmPin)
|
||||
nrf.SAADC.CH[0].PSELN = nrf.RegValue(pwmPin)
|
||||
nrf.SAADC.CH[0].PSELP = nrf.RegValue(pwmPin)
|
||||
|
||||
// Destination for sample result.
|
||||
nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&value))))
|
||||
nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample
|
||||
nrf.SAADC.RESULT.PTR = nrf.RegValue(uintptr(unsafe.Pointer(&value)))
|
||||
nrf.SAADC.RESULT.MAXCNT = 1 // One sample
|
||||
|
||||
// Start tasks.
|
||||
nrf.SAADC.TASKS_START.Set(1)
|
||||
for nrf.SAADC.EVENTS_STARTED.Get() == 0 {
|
||||
nrf.SAADC.TASKS_START = 1
|
||||
for nrf.SAADC.EVENTS_STARTED == 0 {
|
||||
}
|
||||
nrf.SAADC.EVENTS_STARTED.Set(0x00)
|
||||
nrf.SAADC.EVENTS_STARTED = 0x00
|
||||
|
||||
// Start the sample task.
|
||||
nrf.SAADC.TASKS_SAMPLE.Set(1)
|
||||
nrf.SAADC.TASKS_SAMPLE = 1
|
||||
|
||||
// Wait until the sample task is done.
|
||||
for nrf.SAADC.EVENTS_END.Get() == 0 {
|
||||
for nrf.SAADC.EVENTS_END == 0 {
|
||||
}
|
||||
nrf.SAADC.EVENTS_END.Set(0x00)
|
||||
nrf.SAADC.EVENTS_END = 0x00
|
||||
|
||||
// Stop the ADC
|
||||
nrf.SAADC.TASKS_STOP.Set(1)
|
||||
for nrf.SAADC.EVENTS_STOPPED.Get() == 0 {
|
||||
nrf.SAADC.TASKS_STOP = 1
|
||||
for nrf.SAADC.EVENTS_STOPPED == 0 {
|
||||
}
|
||||
nrf.SAADC.EVENTS_STOPPED.Set(0)
|
||||
nrf.SAADC.EVENTS_STOPPED = 0
|
||||
|
||||
// Disable the ADC.
|
||||
nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||
nrf.SAADC.ENABLE = (nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||
|
||||
if value < 0 {
|
||||
value = 0
|
||||
@@ -170,21 +170,21 @@ func (pwm PWM) Set(value uint16) {
|
||||
|
||||
p := pwms[i]
|
||||
|
||||
p.PSEL.OUT[0].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[1].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[2].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[3].Set(uint32(pwm.Pin))
|
||||
p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||
p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2)
|
||||
p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up)
|
||||
p.COUNTERTOP.Set(16384) // frequency
|
||||
p.LOOP.Set(0)
|
||||
p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos))
|
||||
p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i]))))
|
||||
p.SEQ[0].CNT.Set(1)
|
||||
p.SEQ[0].REFRESH.Set(1)
|
||||
p.SEQ[0].ENDDELAY.Set(0)
|
||||
p.TASKS_SEQSTART[0].Set(1)
|
||||
p.PSEL.OUT[0] = nrf.RegValue(pwm.Pin)
|
||||
p.PSEL.OUT[1] = nrf.RegValue(pwm.Pin)
|
||||
p.PSEL.OUT[2] = nrf.RegValue(pwm.Pin)
|
||||
p.PSEL.OUT[3] = nrf.RegValue(pwm.Pin)
|
||||
p.ENABLE = (nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||
p.PRESCALER = nrf.PWM_PRESCALER_PRESCALER_DIV_2
|
||||
p.MODE = nrf.PWM_MODE_UPDOWN_Up
|
||||
p.COUNTERTOP = 16384 // frequency
|
||||
p.LOOP = 0
|
||||
p.DECODER = (nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos)
|
||||
p.SEQ[0].PTR = nrf.RegValue(uintptr(unsafe.Pointer(&pwmChannelSequence[i])))
|
||||
p.SEQ[0].CNT = 1
|
||||
p.SEQ[0].REFRESH = 1
|
||||
p.SEQ[0].ENDDELAY = 0
|
||||
p.TASKS_SEQSTART[0] = 1
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -10,17 +10,17 @@ import (
|
||||
const CPU_FREQUENCY = 64000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
if p >= 32 {
|
||||
return nrf.P1, uint32(p - 32)
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
if p.Pin >= 32 {
|
||||
return nrf.P1, p.Pin - 32
|
||||
} else {
|
||||
return nrf.P0, uint32(p)
|
||||
return nrf.P0, p.Pin
|
||||
}
|
||||
}
|
||||
|
||||
func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSEL.TXD.Set(uint32(tx))
|
||||
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||
func (uart UART) setPins(tx, rx uint32) {
|
||||
nrf.UART0.PSEL.TXD = nrf.RegValue(tx)
|
||||
nrf.UART0.PSEL.RXD = nrf.RegValue(rx)
|
||||
}
|
||||
|
||||
//go:export UARTE0_UART0_IRQHandler
|
||||
@@ -28,13 +28,13 @@ func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||
func (i2c I2C) setPins(scl, sda uint8) {
|
||||
i2c.Bus.PSEL.SCL = nrf.RegValue(scl)
|
||||
i2c.Bus.PSEL.SDA = nrf.RegValue(sda)
|
||||
}
|
||||
|
||||
// SPI
|
||||
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||
func (spi SPI) setPins(sck, mosi, miso uint8) {
|
||||
if sck == 0 {
|
||||
sck = SPI0_SCK_PIN
|
||||
}
|
||||
@@ -44,9 +44,9 @@ func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||
if miso == 0 {
|
||||
miso = SPI0_MISO_PIN
|
||||
}
|
||||
spi.Bus.PSEL.SCK.Set(uint32(sck))
|
||||
spi.Bus.PSEL.MOSI.Set(uint32(mosi))
|
||||
spi.Bus.PSEL.MISO.Set(uint32(miso))
|
||||
spi.Bus.PSEL.SCK = nrf.RegValue(sck)
|
||||
spi.Bus.PSEL.MOSI = nrf.RegValue(mosi)
|
||||
spi.Bus.PSEL.MISO = nrf.RegValue(miso)
|
||||
}
|
||||
|
||||
// InitADC initializes the registers needed for ADC.
|
||||
@@ -93,53 +93,53 @@ func (a ADC) Get() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit)
|
||||
nrf.SAADC.RESOLUTION = nrf.SAADC_RESOLUTION_VAL_12bit
|
||||
|
||||
// Enable ADC.
|
||||
nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||
nrf.SAADC.ENABLE = (nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||
for i := 0; i < 8; i++ {
|
||||
nrf.SAADC.CH[i].PSELN.Set(nrf.SAADC_CH_PSELP_PSELP_NC)
|
||||
nrf.SAADC.CH[i].PSELP.Set(nrf.SAADC_CH_PSELP_PSELP_NC)
|
||||
nrf.SAADC.CH[i].PSELN = nrf.SAADC_CH_PSELP_PSELP_NC
|
||||
nrf.SAADC.CH[i].PSELP = nrf.SAADC_CH_PSELP_PSELP_NC
|
||||
}
|
||||
|
||||
// Configure ADC.
|
||||
nrf.SAADC.CH[0].CONFIG.Set(((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) |
|
||||
nrf.SAADC.CH[0].CONFIG = ((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) |
|
||||
((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk))
|
||||
((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk)
|
||||
|
||||
// Set pin to read.
|
||||
nrf.SAADC.CH[0].PSELN.Set(pwmPin)
|
||||
nrf.SAADC.CH[0].PSELP.Set(pwmPin)
|
||||
nrf.SAADC.CH[0].PSELN = nrf.RegValue(pwmPin)
|
||||
nrf.SAADC.CH[0].PSELP = nrf.RegValue(pwmPin)
|
||||
|
||||
// Destination for sample result.
|
||||
nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&value))))
|
||||
nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample
|
||||
nrf.SAADC.RESULT.PTR = nrf.RegValue(uintptr(unsafe.Pointer(&value)))
|
||||
nrf.SAADC.RESULT.MAXCNT = 1 // One sample
|
||||
|
||||
// Start tasks.
|
||||
nrf.SAADC.TASKS_START.Set(1)
|
||||
for nrf.SAADC.EVENTS_STARTED.Get() == 0 {
|
||||
nrf.SAADC.TASKS_START = 1
|
||||
for nrf.SAADC.EVENTS_STARTED == 0 {
|
||||
}
|
||||
nrf.SAADC.EVENTS_STARTED.Set(0x00)
|
||||
nrf.SAADC.EVENTS_STARTED = 0x00
|
||||
|
||||
// Start the sample task.
|
||||
nrf.SAADC.TASKS_SAMPLE.Set(1)
|
||||
nrf.SAADC.TASKS_SAMPLE = 1
|
||||
|
||||
// Wait until the sample task is done.
|
||||
for nrf.SAADC.EVENTS_END.Get() == 0 {
|
||||
for nrf.SAADC.EVENTS_END == 0 {
|
||||
}
|
||||
nrf.SAADC.EVENTS_END.Set(0x00)
|
||||
nrf.SAADC.EVENTS_END = 0x00
|
||||
|
||||
// Stop the ADC
|
||||
nrf.SAADC.TASKS_STOP.Set(1)
|
||||
for nrf.SAADC.EVENTS_STOPPED.Get() == 0 {
|
||||
nrf.SAADC.TASKS_STOP = 1
|
||||
for nrf.SAADC.EVENTS_STOPPED == 0 {
|
||||
}
|
||||
nrf.SAADC.EVENTS_STOPPED.Set(0)
|
||||
nrf.SAADC.EVENTS_STOPPED = 0
|
||||
|
||||
// Disable the ADC.
|
||||
nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||
nrf.SAADC.ENABLE = (nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||
|
||||
if value < 0 {
|
||||
value = 0
|
||||
@@ -174,21 +174,21 @@ func (pwm PWM) Set(value uint16) {
|
||||
|
||||
p := pwms[i]
|
||||
|
||||
p.PSEL.OUT[0].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[1].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[2].Set(uint32(pwm.Pin))
|
||||
p.PSEL.OUT[3].Set(uint32(pwm.Pin))
|
||||
p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||
p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2)
|
||||
p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up)
|
||||
p.COUNTERTOP.Set(16384) // frequency
|
||||
p.LOOP.Set(0)
|
||||
p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos))
|
||||
p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i]))))
|
||||
p.SEQ[0].CNT.Set(1)
|
||||
p.SEQ[0].REFRESH.Set(1)
|
||||
p.SEQ[0].ENDDELAY.Set(0)
|
||||
p.TASKS_SEQSTART[0].Set(1)
|
||||
p.PSEL.OUT[0] = nrf.RegValue(pwm.Pin)
|
||||
p.PSEL.OUT[1] = nrf.RegValue(pwm.Pin)
|
||||
p.PSEL.OUT[2] = nrf.RegValue(pwm.Pin)
|
||||
p.PSEL.OUT[3] = nrf.RegValue(pwm.Pin)
|
||||
p.ENABLE = (nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||
p.PRESCALER = nrf.PWM_PRESCALER_PRESCALER_DIV_2
|
||||
p.MODE = nrf.PWM_MODE_UPDOWN_Up
|
||||
p.COUNTERTOP = 16384 // frequency
|
||||
p.LOOP = 0
|
||||
p.DECODER = (nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos)
|
||||
p.SEQ[0].PTR = nrf.RegValue(uintptr(unsafe.Pointer(&pwmChannelSequence[i])))
|
||||
p.SEQ[0].CNT = 1
|
||||
p.SEQ[0].REFRESH = 1
|
||||
p.SEQ[0].ENDDELAY = 0
|
||||
p.TASKS_SEQSTART[0] = 1
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -4,4 +4,15 @@ package machine
|
||||
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
|
||||
type PinMode uint8
|
||||
type GPIOMode uint8
|
||||
|
||||
const (
|
||||
portA = iota * 16
|
||||
portB
|
||||
portC
|
||||
portD
|
||||
portE
|
||||
portF
|
||||
portG
|
||||
portH
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user