mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-08 13:03:39 +00:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d5542dda7 | |||
| 8eb6039052 | |||
| 64597de344 | |||
| 515daa7d3c | |||
| ced964f039 | |||
| 8a704e43fc | |||
| 847681457f | |||
| 66d8899aa7 | |||
| b0cad7ed63 | |||
| 00cc486619 | |||
| d627208c48 | |||
| 152caa3b0a | |||
| 7ed6b45149 | |||
| c66d979ba3 | |||
| fc9188a298 | |||
| ffa38b183b | |||
| f0eb4eef5a | |||
| fa928e8cd3 | |||
| c49d80628c | |||
| af523c63d5 | |||
| aabb6ba22b | |||
| 6611578ec8 | |||
| d49a363d0d | |||
| 385d1d0a5d | |||
| 00e91ec569 | |||
| 1fd0c8d48c | |||
| d34bb7e708 | |||
| a328bbdff3 | |||
| 4ecd478d82 | |||
| 40b193f1fa | |||
| 7fd5ec8661 | |||
| 84618c45eb | |||
| c0ff4e566d | |||
| ce9b21a270 | |||
| ed7c242a09 | |||
| 46872a70b1 | |||
| e9f6e51fc8 | |||
| fa5855bff5 | |||
| 16201c41dc | |||
| 208e1719ad | |||
| a3d1f1a514 | |||
| 3e1c0d90c5 | |||
| b7197bcaae | |||
| 8890a0f3c8 | |||
| 5be412dabe | |||
| 412ec5b789 | |||
| b0e767c4a7 | |||
| 6b5b4a681d | |||
| 88bb61f287 | |||
| e169e3b996 | |||
| aa8957dd05 | |||
| 8e5731aee7 | |||
| 0cc35b6188 | |||
| 5cf7fba1a4 | |||
| e67506ee68 | |||
| f2c205a008 | |||
| c84c625585 | |||
| 9673ad3774 | |||
| 66aca428ba | |||
| ec87811420 | |||
| 776dc1e0d9 | |||
| beea2f1f30 | |||
| 5f9c683abf | |||
| 0ce4d90779 | |||
| 1047c9bd05 | |||
| 1d7cc2c242 | |||
| 23c8d15847 |
+26
-22
@@ -44,20 +44,14 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-8-v3
|
- llvm-source-8-v5
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-8-v3
|
key: llvm-source-8-v5
|
||||||
paths:
|
paths:
|
||||||
- llvm
|
- llvm-project
|
||||||
smoketest:
|
|
||||||
steps:
|
|
||||||
- run: make smoketest
|
|
||||||
smoketest-no-avr:
|
|
||||||
steps:
|
|
||||||
- run: make smoketest-no-avr
|
|
||||||
test-linux:
|
test-linux:
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@@ -73,7 +67,7 @@ commands:
|
|||||||
- run: go install .
|
- run: go install .
|
||||||
- run: go test -v
|
- run: go test -v
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- smoketest
|
- run: make smoketest RISCV=0
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
@@ -106,7 +100,7 @@ commands:
|
|||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-8-linux-v5
|
- llvm-build-8-linux-v7
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -124,7 +118,7 @@ commands:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-8-linux-v5
|
key: llvm-build-8-linux-v7
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
- run:
|
||||||
@@ -155,7 +149,12 @@ commands:
|
|||||||
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
||||||
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
||||||
tinygo version
|
tinygo version
|
||||||
- smoketest
|
- 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
|
||||||
build-macos:
|
build-macos:
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@@ -169,17 +168,17 @@ commands:
|
|||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-8-macos-v3
|
- llvm-source-8-macos-v5
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-8-macos-v3
|
key: llvm-source-8-macos-v5
|
||||||
paths:
|
paths:
|
||||||
- llvm
|
- llvm-project
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-8-macos-v4
|
- llvm-build-8-macos-v6
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -191,7 +190,7 @@ commands:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-8-macos-v4
|
key: llvm-build-8-macos-v6
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
- run:
|
||||||
@@ -215,23 +214,28 @@ commands:
|
|||||||
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
|
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
|
||||||
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
|
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
|
||||||
tinygo version
|
tinygo version
|
||||||
- smoketest-no-avr
|
- 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
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-llvm8-go111:
|
test-llvm8-go111:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.11
|
- image: circleci/golang:1.11-stretch
|
||||||
steps:
|
steps:
|
||||||
- test-linux
|
- test-linux
|
||||||
test-llvm8-go112:
|
test-llvm8-go112:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.12
|
- image: circleci/golang:1.12-stretch
|
||||||
steps:
|
steps:
|
||||||
- test-linux
|
- test-linux
|
||||||
build-linux:
|
build-linux:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.12
|
- image: circleci/golang:1.12-stretch
|
||||||
steps:
|
steps:
|
||||||
- build-linux
|
- build-linux
|
||||||
build-macos:
|
build-macos:
|
||||||
|
|||||||
+4
-2
@@ -5,10 +5,12 @@ src/device/avr/*.ld
|
|||||||
src/device/avr/*.s
|
src/device/avr/*.s
|
||||||
src/device/nrf/*.go
|
src/device/nrf/*.go
|
||||||
src/device/nrf/*.s
|
src/device/nrf/*.s
|
||||||
src/device/stm32/*.go
|
|
||||||
src/device/stm32/*.s
|
|
||||||
src/device/sam/*.go
|
src/device/sam/*.go
|
||||||
src/device/sam/*.s
|
src/device/sam/*.s
|
||||||
|
src/device/sifive/*.go
|
||||||
|
src/device/sifive/*.s
|
||||||
|
src/device/stm32/*.go
|
||||||
|
src/device/stm32/*.s
|
||||||
vendor
|
vendor
|
||||||
llvm
|
llvm
|
||||||
llvm-build
|
llvm-build
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
url = https://github.com/avr-rust/avr-mcu.git
|
url = https://github.com/avr-rust/avr-mcu.git
|
||||||
[submodule "lib/cmsis-svd"]
|
[submodule "lib/cmsis-svd"]
|
||||||
path = lib/cmsis-svd
|
path = lib/cmsis-svd
|
||||||
url = https://github.com/posborne/cmsis-svd
|
url = https://github.com/tinygo-org/cmsis-svd
|
||||||
[submodule "lib/compiler-rt"]
|
[submodule "lib/compiler-rt"]
|
||||||
path = lib/compiler-rt
|
path = lib/compiler-rt
|
||||||
url = https://github.com/llvm-mirror/compiler-rt.git
|
url = https://github.com/llvm-mirror/compiler-rt.git
|
||||||
|
|||||||
+2
-2
@@ -27,8 +27,8 @@ on a different system like Mac.
|
|||||||
|
|
||||||
## Download the source
|
## Download the source
|
||||||
|
|
||||||
The first step is to download the TinyGo sources. Then, inside the directory,
|
The first step is to download the TinyGo sources (use `--recursive` if you clone
|
||||||
perform these steps:
|
the git repository). Then, inside the directory, perform these steps:
|
||||||
|
|
||||||
dep ensure -vendor-only # download Go dependencies
|
dep ensure -vendor-only # download Go dependencies
|
||||||
make llvm-source # download LLVM
|
make llvm-source # download LLVM
|
||||||
|
|||||||
@@ -1,3 +1,50 @@
|
|||||||
|
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
|
0.6.0
|
||||||
---
|
---
|
||||||
* **command line**
|
* **command line**
|
||||||
|
|||||||
@@ -16,12 +16,16 @@ 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.
|
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.
|
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
|
### 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.
|
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).
|
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.
|
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,12 +3,12 @@
|
|||||||
all: tinygo
|
all: tinygo
|
||||||
tinygo: build/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`.
|
# Default build and source directories, as created by `make llvm-build`.
|
||||||
LLVM_BUILDDIR ?= llvm-build
|
LLVM_BUILDDIR ?= llvm-build
|
||||||
CLANG_SRC ?= llvm/tools/clang
|
CLANG_SRC ?= llvm-project/clang
|
||||||
LLD_SRC ?= llvm/tools/lld
|
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
|
||||||
|
|
||||||
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
|
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,15 +18,17 @@ ifeq ($(UNAME_S),Linux)
|
|||||||
END_GROUP = -Wl,--end-group
|
END_GROUP = -Wl,--end-group
|
||||||
endif
|
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 -lclangToolingRefactor $(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 $(END_GROUP) -lstdc++
|
||||||
|
|
||||||
LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML $(END_GROUP)
|
LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -llldMachO -llldMinGW -llldReaderWriter -llldWasm -llldYAML $(END_GROUP)
|
||||||
|
|
||||||
|
|
||||||
# For static linking.
|
# For static linking.
|
||||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config)","")
|
||||||
CGO_CXXFLAGS=-std=c++11
|
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||||
CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS))
|
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
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@@ -38,7 +40,8 @@ fmt:
|
|||||||
fmt-check:
|
fmt-check:
|
||||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
@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-stm32
|
|
||||||
|
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32
|
||||||
|
|
||||||
gen-device-avr:
|
gen-device-avr:
|
||||||
./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
||||||
@@ -53,42 +56,43 @@ 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
|
./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
|
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:
|
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
|
./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
|
go fmt ./src/device/stm32
|
||||||
|
|
||||||
|
|
||||||
# Get LLVM sources.
|
# Get LLVM sources.
|
||||||
llvm/README.txt:
|
llvm-project/README.md:
|
||||||
git clone -b release_80 https://github.com/llvm-mirror/llvm.git llvm
|
git clone -b release/8.x https://github.com/llvm/llvm-project
|
||||||
llvm/tools/clang/README.txt:
|
llvm-source: llvm-project/README.md
|
||||||
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.
|
# Configure LLVM.
|
||||||
llvm-build/build.ninja: llvm-source
|
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||||
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
|
$(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
|
||||||
|
|
||||||
# Build LLVM.
|
# Build LLVM.
|
||||||
llvm-build: llvm-build/build.ninja
|
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||||
cd llvm-build; ninja
|
cd $(LLVM_BUILDDIR); ninja
|
||||||
|
|
||||||
|
|
||||||
# Build the Go compiler.
|
# Build the Go compiler.
|
||||||
build/tinygo:
|
build/tinygo:
|
||||||
@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
|
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go build -o build/tinygo -tags byollvm .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go build -o build/tinygo -tags byollvm .
|
||||||
|
|
||||||
test:
|
test:
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test -v -tags byollvm .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test -v -tags byollvm .
|
||||||
|
|
||||||
.PHONY: smoketest smoketest-no-avr
|
tinygo-test:
|
||||||
smoketest: smoketest-no-avr
|
cd tests/tinygotest && tinygo test
|
||||||
tinygo build -size short -o test.elf -target=arduino examples/blinky1
|
|
||||||
tinygo build -size short -o test.elf -target=digispark examples/blinky1
|
.PHONY: smoketest
|
||||||
smoketest-no-avr:
|
smoketest:
|
||||||
# test all examples
|
# test all examples
|
||||||
tinygo build -size short -o test.elf -target=pca10040 examples/blinky1
|
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/adc
|
||||||
@@ -104,7 +108,7 @@ smoketest-no-avr:
|
|||||||
tinygo build -size short -o test.elf -target=pca10040 examples/serial
|
tinygo build -size short -o test.elf -target=pca10040 examples/serial
|
||||||
tinygo build -size short -o test.elf -target=pca10040 examples/test
|
tinygo build -size short -o test.elf -target=pca10040 examples/test
|
||||||
# test all targets/boards
|
# test all targets/boards
|
||||||
tinygo build -o test.elf examples/blinky2 # TODO: re-enable -size flag with MachO support
|
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=microbit examples/echo
|
||||||
tinygo build -size short -o test.elf -target=nrf52840-mdk examples/blinky1
|
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=pca10031 examples/blinky1
|
||||||
@@ -120,6 +124,13 @@ smoketest-no-avr:
|
|||||||
tinygo build -size short -o test.elf -target=stm32f4disco 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=stm32f4disco examples/blinky2
|
||||||
tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
|
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/export
|
||||||
tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
||||||
|
|
||||||
|
|||||||
@@ -43,12 +43,13 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
|||||||
|
|
||||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||||
|
|
||||||
The following 14 microcontroller boards are currently supported:
|
The following 15 microcontroller boards are currently supported:
|
||||||
|
|
||||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
* [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)
|
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||||
* [BBC micro:bit](https://microbit.org/)
|
* [BBC micro:bit](https://microbit.org/)
|
||||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||||
|
|||||||
+303
-1
@@ -36,6 +36,7 @@ type cgoPackage struct {
|
|||||||
typedefs map[string]*typedefInfo
|
typedefs map[string]*typedefInfo
|
||||||
elaboratedTypes map[string]*elaboratedTypeInfo
|
elaboratedTypes map[string]*elaboratedTypeInfo
|
||||||
enums map[string]enumInfo
|
enums map[string]enumInfo
|
||||||
|
anonStructNum int
|
||||||
}
|
}
|
||||||
|
|
||||||
// constantInfo stores some information about a CGo constant found by libclang
|
// constantInfo stores some information about a CGo constant found by libclang
|
||||||
@@ -68,8 +69,20 @@ type typedefInfo struct {
|
|||||||
// elaboratedTypeInfo contains some information about an elaborated type
|
// elaboratedTypeInfo contains some information about an elaborated type
|
||||||
// (struct, union) found in the C AST.
|
// (struct, union) found in the C AST.
|
||||||
type elaboratedTypeInfo struct {
|
type elaboratedTypeInfo struct {
|
||||||
typeExpr ast.Expr
|
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
|
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.
|
// enumInfo contains information about an enum in the C.
|
||||||
@@ -581,10 +594,299 @@ func (p *cgoPackage) addElaboratedTypes() {
|
|||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
obj.Decl = typeSpec
|
||||||
gen.Specs = append(gen.Specs, 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)
|
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:
|
// addEnumTypes adds C enums to the AST. For example, the following C code:
|
||||||
//
|
//
|
||||||
// enum option {
|
// enum option {
|
||||||
|
|||||||
+158
-69
@@ -51,6 +51,7 @@ CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
|||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||||
long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
|
long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
|
||||||
CXType tinygo_clang_getEnumDeclIntegerType(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_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
@@ -512,78 +513,48 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
var cgoName string
|
if name == "" {
|
||||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
// Anonymous record, probably inside a typedef.
|
||||||
case C.CXCursor_StructDecl:
|
typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
|
||||||
cgoName = "struct_" + name
|
if bitfieldList != nil {
|
||||||
case C.CXCursor_UnionDecl:
|
// This struct has bitfields, so we have to declare it as a
|
||||||
cgoName = "union_" + name
|
// named type (for bitfield getters/setters to work).
|
||||||
default:
|
p.anonStructNum++
|
||||||
panic("unknown record declaration")
|
cgoName := "struct_" + strconv.Itoa(p.anonStructNum)
|
||||||
}
|
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
typeExpr: typeExpr,
|
||||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
pos: pos,
|
||||||
fieldList := &ast.FieldList{
|
bitfields: bitfieldList,
|
||||||
Opening: pos,
|
}
|
||||||
Closing: pos,
|
return &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + cgoName,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ref := storedRefs.Put(struct {
|
return typeExpr
|
||||||
fieldList *ast.FieldList
|
} else {
|
||||||
pkg *cgoPackage
|
var cgoName string
|
||||||
}{fieldList, p})
|
|
||||||
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) {
|
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||||
case C.CXCursor_StructDecl:
|
case C.CXCursor_StructDecl:
|
||||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
cgoName = "struct_" + name
|
||||||
typeExpr: &ast.StructType{
|
|
||||||
Struct: pos,
|
|
||||||
Fields: fieldList,
|
|
||||||
},
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
case C.CXCursor_UnionDecl:
|
case C.CXCursor_UnionDecl:
|
||||||
if len(fieldList.List) > 1 {
|
cgoName = "union_" + name
|
||||||
// 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...)
|
|
||||||
}
|
|
||||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
|
||||||
typeExpr: &ast.StructType{
|
|
||||||
Struct: pos,
|
|
||||||
Fields: fieldList,
|
|
||||||
},
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C." + cgoName,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return &ast.Ident{
|
|
||||||
NamePos: pos,
|
|
||||||
Name: "C." + cgoName,
|
|
||||||
}
|
}
|
||||||
case C.CXType_Enum:
|
case C.CXType_Enum:
|
||||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
@@ -629,25 +600,143 @@ 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
|
//export tinygo_clang_struct_visitor
|
||||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
pkg *cgoPackage
|
||||||
|
inBitfield *bool
|
||||||
|
bitfieldNum *int
|
||||||
|
bitfieldList *[]bitfieldInfo
|
||||||
})
|
})
|
||||||
fieldList := passed.fieldList
|
fieldList := passed.fieldList
|
||||||
p := passed.pkg
|
p := passed.pkg
|
||||||
|
inBitfield := passed.inBitfield
|
||||||
|
bitfieldNum := passed.bitfieldNum
|
||||||
|
bitfieldList := passed.bitfieldList
|
||||||
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
|
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
|
||||||
panic("expected field inside cursor")
|
panic("expected field inside cursor")
|
||||||
}
|
}
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
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)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
|
pos := p.getCursorPosition(c)
|
||||||
field := &ast.Field{
|
field := &ast.Field{
|
||||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
Type: p.makeASTType(typ, p.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{
|
field.Names = []*ast.Ident{
|
||||||
&ast.Ident{
|
&ast.Ident{
|
||||||
NamePos: p.getCursorPosition(c),
|
NamePos: pos,
|
||||||
Name: name,
|
Name: name,
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Var,
|
||||||
|
|||||||
@@ -64,3 +64,7 @@ long long tinygo_clang_getEnumConstantDeclValue(CXCursor c) {
|
|||||||
CXType tinygo_clang_getEnumDeclIntegerType(CXCursor c) {
|
CXType tinygo_clang_getEnumDeclIntegerType(CXCursor c) {
|
||||||
return clang_getEnumDeclIntegerType(c);
|
return clang_getEnumDeclIntegerType(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
|
||||||
|
return clang_Cursor_isBitField(c);
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ func execCommand(cmdNames []string, args ...string) error {
|
|||||||
// this command was not found, try the next
|
// this command was not found, try the next
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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
|
// Expand an argument type to a list that can be used in a function call
|
||||||
// paramter list.
|
// parameter list.
|
||||||
func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
|
func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
|
|||||||
+169
-10
@@ -4,6 +4,7 @@ package compiler
|
|||||||
// or pseudo-operations that are lowered during goroutine lowering.
|
// or pseudo-operations that are lowered during goroutine lowering.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
@@ -12,11 +13,22 @@ import (
|
|||||||
|
|
||||||
// emitMakeChan returns a new channel value for the given channel type.
|
// emitMakeChan returns a new channel value for the given channel type.
|
||||||
func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
||||||
chanType := c.mod.GetTypeByName("runtime.channel")
|
chanType := c.getLLVMType(expr.Type())
|
||||||
size := c.targetData.TypeAllocSize(chanType)
|
size := c.targetData.TypeAllocSize(chanType.ElementType())
|
||||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||||
ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc")
|
ptr := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "chan.alloc")
|
||||||
ptr = c.builder.CreateBitCast(ptr, llvm.PointerType(chanType, 0), "chan")
|
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)
|
||||||
return ptr, nil
|
return ptr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,8 +45,7 @@ func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) {
|
|||||||
|
|
||||||
// Do the send.
|
// Do the send.
|
||||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false)
|
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast}, "")
|
||||||
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
|
||||||
|
|
||||||
// End the lifetime of the alloca.
|
// End the lifetime of the alloca.
|
||||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||||
@@ -53,8 +64,7 @@ func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
|
|||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast}, "")
|
||||||
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
|
||||||
received := c.builder.CreateLoad(valueAlloca, "chan.received")
|
received := c.builder.CreateLoad(valueAlloca, "chan.received")
|
||||||
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
|
|
||||||
@@ -72,8 +82,157 @@ func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
|
|||||||
|
|
||||||
// emitChanClose closes the given channel.
|
// emitChanClose closes the given channel.
|
||||||
func (c *Compiler) emitChanClose(frame *Frame, param ssa.Value) {
|
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)
|
ch := c.getValue(frame, param)
|
||||||
c.createRuntimeCall("chanClose", []llvm.Value{ch, valueSize}, "")
|
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, "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+110
-108
@@ -3,6 +3,7 @@ package compiler
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
"go/build"
|
"go/build"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
@@ -26,6 +27,9 @@ func init() {
|
|||||||
llvm.InitializeAllAsmPrinters()
|
llvm.InitializeAllAsmPrinters()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The TinyGo import path.
|
||||||
|
const tinygoPath = "github.com/tinygo-org/tinygo"
|
||||||
|
|
||||||
// Configure the compiler.
|
// Configure the compiler.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
||||||
@@ -37,12 +41,19 @@ type Config struct {
|
|||||||
PanicStrategy string // panic strategy ("abort" or "trap")
|
PanicStrategy string // panic strategy ("abort" or "trap")
|
||||||
CFlags []string // cflags to pass to cgo
|
CFlags []string // cflags to pass to cgo
|
||||||
LDFlags []string // ldflags 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
|
DumpSSA bool // dump Go SSA, for compiler debugging
|
||||||
Debug bool // add debug symbols for gdb
|
Debug bool // add debug symbols for gdb
|
||||||
GOROOT string // GOROOT
|
GOROOT string // GOROOT
|
||||||
TINYGOROOT string // GOROOT for TinyGo
|
TINYGOROOT string // GOROOT for TinyGo
|
||||||
GOPATH string // GOPATH, like `go env GOPATH`
|
GOPATH string // GOPATH, like `go env GOPATH`
|
||||||
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
|
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 {
|
type Compiler struct {
|
||||||
@@ -63,6 +74,7 @@ type Compiler struct {
|
|||||||
interfaceInvokeWrappers []interfaceInvokeWrapper
|
interfaceInvokeWrappers []interfaceInvokeWrapper
|
||||||
ir *ir.Program
|
ir *ir.Program
|
||||||
diagnostics []error
|
diagnostics []error
|
||||||
|
astComments map[string]*ast.CommentGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
@@ -79,6 +91,7 @@ type Frame struct {
|
|||||||
deferFuncs map[*ir.Function]int
|
deferFuncs map[*ir.Function]int
|
||||||
deferInvokeFuncs map[string]int
|
deferInvokeFuncs map[string]int
|
||||||
deferClosureFuncs map[*ir.Function]int
|
deferClosureFuncs map[*ir.Function]int
|
||||||
|
selectRecvBuf map[*ssa.Select]llvm.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
type Phi struct {
|
type Phi struct {
|
||||||
@@ -154,11 +167,10 @@ func (c *Compiler) TargetData() llvm.TargetData {
|
|||||||
|
|
||||||
// selectGC picks an appropriate GC strategy if none was provided.
|
// selectGC picks an appropriate GC strategy if none was provided.
|
||||||
func (c *Compiler) selectGC() string {
|
func (c *Compiler) selectGC() string {
|
||||||
gc := c.GC
|
if c.GC != "" {
|
||||||
if gc == "" {
|
return c.GC
|
||||||
gc = "dumb"
|
|
||||||
}
|
}
|
||||||
return gc
|
return "conservative"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the given package path or .go file path. Return an error when this
|
// Compile the given package path or .go file path. Return an error when this
|
||||||
@@ -198,22 +210,29 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
Compiler: "gc", // must be one of the recognized compilers
|
Compiler: "gc", // must be one of the recognized compilers
|
||||||
BuildTags: append([]string{"tinygo", "gc." + c.selectGC()}, c.BuildTags...),
|
BuildTags: append([]string{"tinygo", "gc." + c.selectGC()}, c.BuildTags...),
|
||||||
},
|
},
|
||||||
ShouldOverlay: func(path string) bool {
|
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/"):]
|
||||||
|
}
|
||||||
switch path {
|
switch path {
|
||||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync":
|
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync", "testing":
|
||||||
return true
|
return path
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||||
return true
|
return path
|
||||||
} else if path == "syscall" {
|
} else if path == "syscall" {
|
||||||
for _, tag := range c.BuildTags {
|
for _, tag := range c.BuildTags {
|
||||||
if tag == "avr" || tag == "cortexm" || tag == "darwin" {
|
if tag == "avr" || tag == "cortexm" || tag == "darwin" || tag == "riscv" {
|
||||||
return true
|
return path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return ""
|
||||||
},
|
},
|
||||||
TypeChecker: types.Config{
|
TypeChecker: types.Config{
|
||||||
Sizes: &StdSizes{
|
Sizes: &StdSizes{
|
||||||
@@ -222,10 +241,12 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Dir: wd,
|
Dir: wd,
|
||||||
TINYGOROOT: c.TINYGOROOT,
|
TINYGOROOT: c.TINYGOROOT,
|
||||||
CFlags: c.CFlags,
|
CFlags: c.CFlags,
|
||||||
|
ClangHeaders: c.ClangHeaders,
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(mainPath, ".go") {
|
if strings.HasSuffix(mainPath, ".go") {
|
||||||
_, err = lprogram.ImportFile(mainPath)
|
_, err = lprogram.ImportFile(mainPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -237,12 +258,13 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = lprogram.Import("runtime", "")
|
_, err = lprogram.Import("runtime", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = lprogram.Parse()
|
err = lprogram.Parse(c.TestConfig.CompileTestBinary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
@@ -265,39 +287,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
|
|
||||||
var frames []*Frame
|
var frames []*Frame
|
||||||
|
|
||||||
// Declare all named struct types.
|
c.loadASTComments(lprogram)
|
||||||
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.
|
// Declare all functions.
|
||||||
for _, f := range c.ir.Functions {
|
for _, f := range c.ir.Functions {
|
||||||
@@ -371,6 +361,15 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
// See emitNilCheck in asserts.go.
|
// See emitNilCheck in asserts.go.
|
||||||
c.mod.NamedFunction("runtime.isnil").AddAttributeAtIndex(1, nocapture)
|
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
|
// Memory copy operations do not capture pointers, even though some weird
|
||||||
// pointer arithmetic is happening in the Go implementation.
|
// pointer arithmetic is happening in the Go implementation.
|
||||||
for _, fnName := range []string{"runtime.memcpy", "runtime.memmove"} {
|
for _, fnName := range []string{"runtime.memcpy", "runtime.memmove"} {
|
||||||
@@ -403,6 +402,22 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
return c.diagnostics
|
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 {
|
func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
||||||
switch typ := goType.(type) {
|
switch typ := goType.(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
@@ -431,7 +446,7 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
|||||||
case types.Complex128:
|
case types.Complex128:
|
||||||
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
||||||
case types.String, types.UntypedString:
|
case types.String, types.UntypedString:
|
||||||
return c.mod.GetTypeByName("runtime._string")
|
return c.getLLVMRuntimeType("_string")
|
||||||
case types.Uintptr:
|
case types.Uintptr:
|
||||||
return c.uintptrType
|
return c.uintptrType
|
||||||
case types.UnsafePointer:
|
case types.UnsafePointer:
|
||||||
@@ -440,16 +455,23 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
|||||||
panic("unknown basic type: " + typ.String())
|
panic("unknown basic type: " + typ.String())
|
||||||
}
|
}
|
||||||
case *types.Chan:
|
case *types.Chan:
|
||||||
return llvm.PointerType(c.mod.GetTypeByName("runtime.channel"), 0)
|
return llvm.PointerType(c.getLLVMRuntimeType("channel"), 0)
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
return c.mod.GetTypeByName("runtime._interface")
|
return c.getLLVMRuntimeType("_interface")
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
return llvm.PointerType(c.mod.GetTypeByName("runtime.hashmap"), 0)
|
return llvm.PointerType(c.getLLVMRuntimeType("hashmap"), 0)
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
if _, ok := typ.Underlying().(*types.Struct); ok {
|
if st, ok := typ.Underlying().(*types.Struct); ok {
|
||||||
llvmType := c.mod.GetTypeByName(typ.Obj().Pkg().Path() + "." + typ.Obj().Name())
|
// 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 llvmType.IsNil() {
|
if llvmType.IsNil() {
|
||||||
panic("underlying type not found: " + typ.Obj().Pkg().Path() + "." + typ.Obj().Name())
|
llvmType = c.ctx.StructCreateNamed(llvmName)
|
||||||
|
underlying := c.getLLVMType(st)
|
||||||
|
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||||
}
|
}
|
||||||
return llvmType
|
return llvmType
|
||||||
}
|
}
|
||||||
@@ -826,7 +848,8 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||||
}
|
}
|
||||||
if !frame.fn.LLVMFn.IsDeclaration() {
|
if !frame.fn.LLVMFn.IsDeclaration() {
|
||||||
panic("function is already defined: " + frame.fn.LLVMFn.Name())
|
c.addError(frame.fn.Pos(), "function is already defined:"+frame.fn.LLVMFn.Name())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if !frame.fn.IsExported() {
|
if !frame.fn.IsExported() {
|
||||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||||
@@ -842,6 +865,10 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||||||
// Add LLVM inline hint to functions with //go:inline pragma.
|
// Add LLVM inline hint to functions with //go:inline pragma.
|
||||||
inline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
|
inline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
|
||||||
frame.fn.LLVMFn.AddFunctionAttr(inline)
|
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.
|
// Add debug info, if needed.
|
||||||
@@ -994,6 +1021,9 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
|||||||
frame.locals[instr] = llvm.Undef(c.getLLVMType(instr.Type()))
|
frame.locals[instr] = llvm.Undef(c.getLLVMType(instr.Type()))
|
||||||
} else {
|
} else {
|
||||||
frame.locals[instr] = value
|
frame.locals[instr] = value
|
||||||
|
if len(*instr.Referrers()) != 0 && c.needsStackObjects() {
|
||||||
|
c.trackExpr(frame, instr, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case *ssa.DebugRef:
|
case *ssa.DebugRef:
|
||||||
// ignore
|
// ignore
|
||||||
@@ -1075,12 +1105,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
|||||||
// nothing to store
|
// nothing to store
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
store := c.builder.CreateStore(llvmVal, llvmAddr)
|
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:
|
default:
|
||||||
c.addError(instr.Pos(), "unknown instruction: "+instr.String())
|
c.addError(instr.Pos(), "unknown instruction: "+instr.String())
|
||||||
}
|
}
|
||||||
@@ -1283,11 +1308,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
|||||||
if fn := instr.StaticCallee(); fn != nil {
|
if fn := instr.StaticCallee(); fn != nil {
|
||||||
name := fn.RelString(nil)
|
name := fn.RelString(nil)
|
||||||
switch {
|
switch {
|
||||||
case name == "device/arm.ReadRegister":
|
case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister":
|
||||||
return c.emitReadRegister(instr.Args)
|
return c.emitReadRegister(name, instr.Args)
|
||||||
case name == "device/arm.Asm" || name == "device/avr.Asm":
|
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||||
return c.emitAsm(instr.Args)
|
return c.emitAsm(instr.Args)
|
||||||
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull":
|
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||||
return c.emitAsmFull(frame, instr)
|
return c.emitAsmFull(frame, instr)
|
||||||
case strings.HasPrefix(name, "device/arm.SVCall"):
|
case strings.HasPrefix(name, "device/arm.SVCall"):
|
||||||
return c.emitSVCall(frame, instr.Args)
|
return c.emitSVCall(frame, instr.Args)
|
||||||
@@ -1327,10 +1352,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
|||||||
value := c.getValue(frame, instr.Value)
|
value := c.getValue(frame, instr.Value)
|
||||||
// This is a func value, which cannot be called directly. We have to
|
// This is a func value, which cannot be called directly. We have to
|
||||||
// extract the function pointer and context first from the func value.
|
// extract the function pointer and context first from the func value.
|
||||||
funcPtr, context, err := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
funcPtr, context := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
c.emitNilCheck(frame, funcPtr, "fpcall")
|
c.emitNilCheck(frame, funcPtr, "fpcall")
|
||||||
return c.parseFunctionCall(frame, instr.Args, funcPtr, context, false), nil
|
return c.parseFunctionCall(frame, instr.Args, funcPtr, context, false), nil
|
||||||
}
|
}
|
||||||
@@ -1350,9 +1372,9 @@ func (c *Compiler) getValue(frame *Frame, expr ssa.Value) llvm.Value {
|
|||||||
}
|
}
|
||||||
return c.createFuncValue(fn.LLVMFn, llvm.Undef(c.i8ptrType), fn.Signature)
|
return c.createFuncValue(fn.LLVMFn, llvm.Undef(c.i8ptrType), fn.Signature)
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
value := c.ir.GetGlobal(expr).LLVMGlobal
|
value := c.getGlobal(expr)
|
||||||
if value.IsNil() {
|
if value.IsNil() {
|
||||||
c.addError(expr.Pos(), "global not found: "+c.ir.GetGlobal(expr).LinkName())
|
c.addError(expr.Pos(), "global not found: "+expr.RelString(nil))
|
||||||
return llvm.Undef(c.getLLVMType(expr.Type()))
|
return llvm.Undef(c.getLLVMType(expr.Type()))
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
@@ -1385,7 +1407,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
// Size would be truncated if truncated to uintptr.
|
// Size would be truncated if truncated to uintptr.
|
||||||
return llvm.Value{}, c.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size))
|
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)
|
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), "")
|
buf = c.builder.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
|
||||||
@@ -1449,9 +1470,11 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
x := c.getValue(frame, expr.X)
|
x := c.getValue(frame, expr.X)
|
||||||
return c.parseConvert(expr.X.Type(), expr.Type(), x, expr.Pos())
|
return c.parseConvert(expr.X.Type(), expr.Type(), x, expr.Pos())
|
||||||
case *ssa.Extract:
|
case *ssa.Extract:
|
||||||
|
if _, ok := expr.Tuple.(*ssa.Select); ok {
|
||||||
|
return c.getChanSelectResult(frame, expr), nil
|
||||||
|
}
|
||||||
value := c.getValue(frame, expr.Tuple)
|
value := c.getValue(frame, expr.Tuple)
|
||||||
result := c.builder.CreateExtractValue(value, expr.Index, "")
|
return c.builder.CreateExtractValue(value, expr.Index, ""), nil
|
||||||
return result, nil
|
|
||||||
case *ssa.Field:
|
case *ssa.Field:
|
||||||
value := c.getValue(frame, expr.X)
|
value := c.getValue(frame, expr.X)
|
||||||
if s := expr.X.Type().Underlying().(*types.Struct); s.NumFields() > 2 && s.Field(0).Name() == "C union" {
|
if s := expr.X.Type().Underlying().(*types.Struct); s.NumFields() > 2 && s.Field(0).Name() == "C union" {
|
||||||
@@ -1630,7 +1653,6 @@ 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))
|
c.emitSliceBoundsCheck(frame, maxSize, sliceLen, sliceCap, expr.Len.Type().(*types.Basic), expr.Cap.Type().(*types.Basic))
|
||||||
|
|
||||||
// Allocate the backing array.
|
// Allocate the backing array.
|
||||||
// TODO: escape analysis
|
|
||||||
sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
@@ -1690,9 +1712,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
var iteratorType llvm.Type
|
var iteratorType llvm.Type
|
||||||
switch typ := expr.X.Type().Underlying().(type) {
|
switch typ := expr.X.Type().Underlying().(type) {
|
||||||
case *types.Basic: // string
|
case *types.Basic: // string
|
||||||
iteratorType = c.mod.GetTypeByName("runtime.stringIterator")
|
iteratorType = c.getLLVMRuntimeType("stringIterator")
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
iteratorType = c.mod.GetTypeByName("runtime.hashmapIterator")
|
iteratorType = c.getLLVMRuntimeType("hashmapIterator")
|
||||||
default:
|
default:
|
||||||
panic("unknown type in range: " + typ.String())
|
panic("unknown type in range: " + typ.String())
|
||||||
}
|
}
|
||||||
@@ -1700,25 +1722,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
c.builder.CreateStore(c.getZeroValue(iteratorType), it)
|
c.builder.CreateStore(c.getZeroValue(iteratorType), it)
|
||||||
return it, nil
|
return it, nil
|
||||||
case *ssa.Select:
|
case *ssa.Select:
|
||||||
if len(expr.States) == 0 {
|
return c.emitSelect(frame, expr), nil
|
||||||
// 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:
|
case *ssa.Slice:
|
||||||
if expr.Max != nil {
|
if expr.Max != nil {
|
||||||
return llvm.Value{}, c.makeError(expr.Pos(), "todo: full slice expressions (with max): "+expr.Type().String())
|
return llvm.Value{}, c.makeError(expr.Pos(), "todo: full slice expressions (with max): "+expr.Type().String())
|
||||||
@@ -1852,7 +1856,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
|
|
||||||
newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
|
newPtr := c.builder.CreateInBoundsGEP(oldPtr, []llvm.Value{low}, "")
|
||||||
newLen := c.builder.CreateSub(high, low, "")
|
newLen := c.builder.CreateSub(high, low, "")
|
||||||
str := llvm.Undef(c.mod.GetTypeByName("runtime._string"))
|
str := llvm.Undef(c.getLLVMRuntimeType("_string"))
|
||||||
str = c.builder.CreateInsertValue(str, newPtr, 0, "")
|
str = c.builder.CreateInsertValue(str, newPtr, 0, "")
|
||||||
str = c.builder.CreateInsertValue(str, newLen, 1, "")
|
str = c.builder.CreateInsertValue(str, newLen, 1, "")
|
||||||
return str, nil
|
return str, nil
|
||||||
@@ -2231,7 +2235,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
|||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
strPtr := c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
strPtr := c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||||
strObj := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._string"), []llvm.Value{strPtr, strLen})
|
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||||
return strObj
|
return strObj
|
||||||
} else if typ.Kind() == types.UnsafePointer {
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
if !expr.IsNil() {
|
if !expr.IsNil() {
|
||||||
@@ -2284,7 +2288,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) llvm.Value {
|
|||||||
llvm.ConstInt(c.uintptrType, 0, false),
|
llvm.ConstInt(c.uintptrType, 0, false),
|
||||||
llvm.ConstPointerNull(c.i8ptrType),
|
llvm.ConstPointerNull(c.i8ptrType),
|
||||||
}
|
}
|
||||||
return llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), fields)
|
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
if expr.Value != nil {
|
if expr.Value != nil {
|
||||||
panic("expected nil pointer constant")
|
panic("expected nil pointer constant")
|
||||||
@@ -2467,8 +2471,10 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p
|
|||||||
switch elemType.Kind() {
|
switch elemType.Kind() {
|
||||||
case types.Byte:
|
case types.Byte:
|
||||||
return c.createRuntimeCall("stringToBytes", []llvm.Value{value}, ""), nil
|
return c.createRuntimeCall("stringToBytes", []llvm.Value{value}, ""), nil
|
||||||
|
case types.Rune:
|
||||||
|
return c.createRuntimeCall("stringToRunes", []llvm.Value{value}, ""), nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(pos, "todo: convert from string: "+elemType.String())
|
panic("unexpected type in string to slice conversion")
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -2494,7 +2500,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())
|
return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
|
||||||
}
|
}
|
||||||
case token.MUL: // *x, dereference pointer
|
case token.MUL: // *x, dereference pointer
|
||||||
valType := unop.X.Type().Underlying().(*types.Pointer).Elem()
|
unop.X.Type().Underlying().(*types.Pointer).Elem()
|
||||||
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
||||||
// zero-length data
|
// zero-length data
|
||||||
return c.getZeroValue(x.Type().ElementType()), nil
|
return c.getZeroValue(x.Type().ElementType()), nil
|
||||||
@@ -2504,8 +2510,8 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
|||||||
// var C.add unsafe.Pointer
|
// var C.add unsafe.Pointer
|
||||||
// Instead of a load from the global, create a bitcast of the
|
// Instead of a load from the global, create a bitcast of the
|
||||||
// function pointer itself.
|
// function pointer itself.
|
||||||
global := c.ir.GetGlobal(unop.X.(*ssa.Global))
|
globalName := c.getGlobalInfo(unop.X.(*ssa.Global)).linkName
|
||||||
name := global.LinkName()[:len(global.LinkName())-len("$funcaddr")]
|
name := globalName[:len(globalName)-len("$funcaddr")]
|
||||||
fn := c.mod.NamedFunction(name)
|
fn := c.mod.NamedFunction(name)
|
||||||
if fn.IsNil() {
|
if fn.IsNil() {
|
||||||
return llvm.Value{}, c.makeError(unop.Pos(), "cgo function not found: "+name)
|
return llvm.Value{}, c.makeError(unop.Pos(), "cgo function not found: "+name)
|
||||||
@@ -2514,10 +2520,6 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
|||||||
} else {
|
} else {
|
||||||
c.emitNilCheck(frame, x, "deref")
|
c.emitNilCheck(frame, x, "deref")
|
||||||
load := c.builder.CreateLoad(x, "")
|
load := c.builder.CreateLoad(x, "")
|
||||||
if c.ir.IsVolatile(valType) {
|
|
||||||
// Volatile load, for memory-mapped registers.
|
|
||||||
load.SetVolatile(true)
|
|
||||||
}
|
|
||||||
return load, nil
|
return load, nil
|
||||||
}
|
}
|
||||||
case token.XOR: // ^x, toggle all bits in integer
|
case token.XOR: // ^x, toggle all bits in integer
|
||||||
|
|||||||
+7
-4
@@ -29,7 +29,7 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
|||||||
frame.deferClosureFuncs = make(map[*ir.Function]int)
|
frame.deferClosureFuncs = make(map[*ir.Function]int)
|
||||||
|
|
||||||
// Create defer list pointer.
|
// Create defer list pointer.
|
||||||
deferType := llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)
|
deferType := llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)
|
||||||
frame.deferPtr = c.builder.CreateAlloca(deferType, "deferPtr")
|
frame.deferPtr = c.builder.CreateAlloca(deferType, "deferPtr")
|
||||||
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
||||||
}
|
}
|
||||||
@@ -130,6 +130,9 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
|||||||
// Put this struct in an alloca.
|
// Put this struct in an alloca.
|
||||||
alloca := c.builder.CreateAlloca(deferFrameType, "defer.alloca")
|
alloca := c.builder.CreateAlloca(deferFrameType, "defer.alloca")
|
||||||
c.builder.CreateStore(deferFrame, alloca)
|
c.builder.CreateStore(deferFrame, alloca)
|
||||||
|
if c.needsStackObjects() {
|
||||||
|
c.trackPointer(alloca)
|
||||||
|
}
|
||||||
|
|
||||||
// Push it on top of the linked list by replacing deferPtr.
|
// Push it on top of the linked list by replacing deferPtr.
|
||||||
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||||
@@ -200,7 +203,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0), c.i8ptrType}
|
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0), c.i8ptrType}
|
||||||
for _, arg := range callback.Args {
|
for _, arg := range callback.Args {
|
||||||
valueTypes = append(valueTypes, c.getLLVMType(arg.Type()))
|
valueTypes = append(valueTypes, c.getLLVMType(arg.Type()))
|
||||||
}
|
}
|
||||||
@@ -231,7 +234,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
|||||||
// Direct call.
|
// Direct call.
|
||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)}
|
||||||
for _, param := range callback.Params {
|
for _, param := range callback.Params {
|
||||||
valueTypes = append(valueTypes, c.getLLVMType(param.Type()))
|
valueTypes = append(valueTypes, c.getLLVMType(param.Type()))
|
||||||
}
|
}
|
||||||
@@ -260,7 +263,7 @@ func (c *Compiler) emitRunDefers(frame *Frame) {
|
|||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
fn := c.ir.GetFunction(callback.Fn.(*ssa.Function))
|
fn := c.ir.GetFunction(callback.Fn.(*ssa.Function))
|
||||||
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)}
|
||||||
params := fn.Signature.Params()
|
params := fn.Signature.Params()
|
||||||
for i := 0; i < params.Len(); i++ {
|
for i := 0; i < params.Len(); i++ {
|
||||||
valueTypes = append(valueTypes, c.getLLVMType(params.At(i).Type()))
|
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.
|
// Find all func values used in the program with their signatures.
|
||||||
funcValueWithSignaturePtr := llvm.PointerType(c.mod.GetTypeByName("runtime.funcValueWithSignature"), 0)
|
funcValueWithSignaturePtr := llvm.PointerType(c.getLLVMRuntimeType("funcValueWithSignature"), 0)
|
||||||
signatures := map[string]*funcSignatureInfo{}
|
signatures := map[string]*funcSignatureInfo{}
|
||||||
for global := c.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
for global := c.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||||
if global.Type() != funcValueWithSignaturePtr {
|
if global.Type() != funcValueWithSignaturePtr {
|
||||||
|
|||||||
+3
-3
@@ -52,7 +52,7 @@ func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signa
|
|||||||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||||
if funcValueWithSignatureGlobal.IsNil() {
|
if funcValueWithSignatureGlobal.IsNil() {
|
||||||
funcValueWithSignatureType := c.mod.GetTypeByName("runtime.funcValueWithSignature")
|
funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
|
||||||
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
|
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
|
||||||
llvm.ConstPtrToInt(funcPtr, c.uintptrType),
|
llvm.ConstPtrToInt(funcPtr, c.uintptrType),
|
||||||
sigGlobal,
|
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
|
// decodeFuncValue extracts the context and the function pointer from this func
|
||||||
// value. This may be an expensive operation.
|
// value. This may be an expensive operation.
|
||||||
func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value, err error) {
|
func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
||||||
context = c.builder.CreateExtractValue(funcValue, 0, "")
|
context = c.builder.CreateExtractValue(funcValue, 0, "")
|
||||||
switch c.funcImplementation() {
|
switch c.funcImplementation() {
|
||||||
case funcValueDoubleword:
|
case funcValueDoubleword:
|
||||||
@@ -126,7 +126,7 @@ func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type {
|
|||||||
rawPtr := c.getRawFuncType(typ)
|
rawPtr := c.getRawFuncType(typ)
|
||||||
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false)
|
||||||
case funcValueSwitch:
|
case funcValueSwitch:
|
||||||
return c.mod.GetTypeByName("runtime.funcValue")
|
return c.getLLVMRuntimeType("funcValue")
|
||||||
default:
|
default:
|
||||||
panic("unimplemented func value variant")
|
panic("unimplemented func value variant")
|
||||||
}
|
}
|
||||||
|
|||||||
+471
@@ -0,0 +1,471 @@
|
|||||||
|
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.
|
// Coroutine setup.
|
||||||
c.builder.SetInsertPointBefore(f.EntryBasicBlock().FirstInstruction())
|
c.builder.SetInsertPointBefore(f.EntryBasicBlock().FirstInstruction())
|
||||||
taskState := c.builder.CreateAlloca(c.mod.GetTypeByName("runtime.taskState"), "task.state")
|
taskState := c.builder.CreateAlloca(c.getLLVMRuntimeType("taskState"), "task.state")
|
||||||
stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8")
|
stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8")
|
||||||
id := c.builder.CreateCall(coroIdFunc, []llvm.Value{
|
id := c.builder.CreateCall(coroIdFunc, []llvm.Value{
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||||
@@ -336,6 +336,9 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
size = c.builder.CreateZExt(size, c.uintptrType, "task.size.uintptr")
|
size = c.builder.CreateZExt(size, c.uintptrType, "task.size.uintptr")
|
||||||
}
|
}
|
||||||
data := c.createRuntimeCall("alloc", []llvm.Value{size}, "task.data")
|
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")
|
frame.taskHandle = c.builder.CreateCall(coroBeginFunc, []llvm.Value{id, data}, "task.handle")
|
||||||
|
|
||||||
// Modify async calls so this function suspends right after the child
|
// Modify async calls so this function suspends right after the child
|
||||||
|
|||||||
+11
-2
@@ -18,10 +18,19 @@ import (
|
|||||||
// func ReadRegister(name string) uintptr
|
// func ReadRegister(name string) uintptr
|
||||||
//
|
//
|
||||||
// The register name must be a constant, for example "sp".
|
// The register name must be a constant, for example "sp".
|
||||||
func (c *Compiler) emitReadRegister(args []ssa.Value) (llvm.Value, error) {
|
func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
|
||||||
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false)
|
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false)
|
||||||
regname := constant.StringVal(args[0].(*ssa.Const).Value)
|
regname := constant.StringVal(args[0].(*ssa.Const).Value)
|
||||||
target := llvm.InlineAsm(fnType, "mov $0, "+regname, "=r", false, false, 0)
|
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)
|
||||||
return c.builder.CreateCall(target, nil, ""), nil
|
return c.builder.CreateCall(target, nil, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ func (c *Compiler) LowerInterfaces() {
|
|||||||
// run runs the pass itself.
|
// run runs the pass itself.
|
||||||
func (p *lowerInterfacesPass) run() {
|
func (p *lowerInterfacesPass) run() {
|
||||||
// Collect all type codes.
|
// Collect all type codes.
|
||||||
typecodeIDPtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typecodeID"), 0)
|
typecodeIDPtr := llvm.PointerType(p.getLLVMRuntimeType("typecodeID"), 0)
|
||||||
typeInInterfacePtr := llvm.PointerType(p.mod.GetTypeByName("runtime.typeInInterface"), 0)
|
typeInInterfacePtr := llvm.PointerType(p.getLLVMRuntimeType("typeInInterface"), 0)
|
||||||
var typesInInterfaces []llvm.Value
|
var typesInInterfaces []llvm.Value
|
||||||
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||||
switch global.Type() {
|
switch global.Type() {
|
||||||
|
|||||||
@@ -28,14 +28,14 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.
|
|||||||
itfMethodSetGlobal := c.getTypeMethodSet(typ)
|
itfMethodSetGlobal := c.getTypeMethodSet(typ)
|
||||||
itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
||||||
if itfConcreteTypeGlobal.IsNil() {
|
if itfConcreteTypeGlobal.IsNil() {
|
||||||
typeInInterface := c.mod.GetTypeByName("runtime.typeInInterface")
|
typeInInterface := c.getLLVMRuntimeType("typeInInterface")
|
||||||
itfConcreteTypeGlobal = llvm.AddGlobal(c.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
|
itfConcreteTypeGlobal = llvm.AddGlobal(c.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
|
||||||
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
|
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
|
||||||
itfConcreteTypeGlobal.SetGlobalConstant(true)
|
itfConcreteTypeGlobal.SetGlobalConstant(true)
|
||||||
itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage)
|
itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage)
|
||||||
}
|
}
|
||||||
itfTypeCode := c.builder.CreatePtrToInt(itfConcreteTypeGlobal, c.uintptrType, "")
|
itfTypeCode := c.builder.CreatePtrToInt(itfConcreteTypeGlobal, c.uintptrType, "")
|
||||||
itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface"))
|
itf := llvm.Undef(c.getLLVMRuntimeType("_interface"))
|
||||||
itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "")
|
itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "")
|
||||||
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
|
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
|
||||||
return itf
|
return itf
|
||||||
@@ -48,7 +48,7 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
globalName := "type:" + getTypeCodeName(typ)
|
globalName := "type:" + getTypeCodeName(typ)
|
||||||
global := c.mod.NamedGlobal(globalName)
|
global := c.mod.NamedGlobal(globalName)
|
||||||
if global.IsNil() {
|
if global.IsNil() {
|
||||||
global = llvm.AddGlobal(c.mod, c.mod.GetTypeByName("runtime.typecodeID"), globalName)
|
global = llvm.AddGlobal(c.mod, c.getLLVMRuntimeType("typecodeID"), globalName)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
}
|
}
|
||||||
return global
|
return global
|
||||||
@@ -163,11 +163,11 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||||||
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
||||||
if ms.Len() == 0 {
|
if ms.Len() == 0 {
|
||||||
// no methods, so can leave that one out
|
// no methods, so can leave that one out
|
||||||
return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0))
|
return llvm.ConstPointerNull(llvm.PointerType(c.getLLVMRuntimeType("interfaceMethodInfo"), 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
methods := make([]llvm.Value, ms.Len())
|
methods := make([]llvm.Value, ms.Len())
|
||||||
interfaceMethodInfoType := c.mod.GetTypeByName("runtime.interfaceMethodInfo")
|
interfaceMethodInfoType := c.getLLVMRuntimeType("interfaceMethodInfo")
|
||||||
for i := 0; i < ms.Len(); i++ {
|
for i := 0; i < ms.Len(); i++ {
|
||||||
method := ms.At(i)
|
method := ms.At(i)
|
||||||
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
||||||
|
|||||||
+5
-3
@@ -35,7 +35,7 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||||||
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||||
} else {
|
} else {
|
||||||
// Not trivially comparable using memcmp.
|
// Not trivially comparable using memcmp.
|
||||||
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the resulting value from the hashmap. The value is set to the zero
|
// Load the resulting value from the hashmap. The value is set to the zero
|
||||||
@@ -69,7 +69,7 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, p
|
|||||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
c.createRuntimeCall("hashmapBinarySet", params, "")
|
||||||
c.emitLifetimeEnd(keyPtr, keySize)
|
c.emitLifetimeEnd(keyPtr, keySize)
|
||||||
} else {
|
} else {
|
||||||
c.addError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
c.addError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
}
|
}
|
||||||
c.emitLifetimeEnd(valuePtr, valueSize)
|
c.emitLifetimeEnd(valuePtr, valueSize)
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke
|
|||||||
c.emitLifetimeEnd(keyPtr, keySize)
|
c.emitLifetimeEnd(keyPtr, keySize)
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
return c.makeError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +121,8 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
|
|||||||
switch keyType := keyType.(type) {
|
switch keyType := keyType.(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
|
||||||
|
case *types.Pointer:
|
||||||
|
return true
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
for i := 0; i < keyType.NumFields(); i++ {
|
for i := 0; i < keyType.NumFields(); i++ {
|
||||||
fieldType := keyType.Field(i).Type().Underlying()
|
fieldType := keyType.Field(i).Type().Underlying()
|
||||||
|
|||||||
@@ -114,6 +114,14 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
builder.Populate(modPasses)
|
builder.Populate(modPasses)
|
||||||
modPasses.Run(c.mod)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-6
@@ -4,7 +4,6 @@ package compiler
|
|||||||
// compiler builtins.
|
// compiler builtins.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/constant"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
@@ -14,7 +13,7 @@ import (
|
|||||||
// emitSyscall emits an inline system call instruction, depending on the target
|
// emitSyscall emits an inline system call instruction, depending on the target
|
||||||
// OS/arch.
|
// OS/arch.
|
||||||
func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
|
func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value)
|
num := c.getValue(frame, call.Args[0])
|
||||||
var syscallResult llvm.Value
|
var syscallResult llvm.Value
|
||||||
switch {
|
switch {
|
||||||
case c.GOARCH == "amd64":
|
case c.GOARCH == "amd64":
|
||||||
@@ -29,12 +28,12 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
|||||||
// > All system classes enter the kernel via the syscall instruction.
|
// > 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
|
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
||||||
num += 0x2000000
|
num = c.builder.CreateOr(num, llvm.ConstInt(c.uintptrType, 0x2000000, false), "")
|
||||||
}
|
}
|
||||||
// Sources:
|
// Sources:
|
||||||
// https://stackoverflow.com/a/2538212
|
// https://stackoverflow.com/a/2538212
|
||||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
||||||
args := []llvm.Value{llvm.ConstInt(c.uintptrType, num, false)}
|
args := []llvm.Value{num}
|
||||||
argTypes := []llvm.Type{c.uintptrType}
|
argTypes := []llvm.Type{c.uintptrType}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
||||||
@@ -81,7 +80,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
|||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
args = append(args, llvm.ConstInt(c.uintptrType, num, false))
|
args = append(args, num)
|
||||||
argTypes = append(argTypes, c.uintptrType)
|
argTypes = append(argTypes, c.uintptrType)
|
||||||
constraints += ",{r7}" // syscall number
|
constraints += ",{r7}" // syscall number
|
||||||
for i := len(call.Args) - 1; i < 4; i++ {
|
for i := len(call.Args) - 1; i < 4; i++ {
|
||||||
@@ -111,7 +110,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
|||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
args = append(args, llvm.ConstInt(c.uintptrType, num, false))
|
args = append(args, num)
|
||||||
argTypes = append(argTypes, c.uintptrType)
|
argTypes = append(argTypes, c.uintptrType)
|
||||||
constraints += ",{x8}" // syscall number
|
constraints += ",{x8}" // syscall number
|
||||||
for i := len(call.Args) - 1; i < 8; i++ {
|
for i := len(call.Args) - 1; i < 8; i++ {
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||||
packedHeapAlloc = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "")
|
packedHeapAlloc = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "")
|
||||||
|
if c.needsStackObjects() {
|
||||||
|
c.trackPointer(packedHeapAlloc)
|
||||||
|
}
|
||||||
packedAlloc = c.builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
packedAlloc = c.builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
|
||||||
}
|
}
|
||||||
// Store all values in the alloca or heap pointer.
|
// Store all values in the alloca or heap pointer.
|
||||||
|
|||||||
@@ -176,6 +176,20 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
continue // special case: bitcast of alloc
|
continue // special case: bitcast of alloc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if _, ok := fr.getLocal(operand).(*MapValue); ok {
|
||||||
|
// Special case for runtime.trackPointer calls.
|
||||||
|
// Note: this might not be entirely sound in some rare cases
|
||||||
|
// where the map is stored in a dirty global.
|
||||||
|
uses := getUses(inst)
|
||||||
|
if len(uses) == 1 {
|
||||||
|
use := uses[0]
|
||||||
|
if !use.IsACallInst().IsNil() && !use.CalledValue().IsAFunction().IsNil() && use.CalledValue().Name() == "runtime.trackPointer" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It is not possible in Go to bitcast a map value to a pointer.
|
||||||
|
panic("unimplemented: bitcast of map")
|
||||||
|
}
|
||||||
value := fr.getLocal(operand).(*LocalValue)
|
value := fr.getLocal(operand).(*LocalValue)
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
|
||||||
|
|
||||||
@@ -368,6 +382,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
||||||
case callee.Name() == "llvm.dbg.value":
|
case callee.Name() == "llvm.dbg.value":
|
||||||
// do nothing
|
// do nothing
|
||||||
|
case callee.Name() == "runtime.trackPointer":
|
||||||
|
// do nothing
|
||||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||||
// This are all print instructions, which necessarily have side
|
// This are all print instructions, which necessarily have side
|
||||||
// effects but no results.
|
// effects but no results.
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
|||||||
return &sideEffectResult{severity: sideEffectLimited}
|
return &sideEffectResult{severity: sideEffectLimited}
|
||||||
case "runtime.interfaceImplements":
|
case "runtime.interfaceImplements":
|
||||||
return &sideEffectResult{severity: sideEffectNone}
|
return &sideEffectResult{severity: sideEffectNone}
|
||||||
|
case "runtime.trackPointer":
|
||||||
|
return &sideEffectResult{severity: sideEffectNone}
|
||||||
case "llvm.dbg.value":
|
case "llvm.dbg.value":
|
||||||
return &sideEffectResult{severity: sideEffectNone}
|
return &sideEffectResult{severity: sideEffectNone}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package ir
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
|
||||||
"go/types"
|
"go/types"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -23,10 +22,6 @@ type Program struct {
|
|||||||
mainPkg *ssa.Package
|
mainPkg *ssa.Package
|
||||||
Functions []*Function
|
Functions []*Function
|
||||||
functionMap map[*ssa.Function]*Function
|
functionMap map[*ssa.Function]*Function
|
||||||
Globals []*Global
|
|
||||||
globalMap map[*ssa.Global]*Global
|
|
||||||
comments map[string]*ast.CommentGroup
|
|
||||||
NamedTypes []*NamedType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function or method.
|
// Function or method.
|
||||||
@@ -41,28 +36,6 @@ type Function struct {
|
|||||||
inline InlineType // go:inline
|
inline InlineType // go:inline
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// Interface type that is at some point used in a type assert (to check whether
|
||||||
// it implements another interface).
|
// it implements another interface).
|
||||||
type Interface struct {
|
type Interface struct {
|
||||||
@@ -83,39 +56,14 @@ const (
|
|||||||
// //go:inline). The compiler will be more likely to inline this function,
|
// //go:inline). The compiler will be more likely to inline this function,
|
||||||
// but it is not a guarantee.
|
// but it is not a guarantee.
|
||||||
InlineHint
|
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 initialize a new *Program from a *ssa.Program.
|
||||||
func NewProgram(lprogram *loader.Program, mainPath string) *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 := lprogram.LoadSSA()
|
||||||
program.Build()
|
program.Build()
|
||||||
|
|
||||||
@@ -187,8 +135,6 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
|||||||
LoaderProgram: lprogram,
|
LoaderProgram: lprogram,
|
||||||
mainPkg: mainPkg,
|
mainPkg: mainPkg,
|
||||||
functionMap: make(map[*ssa.Function]*Function),
|
functionMap: make(map[*ssa.Function]*Function),
|
||||||
globalMap: make(map[*ssa.Global]*Global),
|
|
||||||
comments: comments,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pkg := range packageList {
|
for _, pkg := range packageList {
|
||||||
@@ -213,8 +159,6 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
|||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
p.addFunction(member)
|
p.addFunction(member)
|
||||||
case *ssa.Type:
|
case *ssa.Type:
|
||||||
t := &NamedType{Type: member}
|
|
||||||
p.NamedTypes = append(p.NamedTypes, t)
|
|
||||||
methods := getAllMethods(pkg.Prog, member.Type())
|
methods := getAllMethods(pkg.Prog, member.Type())
|
||||||
if !types.IsInterface(member.Type()) {
|
if !types.IsInterface(member.Type()) {
|
||||||
// named type
|
// named type
|
||||||
@@ -223,13 +167,7 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
g := &Global{program: p, Global: member}
|
// Ignore. Globals are not handled here.
|
||||||
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:
|
case *ssa.NamedConst:
|
||||||
// Ignore: these are already resolved.
|
// Ignore: these are already resolved.
|
||||||
default:
|
default:
|
||||||
@@ -263,10 +201,6 @@ func (p *Program) GetFunction(ssaFn *ssa.Function) *Function {
|
|||||||
return p.functionMap[ssaFn]
|
return p.functionMap[ssaFn]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global {
|
|
||||||
return p.globalMap[ssaGlobal]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Program) MainPkg() *ssa.Package {
|
func (p *Program) MainPkg() *ssa.Package {
|
||||||
return p.mainPkg
|
return p.mainPkg
|
||||||
}
|
}
|
||||||
@@ -297,6 +231,8 @@ func (f *Function) parsePragmas() {
|
|||||||
f.exported = true
|
f.exported = true
|
||||||
case "//go:inline":
|
case "//go:inline":
|
||||||
f.inline = InlineHint
|
f.inline = InlineHint
|
||||||
|
case "//go:noinline":
|
||||||
|
f.inline = InlineNone
|
||||||
case "//go:interrupt":
|
case "//go:interrupt":
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
continue
|
continue
|
||||||
@@ -389,72 +325,6 @@ func (f *Function) CName() string {
|
|||||||
return ""
|
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.
|
// Get all methods of a type.
|
||||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||||
ms := prog.MethodSets.MethodSet(typ)
|
ms := prog.MethodSets.MethodSet(typ)
|
||||||
|
|||||||
+1
-1
Submodule lib/cmsis-svd updated: b6f0a65ac3...5910f3d115
@@ -63,6 +63,7 @@ func Link(linker string, flags ...string) error {
|
|||||||
cmd := exec.Command(linker, flags...)
|
cmd := exec.Command(linker, flags...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Dir = sourceDir()
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,5 +20,6 @@ func Link(linker string, flags ...string) error {
|
|||||||
cmd := exec.Command(linker, flags...)
|
cmd := exec.Command(linker, flags...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Dir = sourceDir()
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|||||||
+144
-29
@@ -1,6 +1,7 @@
|
|||||||
package loader
|
package loader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
"go/build"
|
||||||
@@ -10,22 +11,26 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/cgo"
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Program holds all packages and some metadata about the program as a whole.
|
// Program holds all packages and some metadata about the program as a whole.
|
||||||
type Program struct {
|
type Program struct {
|
||||||
Build *build.Context
|
mainPkg string
|
||||||
OverlayBuild *build.Context
|
Build *build.Context
|
||||||
ShouldOverlay func(path string) bool
|
OverlayBuild *build.Context
|
||||||
Packages map[string]*Package
|
OverlayPath func(path string) string
|
||||||
sorted []*Package
|
Packages map[string]*Package
|
||||||
fset *token.FileSet
|
sorted []*Package
|
||||||
TypeChecker types.Config
|
fset *token.FileSet
|
||||||
Dir string // current working directory (for error reporting)
|
TypeChecker types.Config
|
||||||
TINYGOROOT string // root of the TinyGo installation or root of the source code
|
Dir string // current working directory (for error reporting)
|
||||||
CFlags []string
|
TINYGOROOT string // root of the TinyGo installation or root of the source code
|
||||||
|
CFlags []string
|
||||||
|
ClangHeaders string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package holds a loaded package, its imports, and its parsed files.
|
// Package holds a loaded package, its imports, and its parsed files.
|
||||||
@@ -48,8 +53,9 @@ func (p *Program) Import(path, srcDir string) (*Package, error) {
|
|||||||
|
|
||||||
// Load this package.
|
// Load this package.
|
||||||
ctx := p.Build
|
ctx := p.Build
|
||||||
if p.ShouldOverlay(path) {
|
if newPath := p.OverlayPath(path); newPath != "" {
|
||||||
ctx = p.OverlayBuild
|
ctx = p.OverlayBuild
|
||||||
|
path = newPath
|
||||||
}
|
}
|
||||||
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,6 +68,11 @@ func (p *Program) Import(path, srcDir string) (*Package, error) {
|
|||||||
p.sorted = nil // invalidate the sorted order of packages
|
p.sorted = nil // invalidate the sorted order of packages
|
||||||
pkg := p.newPackage(buildPkg)
|
pkg := p.newPackage(buildPkg)
|
||||||
p.Packages[buildPkg.ImportPath] = pkg
|
p.Packages[buildPkg.ImportPath] = pkg
|
||||||
|
|
||||||
|
if p.mainPkg == "" {
|
||||||
|
p.mainPkg = buildPkg.ImportPath
|
||||||
|
}
|
||||||
|
|
||||||
return pkg, nil
|
return pkg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +102,11 @@ func (p *Program) ImportFile(path string) (*Package, error) {
|
|||||||
p.sorted = nil // invalidate the sorted order of packages
|
p.sorted = nil // invalidate the sorted order of packages
|
||||||
pkg := p.newPackage(buildPkg)
|
pkg := p.newPackage(buildPkg)
|
||||||
p.Packages[buildPkg.ImportPath] = pkg
|
p.Packages[buildPkg.ImportPath] = pkg
|
||||||
|
|
||||||
|
if p.mainPkg == "" {
|
||||||
|
p.mainPkg = buildPkg.ImportPath
|
||||||
|
}
|
||||||
|
|
||||||
return pkg, nil
|
return pkg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,10 +185,12 @@ func (p *Program) sort() {
|
|||||||
// The returned error may be an Errors error, which contains a list of errors.
|
// The returned error may be an Errors error, which contains a list of errors.
|
||||||
//
|
//
|
||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Program) Parse() error {
|
func (p *Program) Parse(compileTestBinary bool) error {
|
||||||
|
includeTests := compileTestBinary
|
||||||
|
|
||||||
// Load all imports
|
// Load all imports
|
||||||
for _, pkg := range p.Sorted() {
|
for _, pkg := range p.Sorted() {
|
||||||
err := pkg.importRecursively()
|
err := pkg.importRecursively(includeTests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, ok := err.(*ImportCycleError); ok {
|
if err, ok := err.(*ImportCycleError); ok {
|
||||||
if pkg.ImportPath != err.Packages[0] {
|
if pkg.ImportPath != err.Packages[0] {
|
||||||
@@ -185,7 +203,14 @@ func (p *Program) Parse() error {
|
|||||||
|
|
||||||
// Parse all packages.
|
// Parse all packages.
|
||||||
for _, pkg := range p.Sorted() {
|
for _, pkg := range p.Sorted() {
|
||||||
err := pkg.Parse()
|
err := pkg.Parse(includeTests)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if compileTestBinary {
|
||||||
|
err := p.SwapTestMain()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -202,6 +227,83 @@ func (p *Program) Parse() error {
|
|||||||
return nil
|
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.
|
// parseFile is a wrapper around parser.ParseFile.
|
||||||
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||||
if p.fset == nil {
|
if p.fset == nil {
|
||||||
@@ -226,7 +328,7 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
|||||||
// Parse parses and typechecks this package.
|
// Parse parses and typechecks this package.
|
||||||
//
|
//
|
||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Package) Parse() error {
|
func (p *Package) Parse(includeTests bool) error {
|
||||||
if len(p.Files) != 0 {
|
if len(p.Files) != 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -240,7 +342,7 @@ func (p *Package) Parse() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
files, err := p.parseFiles()
|
files, err := p.parseFiles(includeTests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -279,11 +381,21 @@ func (p *Package) Check() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseFiles parses the loaded list of files and returns this list.
|
// parseFiles parses the loaded list of files and returns this list.
|
||||||
func (p *Package) parseFiles() ([]*ast.File, error) {
|
func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
|
||||||
// TODO: do this concurrently.
|
// TODO: do this concurrently.
|
||||||
var files []*ast.File
|
var files []*ast.File
|
||||||
var fileErrs []error
|
var fileErrs []error
|
||||||
for _, file := range p.GoFiles {
|
|
||||||
|
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 {
|
||||||
f, err := p.parseFile(filepath.Join(p.Package.Dir, file), parser.ParseComments)
|
f, err := p.parseFile(filepath.Join(p.Package.Dir, file), parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fileErrs = append(fileErrs, err)
|
fileErrs = append(fileErrs, err)
|
||||||
@@ -305,15 +417,11 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
if len(p.CgoFiles) != 0 {
|
if len(p.CgoFiles) != 0 {
|
||||||
clangIncludes := ""
|
cflags := append(p.CFlags, "-I"+p.Package.Dir)
|
||||||
if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) {
|
if p.ClangHeaders != "" {
|
||||||
// Running from the source directory.
|
cflags = append(cflags, "-I"+p.ClangHeaders)
|
||||||
clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
|
||||||
} else {
|
|
||||||
// Running from the installation directory.
|
|
||||||
clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include")
|
|
||||||
}
|
}
|
||||||
generated, errs := cgo.Process(files, p.Program.Dir, p.fset, append(p.CFlags, "-I"+p.Package.Dir, "-I"+clangIncludes))
|
generated, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
}
|
}
|
||||||
@@ -322,6 +430,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
if len(fileErrs) != 0 {
|
if len(fileErrs) != 0 {
|
||||||
return nil, Errors{p, fileErrs}
|
return nil, Errors{p, fileErrs}
|
||||||
}
|
}
|
||||||
|
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,9 +451,15 @@ func (p *Package) Import(to string) (*types.Package, error) {
|
|||||||
// importRecursively() on the imported packages as well.
|
// importRecursively() on the imported packages as well.
|
||||||
//
|
//
|
||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Package) importRecursively() error {
|
func (p *Package) importRecursively(includeTests bool) error {
|
||||||
p.Importing = true
|
p.Importing = true
|
||||||
for _, to := range p.Package.Imports {
|
|
||||||
|
imports := p.Package.Imports
|
||||||
|
if includeTests {
|
||||||
|
imports = append(imports, p.Package.TestImports...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, to := range imports {
|
||||||
if to == "C" {
|
if to == "C" {
|
||||||
// Do CGo processing in a later stage.
|
// Do CGo processing in a later stage.
|
||||||
continue
|
continue
|
||||||
@@ -362,7 +477,7 @@ func (p *Package) importRecursively() error {
|
|||||||
if importedPkg.Importing {
|
if importedPkg.Importing {
|
||||||
return &ImportCycleError{[]string{p.ImportPath, importedPkg.ImportPath}, p.ImportPos[to]}
|
return &ImportCycleError{[]string{p.ImportPath, importedPkg.ImportPath}, p.ImportPos[to]}
|
||||||
}
|
}
|
||||||
err = importedPkg.importRecursively()
|
err = importedPkg.importRecursively(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, ok := err.(*ImportCycleError); ok {
|
if err, ok := err.(*ImportCycleError); ok {
|
||||||
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
||||||
|
|||||||
@@ -53,7 +53,10 @@ type BuildConfig struct {
|
|||||||
printSizes string
|
printSizes string
|
||||||
cFlags []string
|
cFlags []string
|
||||||
ldFlags []string
|
ldFlags []string
|
||||||
|
tags string
|
||||||
wasmAbi string
|
wasmAbi string
|
||||||
|
heapSize int64
|
||||||
|
testConfig compiler.TestConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for Compiler object.
|
// Helper function for Compiler object.
|
||||||
@@ -81,16 +84,19 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
return errors.New("cannot locate $GOROOT, please set it manually")
|
return errors.New("cannot locate $GOROOT, please set it manually")
|
||||||
}
|
}
|
||||||
tags := spec.BuildTags
|
tags := spec.BuildTags
|
||||||
major, minor := getGorootVersion(goroot)
|
major, minor, err := getGorootVersion(goroot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||||
|
}
|
||||||
if major != 1 {
|
if major != 1 {
|
||||||
if major == 0 {
|
|
||||||
return errors.New("could not read version from GOROOT: " + goroot)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("expected major version 1, got go%d.%d", major, minor)
|
return fmt.Errorf("expected major version 1, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
for i := 1; i <= minor; i++ {
|
for i := 1; i <= minor; i++ {
|
||||||
tags = append(tags, fmt.Sprintf("go1.%d", 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{
|
compilerConfig := compiler.Config{
|
||||||
Triple: spec.Triple,
|
Triple: spec.Triple,
|
||||||
CPU: spec.CPU,
|
CPU: spec.CPU,
|
||||||
@@ -101,12 +107,14 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
PanicStrategy: config.panicStrategy,
|
PanicStrategy: config.panicStrategy,
|
||||||
CFlags: cflags,
|
CFlags: cflags,
|
||||||
LDFlags: ldflags,
|
LDFlags: ldflags,
|
||||||
|
ClangHeaders: getClangHeaderPath(root),
|
||||||
Debug: config.debug,
|
Debug: config.debug,
|
||||||
DumpSSA: config.dumpSSA,
|
DumpSSA: config.dumpSSA,
|
||||||
TINYGOROOT: root,
|
TINYGOROOT: root,
|
||||||
GOROOT: goroot,
|
GOROOT: goroot,
|
||||||
GOPATH: getGopath(),
|
GOPATH: getGopath(),
|
||||||
BuildTags: tags,
|
BuildTags: tags,
|
||||||
|
TestConfig: config.testConfig,
|
||||||
}
|
}
|
||||||
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -233,10 +241,16 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
executable := filepath.Join(dir, "main")
|
executable := filepath.Join(dir, "main")
|
||||||
tmppath := executable // final file
|
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" {
|
if spec.RTLib == "compiler-rt" {
|
||||||
ldflags = append(ldflags, librt)
|
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.
|
// Compile extra files.
|
||||||
for i, path := range spec.ExtraFiles {
|
for i, path := range spec.ExtraFiles {
|
||||||
@@ -348,6 +362,33 @@ 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 {
|
func Flash(pkgName, target, port string, config *BuildConfig) error {
|
||||||
spec, err := LoadTarget(target)
|
spec, err := LoadTarget(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -506,6 +547,30 @@ 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() {
|
func usage() {
|
||||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||||
fmt.Fprintln(os.Stderr, "version:", version)
|
fmt.Fprintln(os.Stderr, "version:", version)
|
||||||
@@ -513,6 +578,7 @@ func usage() {
|
|||||||
fmt.Fprintln(os.Stderr, "\ncommands:")
|
fmt.Fprintln(os.Stderr, "\ncommands:")
|
||||||
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
|
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
|
||||||
fmt.Fprintln(os.Stderr, " run: compile and run immediately")
|
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, " flash: compile and flash to the device")
|
||||||
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
|
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
|
||||||
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+cacheDir()+")")
|
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+cacheDir()+")")
|
||||||
@@ -523,23 +589,24 @@ func usage() {
|
|||||||
|
|
||||||
func handleCompilerError(err error) {
|
func handleCompilerError(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errUnsupported, ok := err.(*interp.Unsupported); ok {
|
switch err := err.(type) {
|
||||||
|
case *interp.Unsupported:
|
||||||
// hit an unknown/unsupported instruction
|
// hit an unknown/unsupported instruction
|
||||||
fmt.Fprintln(os.Stderr, "unsupported instruction during init evaluation:")
|
fmt.Fprintln(os.Stderr, "unsupported instruction during init evaluation:")
|
||||||
errUnsupported.Inst.Dump()
|
err.Inst.Dump()
|
||||||
fmt.Fprintln(os.Stderr)
|
fmt.Fprintln(os.Stderr)
|
||||||
} else if errCompiler, ok := err.(types.Error); ok {
|
case types.Error:
|
||||||
fmt.Fprintln(os.Stderr, errCompiler)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
} else if errLoader, ok := err.(loader.Errors); ok {
|
case loader.Errors:
|
||||||
fmt.Fprintln(os.Stderr, "#", errLoader.Pkg.ImportPath)
|
fmt.Fprintln(os.Stderr, "#", err.Pkg.ImportPath)
|
||||||
for _, err := range errLoader.Errs {
|
for _, err := range err.Errs {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
}
|
}
|
||||||
} else if errMulti, ok := err.(*multiError); ok {
|
case *multiError:
|
||||||
for _, err := range errMulti.Errs {
|
for _, err := range err.Errs {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
fmt.Fprintln(os.Stderr, "error:", err)
|
fmt.Fprintln(os.Stderr, "error:", err)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -549,11 +616,12 @@ func handleCompilerError(err error) {
|
|||||||
func main() {
|
func main() {
|
||||||
outpath := flag.String("o", "", "output filename")
|
outpath := flag.String("o", "", "output filename")
|
||||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||||
gc := flag.String("gc", "", "garbage collector to use (none, dumb, marksweep)")
|
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
|
||||||
panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)")
|
panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)")
|
||||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||||
target := flag.String("target", "", "LLVM target")
|
tags := flag.String("tags", "", "a space-separated list of extra build tags")
|
||||||
|
target := flag.String("target", "", "LLVM target | .json file with TargetSpec")
|
||||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||||
@@ -561,6 +629,7 @@ func main() {
|
|||||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||||
wasmAbi := flag.String("wasm-abi", "js", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
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 {
|
if len(os.Args) < 2 {
|
||||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||||
@@ -578,6 +647,7 @@ func main() {
|
|||||||
dumpSSA: *dumpSSA,
|
dumpSSA: *dumpSSA,
|
||||||
debug: !*nodebug,
|
debug: !*nodebug,
|
||||||
printSizes: *printSize,
|
printSizes: *printSize,
|
||||||
|
tags: *tags,
|
||||||
wasmAbi: *wasmAbi,
|
wasmAbi: *wasmAbi,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,6 +665,13 @@ func main() {
|
|||||||
os.Exit(1)
|
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)
|
os.Setenv("CC", "clang -target="+*target)
|
||||||
|
|
||||||
switch command {
|
switch command {
|
||||||
@@ -604,8 +681,11 @@ func main() {
|
|||||||
usage()
|
usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if flag.NArg() != 1 {
|
pkgName := "."
|
||||||
fmt.Fprintln(os.Stderr, "No package specified.")
|
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")
|
||||||
usage()
|
usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@@ -613,7 +693,7 @@ func main() {
|
|||||||
if target == "" && filepath.Ext(*outpath) == ".wasm" {
|
if target == "" && filepath.Ext(*outpath) == ".wasm" {
|
||||||
target = "wasm"
|
target = "wasm"
|
||||||
}
|
}
|
||||||
err := Build(flag.Arg(0), *outpath, target, config)
|
err := Build(pkgName, *outpath, target, config)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
case "build-builtins":
|
case "build-builtins":
|
||||||
// Note: this command is only meant to be used while making a release!
|
// Note: this command is only meant to be used while making a release!
|
||||||
@@ -655,6 +735,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
err := Run(flag.Arg(0), *target, config)
|
err := Run(flag.Arg(0), *target, config)
|
||||||
handleCompilerError(err)
|
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":
|
case "clean":
|
||||||
// remove cache directory
|
// remove cache directory
|
||||||
dir := cacheDir()
|
dir := cacheDir()
|
||||||
|
|||||||
+9
-11
@@ -30,6 +30,7 @@
|
|||||||
package arm
|
package arm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,9 +70,6 @@ func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
|
|||||||
// Run the following system call (SVCall) with 4 arguments.
|
// Run the following system call (SVCall) with 4 arguments.
|
||||||
func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
|
||||||
|
|
||||||
//go:volatile
|
|
||||||
type RegValue uint32
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SCS_BASE = 0xE000E000
|
SCS_BASE = 0xE000E000
|
||||||
NVIC_BASE = SCS_BASE + 0x0100
|
NVIC_BASE = SCS_BASE + 0x0100
|
||||||
@@ -82,24 +80,24 @@ const (
|
|||||||
// Source:
|
// Source:
|
||||||
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CIHIGCIF.html
|
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CIHIGCIF.html
|
||||||
type NVIC_Type struct {
|
type NVIC_Type struct {
|
||||||
ISER [8]RegValue // Interrupt Set-enable Registers
|
ISER [8]volatile.Register32 // Interrupt Set-enable Registers
|
||||||
_ [24]uint32
|
_ [24]uint32
|
||||||
ICER [8]RegValue // Interrupt Clear-enable Registers
|
ICER [8]volatile.Register32 // Interrupt Clear-enable Registers
|
||||||
_ [24]uint32
|
_ [24]uint32
|
||||||
ISPR [8]RegValue // Interrupt Set-pending Registers
|
ISPR [8]volatile.Register32 // Interrupt Set-pending Registers
|
||||||
_ [24]uint32
|
_ [24]uint32
|
||||||
ICPR [8]RegValue // Interrupt Clear-pending Registers
|
ICPR [8]volatile.Register32 // Interrupt Clear-pending Registers
|
||||||
_ [24]uint32
|
_ [24]uint32
|
||||||
IABR [8]RegValue // Interrupt Active Bit Registers
|
IABR [8]volatile.Register32 // Interrupt Active Bit Registers
|
||||||
_ [56]uint32
|
_ [56]uint32
|
||||||
IPR [60]RegValue // Interrupt Priority Registers
|
IPR [60]volatile.Register32 // Interrupt Priority Registers
|
||||||
}
|
}
|
||||||
|
|
||||||
var NVIC = (*NVIC_Type)(unsafe.Pointer(uintptr(NVIC_BASE)))
|
var NVIC = (*NVIC_Type)(unsafe.Pointer(uintptr(NVIC_BASE)))
|
||||||
|
|
||||||
// Enable the given interrupt number.
|
// Enable the given interrupt number.
|
||||||
func EnableIRQ(irq uint32) {
|
func EnableIRQ(irq uint32) {
|
||||||
NVIC.ISER[irq>>5] = 1 << (irq & 0x1F)
|
NVIC.ISER[irq>>5].Set(1 << (irq & 0x1F))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the priority of the given interrupt number.
|
// Set the priority of the given interrupt number.
|
||||||
@@ -116,7 +114,7 @@ func SetPriority(irq uint32, priority uint32) {
|
|||||||
regpos := irq % 4
|
regpos := irq % 4
|
||||||
mask := uint32(0xff) << (regpos * 8) // bits to clear
|
mask := uint32(0xff) << (regpos * 8) // bits to clear
|
||||||
priority = priority << (regpos * 8) // bits to set
|
priority = priority << (regpos * 8) // bits to set
|
||||||
NVIC.IPR[regnum] = RegValue((uint32(NVIC.IPR[regnum]) &^ mask) | priority)
|
NVIC.IPR[regnum].Set((uint32(NVIC.IPR[regnum].Get()) &^ mask) | priority)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableInterrupts disables all interrupts, and returns the old state.
|
// DisableInterrupts disables all interrupts, and returns the old state.
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.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
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build avr,arduino
|
// +build arduino
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
// +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 stm32,bluepill
|
// +build bluepill
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,20 @@ const (
|
|||||||
UART_RX_PIN = PB09 // PORTB
|
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
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = PB02 // I2C0 external
|
SDA_PIN = PB02 // I2C0 external
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build attiny85,digispark
|
// +build digispark
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// +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
|
||||||
|
)
|
||||||
@@ -48,6 +48,20 @@ const (
|
|||||||
UART_RX_PIN = D11
|
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
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// +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,6 +48,20 @@ const (
|
|||||||
UART_RX_PIN = D11
|
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
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build nrf51,microbit
|
// +build microbit
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build nrf51,pca10031
|
// +build pca10031
|
||||||
|
|
||||||
// pca10031 is a nrf51 based dongle, intended for use in wireless applications.
|
// pca10031 is a nrf51 based dongle, intended for use in wireless applications.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build nrf,pca10040
|
// +build pca10040
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,24 @@ package machine
|
|||||||
|
|
||||||
const HasLowFrequencyCrystal = true
|
const HasLowFrequencyCrystal = true
|
||||||
|
|
||||||
// LEDs on the reel board
|
// Pins on the reel board
|
||||||
const (
|
const (
|
||||||
LED Pin = LED1
|
LED Pin = LED1
|
||||||
LED1 Pin = LED_YELLOW
|
LED1 Pin = LED_YELLOW
|
||||||
LED2 Pin = LED_RED
|
LED2 Pin = LED_RED
|
||||||
LED3 Pin = LED_GREEN
|
LED3 Pin = LED_GREEN
|
||||||
LED4 Pin = LED_BLUE
|
LED4 Pin = LED_BLUE
|
||||||
LED_RED Pin = 11
|
LED_RED Pin = 11
|
||||||
LED_GREEN Pin = 12
|
LED_GREEN Pin = 12
|
||||||
LED_BLUE Pin = 41
|
LED_BLUE Pin = 41
|
||||||
LED_YELLOW Pin = 13
|
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
|
||||||
)
|
)
|
||||||
|
|
||||||
// User "a" button on the reel board
|
// User "a" button on the reel board
|
||||||
@@ -40,3 +47,15 @@ const (
|
|||||||
SPI0_MOSI_PIN Pin = 45
|
SPI0_MOSI_PIN Pin = 45
|
||||||
SPI0_MISO_PIN Pin = 46
|
SPI0_MISO_PIN 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// +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 stm32,stm32f4disco
|
// +build stm32f4disco
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,20 @@ const (
|
|||||||
UART_RX_PIN = D3
|
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
|
// SPI pins
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = D3
|
SPI0_SCK_PIN = D3
|
||||||
|
|||||||
+12
-11
@@ -1,9 +1,10 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
const bufferSize = 128
|
import (
|
||||||
|
"runtime/volatile"
|
||||||
|
)
|
||||||
|
|
||||||
//go:volatile
|
const bufferSize = 128
|
||||||
type volatileByte byte
|
|
||||||
|
|
||||||
// RingBuffer is ring buffer implementation inspired by post at
|
// RingBuffer is ring buffer implementation inspired by post at
|
||||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||||
@@ -12,9 +13,9 @@ type volatileByte byte
|
|||||||
// members of a struct are not compiled correctly by TinyGo.
|
// members of a struct are not compiled correctly by TinyGo.
|
||||||
// See https://github.com/tinygo-org/tinygo/issues/151 for details.
|
// See https://github.com/tinygo-org/tinygo/issues/151 for details.
|
||||||
type RingBuffer struct {
|
type RingBuffer struct {
|
||||||
rxbuffer [bufferSize]volatileByte
|
rxbuffer [bufferSize]volatile.Register8
|
||||||
head volatileByte
|
head volatile.Register8
|
||||||
tail volatileByte
|
tail volatile.Register8
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRingBuffer returns a new ring buffer.
|
// NewRingBuffer returns a new ring buffer.
|
||||||
@@ -24,15 +25,15 @@ func NewRingBuffer() *RingBuffer {
|
|||||||
|
|
||||||
// Used returns how many bytes in buffer have been used.
|
// Used returns how many bytes in buffer have been used.
|
||||||
func (rb *RingBuffer) Used() uint8 {
|
func (rb *RingBuffer) Used() uint8 {
|
||||||
return uint8(rb.head - rb.tail)
|
return uint8(rb.head.Get() - rb.tail.Get())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put stores a byte in the buffer. If the buffer is already
|
// Put stores a byte in the buffer. If the buffer is already
|
||||||
// full, the method will return false.
|
// full, the method will return false.
|
||||||
func (rb *RingBuffer) Put(val byte) bool {
|
func (rb *RingBuffer) Put(val byte) bool {
|
||||||
if rb.Used() != bufferSize {
|
if rb.Used() != bufferSize {
|
||||||
rb.head++
|
rb.head.Set(rb.head.Get() + 1)
|
||||||
rb.rxbuffer[rb.head%bufferSize] = volatileByte(val)
|
rb.rxbuffer[rb.head.Get()%bufferSize].Set(val)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -42,8 +43,8 @@ func (rb *RingBuffer) Put(val byte) bool {
|
|||||||
// the method will return a false as the second value.
|
// the method will return a false as the second value.
|
||||||
func (rb *RingBuffer) Get() (byte, bool) {
|
func (rb *RingBuffer) Get() (byte, bool) {
|
||||||
if rb.Used() != 0 {
|
if rb.Used() != 0 {
|
||||||
rb.tail++
|
rb.tail.Set(rb.tail.Get() + 1)
|
||||||
return byte(rb.rxbuffer[rb.tail%bufferSize]), true
|
return rb.rxbuffer[rb.tail.Get()%bufferSize].Get(), true
|
||||||
}
|
}
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build avr nrf sam stm32f103xx
|
// +build !stm32f4disco,!hifive1b
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configure sets the pin to input or output.
|
// Configure sets the pin to input or output.
|
||||||
@@ -34,7 +35,7 @@ func (p Pin) Get() bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Pin) getPortMask() (*avr.Register8, uint8) {
|
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||||
if p < 8 {
|
if p < 8 {
|
||||||
return avr.PORTD, 1 << uint8(p)
|
return avr.PORTD, 1 << uint8(p)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -250,14 +250,13 @@ func waitADCSync() {
|
|||||||
type UART struct {
|
type UART struct {
|
||||||
Buffer *RingBuffer
|
Buffer *RingBuffer
|
||||||
Bus *sam.SERCOM_USART_Type
|
Bus *sam.SERCOM_USART_Type
|
||||||
|
Mode PinMode
|
||||||
|
IRQVal uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// UART0 is actually a USB CDC interface.
|
// UART0 is actually a USB CDC interface.
|
||||||
UART0 = USBCDC{Buffer: NewRingBuffer()}
|
UART0 = USBCDC{Buffer: NewRingBuffer()}
|
||||||
|
|
||||||
// The first hardware serial port on the SAMD21. Uses the SERCOM0 interface.
|
|
||||||
UART1 = UART{Bus: sam.SERCOM1_USART, Buffer: NewRingBuffer()}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -300,6 +299,8 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
txpad = sercomTXPad2
|
txpad = sercomTXPad2
|
||||||
case PA16:
|
case PA16:
|
||||||
txpad = sercomTXPad0
|
txpad = sercomTXPad0
|
||||||
|
case PA22:
|
||||||
|
txpad = sercomTXPad0
|
||||||
default:
|
default:
|
||||||
panic("Invalid TX pin for UART")
|
panic("Invalid TX pin for UART")
|
||||||
}
|
}
|
||||||
@@ -315,13 +316,15 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
rxpad = sercomRXPad3
|
rxpad = sercomRXPad3
|
||||||
case PA17:
|
case PA17:
|
||||||
rxpad = sercomRXPad1
|
rxpad = sercomRXPad1
|
||||||
|
case PA23:
|
||||||
|
rxpad = sercomRXPad1
|
||||||
default:
|
default:
|
||||||
panic("Invalid RX pin for UART")
|
panic("Invalid RX pin for UART")
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure pins
|
// configure pins
|
||||||
config.TX.Configure(PinConfig{Mode: PinSERCOM})
|
config.TX.Configure(PinConfig{Mode: uart.Mode})
|
||||||
config.RX.Configure(PinConfig{Mode: PinSERCOM})
|
config.RX.Configure(PinConfig{Mode: uart.Mode})
|
||||||
|
|
||||||
// reset SERCOM0
|
// reset SERCOM0
|
||||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_CTRLA_SWRST)
|
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_CTRLA_SWRST)
|
||||||
@@ -372,13 +375,7 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
if config.TX == PA10 {
|
arm.EnableIRQ(uart.IRQVal)
|
||||||
// UART0
|
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM0)
|
|
||||||
} else {
|
|
||||||
// UART1 which is the normal default, since UART0 is used for USBCDC.
|
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the UART.
|
// SetBaudRate sets the communication speed for the UART.
|
||||||
@@ -404,8 +401,8 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export SERCOM1_IRQHandler
|
// defaultUART1Handler handles the UART1 IRQ.
|
||||||
func handleUART1() {
|
func defaultUART1Handler() {
|
||||||
// should reset IRQ
|
// should reset IRQ
|
||||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
||||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||||
@@ -1182,23 +1179,35 @@ func (usbcdc USBCDC) WriteByte(c byte) error {
|
|||||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||||
|
|
||||||
// set count of bytes to be sent
|
// set count of bytes to be sent
|
||||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((1&usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)<<usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos |
|
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((1 & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos))
|
|
||||||
|
|
||||||
// ack transfer complete flag
|
// clear transfer complete flag
|
||||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||||
|
|
||||||
// send data by setting bank ready
|
// send data by setting bank ready
|
||||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||||
|
|
||||||
// wait for transfer to complete
|
// wait for transfer to complete
|
||||||
|
timeout := 3000
|
||||||
for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
||||||
|
timeout--
|
||||||
|
if timeout == 0 {
|
||||||
|
return errors.New("USBCDC write byte timeout")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (usbcdc USBCDC) DTR() bool {
|
||||||
|
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usbcdc USBCDC) RTS() bool {
|
||||||
|
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// these are SAMD21 specific.
|
// these are SAMD21 specific.
|
||||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||||
@@ -1346,6 +1355,7 @@ func handleUSB() {
|
|||||||
|
|
||||||
// Clear the Bank 0 ready flag on Control OUT
|
// Clear the Bank 0 ready flag on Control OUT
|
||||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||||
|
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
|
|
||||||
ok := false
|
ok := false
|
||||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||||
@@ -1375,19 +1385,24 @@ func handleUSB() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now the actual transfer handlers
|
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||||
eptInts := sam.USB_DEVICE.EPINTSMRY.Get() & 0xFE // Remove endpoint number 0 (setup)
|
|
||||||
var i uint32
|
var i uint32
|
||||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||||
// Check if endpoint has a pending interrupt
|
// Check if endpoint has a pending interrupt
|
||||||
if eptInts&(1<<i) > 0 {
|
epFlags := getEPINTFLAG(i)
|
||||||
// yes, so handle flags
|
if epFlags > 0 {
|
||||||
epFlags := getEPINTFLAG(i)
|
switch i {
|
||||||
setEPINTFLAG(i, epFlags)
|
case usb_CDC_ENDPOINT_OUT:
|
||||||
|
if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 {
|
||||||
|
handleEndpoint(i)
|
||||||
|
}
|
||||||
|
setEPINTFLAG(i, epFlags)
|
||||||
|
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
||||||
|
// set bank ready
|
||||||
|
setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||||
|
|
||||||
// Endpoint Transfer Complete Interrupt
|
// ack transfer complete
|
||||||
if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 {
|
setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||||
handleEndpoint(i)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1403,7 +1418,7 @@ func initEndpoint(ep, config uint32) {
|
|||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||||
|
|
||||||
// set endpoint type
|
// set endpoint type
|
||||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_INTERRUPT+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||||
|
|
||||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||||
// set packet size
|
// set packet size
|
||||||
@@ -1413,11 +1428,14 @@ func initEndpoint(ep, config uint32) {
|
|||||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||||
|
|
||||||
// set endpoint type
|
// set endpoint type
|
||||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_BULK+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||||
|
|
||||||
// ack the current transfer
|
// receive interrupts when current transfer complete
|
||||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||||
|
|
||||||
|
// set byte count to zero, we have not received anything yet
|
||||||
|
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
|
|
||||||
// ready for next transfer
|
// ready for next transfer
|
||||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||||
|
|
||||||
@@ -1432,7 +1450,7 @@ func initEndpoint(ep, config uint32) {
|
|||||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||||
|
|
||||||
// set endpoint type
|
// set endpoint type
|
||||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_BULK+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||||
|
|
||||||
// NAK on endpoint IN, the bank is not yet filled in.
|
// NAK on endpoint IN, the bank is not yet filled in.
|
||||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||||
@@ -1515,7 +1533,12 @@ func handleStandardSetup(setup usbSetup) bool {
|
|||||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||||
|
|
||||||
// wait for transfer to complete
|
// wait for transfer to complete
|
||||||
|
timeout := 3000
|
||||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
||||||
|
timeout--
|
||||||
|
if timeout == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// last, set the device address to that requested by host
|
// last, set the device address to that requested by host
|
||||||
@@ -1659,11 +1682,21 @@ func armRecvCtrlOUT(ep uint32) uint32 {
|
|||||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||||
|
|
||||||
// Wait until OUT transfer is ready.
|
// Wait until OUT transfer is ready.
|
||||||
|
timeout := 3000
|
||||||
for (getEPSTATUS(ep) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
for (getEPSTATUS(ep) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||||
|
timeout--
|
||||||
|
if timeout == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until OUT transfer is completed.
|
// Wait until OUT transfer is completed.
|
||||||
|
timeout = 3000
|
||||||
for (getEPINTFLAG(ep) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
for (getEPINTFLAG(ep) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||||
|
timeout--
|
||||||
|
if timeout == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// return number of bytes received
|
// return number of bytes received
|
||||||
@@ -1800,9 +1833,14 @@ func handleEndpoint(ep uint32) {
|
|||||||
UART0.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
|
UART0.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set byte count to zero
|
||||||
|
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||||
|
|
||||||
|
// set multi packet size to 64
|
||||||
|
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||||
|
|
||||||
// set ready for next data
|
// set ready for next data
|
||||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendZlp(ep uint32) {
|
func sendZlp(ep uint32) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configure sets the pin to input or output.
|
// Configure sets the pin to input or output.
|
||||||
@@ -15,7 +16,7 @@ func (p Pin) Configure(config PinConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Pin) getPortMask() (*avr.Register8, uint8) {
|
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||||
return avr.PORTB, 1 << uint8(p)
|
return avr.PORTB, 1 << uint8(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
@@ -30,7 +31,7 @@ func (p Pin) Set(value bool) {
|
|||||||
// Warning: there are no separate pin set/clear registers on the AVR. The
|
// 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
|
// returned mask is only valid as long as no other pin in the same port has been
|
||||||
// changed.
|
// changed.
|
||||||
func (p Pin) PortMaskSet() (*avr.Register8, uint8) {
|
func (p Pin) PortMaskSet() (*volatile.Register8, uint8) {
|
||||||
port, mask := p.getPortMask()
|
port, mask := p.getPortMask()
|
||||||
return port, port.Get() | mask
|
return port, port.Get() | mask
|
||||||
}
|
}
|
||||||
@@ -41,7 +42,7 @@ func (p Pin) PortMaskSet() (*avr.Register8, uint8) {
|
|||||||
// Warning: there are no separate pin set/clear registers on the AVR. The
|
// 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
|
// returned mask is only valid as long as no other pin in the same port has been
|
||||||
// changed.
|
// changed.
|
||||||
func (p Pin) PortMaskClear() (*avr.Register8, uint8) {
|
func (p Pin) PortMaskClear() (*volatile.Register8, uint8) {
|
||||||
port, mask := p.getPortMask()
|
port, mask := p.getPortMask()
|
||||||
return port, port.Get() &^ mask
|
return port, port.Get() &^ mask
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
// +build !avr,!nrf,!sam,!stm32
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
// Dummy machine package, filled with no-ops.
|
|
||||||
|
|
||||||
type PinMode uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
PinInput PinMode = iota
|
|
||||||
PinOutput
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fake LED numbers, for testing.
|
|
||||||
const (
|
|
||||||
LED Pin = LED1
|
|
||||||
LED1 Pin = 0
|
|
||||||
LED2 Pin = 0
|
|
||||||
LED3 Pin = 0
|
|
||||||
LED4 Pin = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fake button numbers, for testing.
|
|
||||||
const (
|
|
||||||
BUTTON Pin = BUTTON1
|
|
||||||
BUTTON1 Pin = 0
|
|
||||||
BUTTON2 Pin = 0
|
|
||||||
BUTTON3 Pin = 0
|
|
||||||
BUTTON4 Pin = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p Pin) Configure(config PinConfig) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pin) Set(value bool) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pin) Get() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// +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))
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
// +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
|
||||||
@@ -5,14 +5,3 @@ package machine
|
|||||||
// Peripheral abstraction layer for the stm32.
|
// Peripheral abstraction layer for the stm32.
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
const (
|
|
||||||
portA Pin = iota * 16
|
|
||||||
portB
|
|
||||||
portC
|
|
||||||
portD
|
|
||||||
portE
|
|
||||||
portF
|
|
||||||
portG
|
|
||||||
portH
|
|
||||||
)
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build nrf stm32f103xx atsamd21g18a
|
// +build !stm32f407,!avr,!hifive1b
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build avr nrf sam stm32
|
// +build avr nrf sam sifive stm32
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
+9
-6
@@ -4,9 +4,9 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"device/sam"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
const deviceDescriptorSize = 18
|
const deviceDescriptorSize = 18
|
||||||
@@ -473,6 +473,9 @@ const (
|
|||||||
usb_CDC_CS_INTERFACE = 0x24
|
usb_CDC_CS_INTERFACE = 0x24
|
||||||
usb_CDC_CS_ENDPOINT = 0x25
|
usb_CDC_CS_ENDPOINT = 0x25
|
||||||
usb_CDC_DATA_INTERFACE_CLASS = 0x0A
|
usb_CDC_DATA_INTERFACE_CLASS = 0x0A
|
||||||
|
|
||||||
|
usb_CDC_LINESTATE_DTR = 0x01
|
||||||
|
usb_CDC_LINESTATE_RTS = 0x02
|
||||||
)
|
)
|
||||||
|
|
||||||
// usbDeviceDescBank is the USB device endpoint descriptor.
|
// usbDeviceDescBank is the USB device endpoint descriptor.
|
||||||
@@ -484,11 +487,11 @@ const (
|
|||||||
// RoReg8 Reserved1[0x5];
|
// RoReg8 Reserved1[0x5];
|
||||||
// } UsbDeviceDescBank;
|
// } UsbDeviceDescBank;
|
||||||
type usbDeviceDescBank struct {
|
type usbDeviceDescBank struct {
|
||||||
ADDR sam.Register32
|
ADDR volatile.Register32
|
||||||
PCKSIZE sam.Register32
|
PCKSIZE volatile.Register32
|
||||||
EXTREG sam.Register16
|
EXTREG volatile.Register16
|
||||||
STATUS_BK sam.Register8
|
STATUS_BK volatile.Register8
|
||||||
_reserved [5]sam.Register8
|
_reserved [5]volatile.Register8
|
||||||
}
|
}
|
||||||
|
|
||||||
type usbDeviceDescriptor struct {
|
type usbDeviceDescriptor struct {
|
||||||
|
|||||||
+160
-1
@@ -11,7 +11,8 @@ import (
|
|||||||
|
|
||||||
// Portable analogs of some common system call errors.
|
// Portable analogs of some common system call errors.
|
||||||
var (
|
var (
|
||||||
ErrUnsupported = errors.New("operation not supported")
|
errUnsupported = errors.New("operation not supported")
|
||||||
|
notImplemented = errors.New("os: not implemented")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
||||||
@@ -28,6 +29,21 @@ type File struct {
|
|||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Readdir is a stub, not yet implemented
|
||||||
|
func (f *File) Readdir(n int) ([]FileInfo, error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// Readdirnames is a stub, not yet implemented
|
||||||
|
func (f *File) Readdirnames(n int) (names []string, err error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat is a stub, not yet implemented
|
||||||
|
func (f *File) Stat() (FileInfo, error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// NewFile returns a new File with the given file descriptor and name.
|
// NewFile returns a new File with the given file descriptor and name.
|
||||||
func NewFile(fd uintptr, name string) *File {
|
func NewFile(fd uintptr, name string) *File {
|
||||||
return &File{fd, name}
|
return &File{fd, name}
|
||||||
@@ -38,3 +54,146 @@ func NewFile(fd uintptr, name string) *File {
|
|||||||
func (f *File) Fd() uintptr {
|
func (f *File) Fd() uintptr {
|
||||||
return f.fd
|
return f.fd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PathSeparator = '/' // OS-specific path separator
|
||||||
|
PathListSeparator = ':' // OS-specific path list separator
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsPathSeparator reports whether c is a directory separator character.
|
||||||
|
func IsPathSeparator(c uint8) bool {
|
||||||
|
return PathSeparator == c
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathError records an error and the operation and file path that caused it.
|
||||||
|
type PathError struct {
|
||||||
|
Op string
|
||||||
|
Path string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
|
||||||
|
|
||||||
|
// Open is a super simple stub function (for now), only capable of opening stdin, stdout, and stderr
|
||||||
|
func Open(name string) (*File, error) {
|
||||||
|
fd := uintptr(999)
|
||||||
|
switch name {
|
||||||
|
case "/dev/stdin":
|
||||||
|
fd = 0
|
||||||
|
case "/dev/stdout":
|
||||||
|
fd = 1
|
||||||
|
case "/dev/stderr":
|
||||||
|
fd = 2
|
||||||
|
default:
|
||||||
|
return nil, &PathError{"open", name, notImplemented}
|
||||||
|
}
|
||||||
|
return &File{fd, name}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile is a stub, passing through to the stub Open() call
|
||||||
|
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
|
||||||
|
return Open(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create is a stub, passing through to the stub Open() call
|
||||||
|
func Create(name string) (*File, error) {
|
||||||
|
return Open(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileMode uint32
|
||||||
|
|
||||||
|
// Mode constants, copied from the mainline Go source
|
||||||
|
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/types.go#L35-L63
|
||||||
|
const (
|
||||||
|
// The single letters are the abbreviations used by the String method's formatting.
|
||||||
|
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
|
||||||
|
ModeAppend // a: append-only
|
||||||
|
ModeExclusive // l: exclusive use
|
||||||
|
ModeTemporary // T: temporary file; Plan 9 only
|
||||||
|
ModeSymlink // L: symbolic link
|
||||||
|
ModeDevice // D: device file
|
||||||
|
ModeNamedPipe // p: named pipe (FIFO)
|
||||||
|
ModeSocket // S: Unix domain socket
|
||||||
|
ModeSetuid // u: setuid
|
||||||
|
ModeSetgid // g: setgid
|
||||||
|
ModeCharDevice // c: Unix character device, when ModeDevice is set
|
||||||
|
ModeSticky // t: sticky
|
||||||
|
ModeIrregular // ?: non-regular file; nothing else is known about this file
|
||||||
|
|
||||||
|
// Mask for the type bits. For regular files, none will be set.
|
||||||
|
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
|
||||||
|
|
||||||
|
ModePerm FileMode = 0777 // Unix permission bits
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsDir is a stub, always returning false
|
||||||
|
func (m FileMode) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stub constants
|
||||||
|
const (
|
||||||
|
O_RDONLY int = 1
|
||||||
|
O_WRONLY int = 2
|
||||||
|
O_RDWR int = 4
|
||||||
|
O_APPEND int = 8
|
||||||
|
O_CREATE int = 16
|
||||||
|
O_EXCL int = 32
|
||||||
|
O_SYNC int = 64
|
||||||
|
O_TRUNC int = 128
|
||||||
|
)
|
||||||
|
|
||||||
|
// A FileInfo describes a file and is returned by Stat and Lstat.
|
||||||
|
type FileInfo interface {
|
||||||
|
Name() string // base name of the file
|
||||||
|
Size() int64 // length in bytes for regular files; system-dependent for others
|
||||||
|
Mode() FileMode // file mode bits
|
||||||
|
// ModTime() time.Time // modification time
|
||||||
|
IsDir() bool // abbreviation for Mode().IsDir()
|
||||||
|
Sys() interface{} // underlying data source (can return nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat is a stub, not yet implemented
|
||||||
|
func Stat(name string) (FileInfo, error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lstat is a stub, not yet implemented
|
||||||
|
func Lstat(name string) (FileInfo, error) {
|
||||||
|
return nil, notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getwd is a stub (for now), always returning an empty string
|
||||||
|
func Getwd() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Readlink is a stub (for now), always returning the string it was given
|
||||||
|
func Readlink(name string) (string, error) {
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TempDir is a stub (for now), always returning the string "/tmp"
|
||||||
|
func TempDir() string {
|
||||||
|
return "/tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mkdir is a stub, not yet implemented
|
||||||
|
func Mkdir(name string, perm FileMode) error {
|
||||||
|
return notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExist is a stub (for now), always returning false
|
||||||
|
func IsExist(err error) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist is a stub (for now), always returning false
|
||||||
|
func IsNotExist(err error) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getpid is a stub (for now), always returning 1
|
||||||
|
func Getpid() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build avr cortexm wasm
|
// +build avr cortexm tinygo.riscv wasm
|
||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
// Read is unsupported on this system.
|
// Read is unsupported on this system.
|
||||||
func (f *File) Read(b []byte) (n int, err error) {
|
func (f *File) Read(b []byte) (n int, err error) {
|
||||||
return 0, ErrUnsupported
|
return 0, errUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes len(b) bytes to the output. It returns the number of bytes
|
// Write writes len(b) bytes to the output. It returns the number of bytes
|
||||||
@@ -21,13 +21,13 @@ func (f *File) Write(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
default:
|
default:
|
||||||
return 0, ErrUnsupported
|
return 0, errUnsupported
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close is unsupported on this system.
|
// Close is unsupported on this system.
|
||||||
func (f *File) Close() error {
|
func (f *File) Close() error {
|
||||||
return ErrUnsupported
|
return errUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname putchar runtime.putchar
|
//go:linkname putchar runtime.putchar
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build darwin linux,!avr,!cortexm
|
// +build darwin linux,!avr,!cortexm,!tinygo.riscv
|
||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Package os implements a subset of the Go "os" package. See
|
||||||
|
// https://godoc.org/os for details.
|
||||||
|
//
|
||||||
|
// Note that the current implementation is blocking. This limitation should be
|
||||||
|
// removed in a future version.
|
||||||
|
package os
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exit causes the current program to exit with the given status code.
|
||||||
|
// Conventionally, code zero indicates success, non-zero an error.
|
||||||
|
// The program terminates immediately; deferred functions are not run.
|
||||||
|
func Exit(code int) {
|
||||||
|
syscall.Exit(code)
|
||||||
|
}
|
||||||
@@ -92,7 +92,7 @@ func (v Value) Pointer() uintptr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) IsValid() bool {
|
func (v Value) IsValid() bool {
|
||||||
panic("unimplemented: (reflect.Value).IsValid()")
|
return v.typecode != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) CanInterface() bool {
|
func (v Value) CanInterface() bool {
|
||||||
@@ -502,6 +502,14 @@ func MakeSlice(typ Type, len, cap int) Value {
|
|||||||
panic("unimplemented: reflect.MakeSlice()")
|
panic("unimplemented: reflect.MakeSlice()")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Zero(typ Type) Value {
|
||||||
|
panic("unimplemented: reflect.Zero()")
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(typ Type) Value {
|
||||||
|
panic("unimplemented: reflect.New()")
|
||||||
|
}
|
||||||
|
|
||||||
type funcHeader struct {
|
type funcHeader struct {
|
||||||
Context unsafe.Pointer
|
Context unsafe.Pointer
|
||||||
Code unsafe.Pointer
|
Code unsafe.Pointer
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build arm,!avr,!cortexm
|
// +build arm,!avr,!cortexm,!tinygo.riscv
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
|||||||
+1
-16
@@ -2,26 +2,11 @@
|
|||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
const GOARCH = "arm" // avr pretends to be arm
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const GOARCH = "avr"
|
|
||||||
|
|
||||||
// The bitness of the CPU (e.g. 8, 32, 64).
|
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||||
const TargetBits = 8
|
const TargetBits = 8
|
||||||
|
|
||||||
//go:extern _heap_start
|
|
||||||
var heapStartSymbol unsafe.Pointer
|
|
||||||
|
|
||||||
//go:extern _heap_end
|
|
||||||
var heapEndSymbol unsafe.Pointer
|
|
||||||
|
|
||||||
var (
|
|
||||||
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
|
||||||
heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol))
|
|
||||||
)
|
|
||||||
|
|
||||||
// Align on a word boundary.
|
// Align on a word boundary.
|
||||||
func align(ptr uintptr) uintptr {
|
func align(ptr uintptr) uintptr {
|
||||||
// No alignment necessary on the AVR.
|
// No alignment necessary on the AVR.
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"device/arm"
|
"device/arm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,29 +11,6 @@ const GOARCH = "arm"
|
|||||||
// The bitness of the CPU (e.g. 8, 32, 64).
|
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||||
const TargetBits = 32
|
const TargetBits = 32
|
||||||
|
|
||||||
//go:extern _heap_start
|
|
||||||
var heapStartSymbol unsafe.Pointer
|
|
||||||
|
|
||||||
//go:extern _heap_end
|
|
||||||
var heapEndSymbol unsafe.Pointer
|
|
||||||
|
|
||||||
//go:extern _globals_start
|
|
||||||
var globalsStartSymbol unsafe.Pointer
|
|
||||||
|
|
||||||
//go:extern _globals_end
|
|
||||||
var globalsEndSymbol unsafe.Pointer
|
|
||||||
|
|
||||||
//go:extern _stack_top
|
|
||||||
var stackTopSymbol unsafe.Pointer
|
|
||||||
|
|
||||||
var (
|
|
||||||
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
|
||||||
heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol))
|
|
||||||
globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol))
|
|
||||||
globalsEnd = uintptr(unsafe.Pointer(&globalsEndSymbol))
|
|
||||||
stackTop = uintptr(unsafe.Pointer(&stackTopSymbol))
|
|
||||||
)
|
|
||||||
|
|
||||||
// Align on word boundary.
|
// Align on word boundary.
|
||||||
func align(ptr uintptr) uintptr {
|
func align(ptr uintptr) uintptr {
|
||||||
return (ptr + 3) &^ 3
|
return (ptr + 3) &^ 3
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// +build tinygo.riscv
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "device/riscv"
|
||||||
|
|
||||||
|
const GOARCH = "arm" // riscv pretends to be arm
|
||||||
|
|
||||||
|
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||||
|
const TargetBits = 32
|
||||||
|
|
||||||
|
// Align on word boundary.
|
||||||
|
func align(ptr uintptr) uintptr {
|
||||||
|
return (ptr + 3) &^ 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentStackPointer() uintptr {
|
||||||
|
return riscv.ReadRegister("sp")
|
||||||
|
}
|
||||||
@@ -14,9 +14,12 @@ const TargetBits = 32
|
|||||||
//go:extern __heap_base
|
//go:extern __heap_base
|
||||||
var heapStartSymbol unsafe.Pointer
|
var heapStartSymbol unsafe.Pointer
|
||||||
|
|
||||||
|
//go:export llvm.wasm.memory.size.i32
|
||||||
|
func wasm_memory_size(index int32) int32
|
||||||
|
|
||||||
var (
|
var (
|
||||||
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
||||||
heapEnd = (heapStart + wasmPageSize - 1) &^ (wasmPageSize - 1) // conservative guess: one page of heap memory
|
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
|
||||||
)
|
)
|
||||||
|
|
||||||
const wasmPageSize = 64 * 1024
|
const wasmPageSize = 64 * 1024
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// +build avr cortexm tinygo.riscv
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:extern _heap_start
|
||||||
|
var heapStartSymbol unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern _heap_end
|
||||||
|
var heapEndSymbol unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern _globals_start
|
||||||
|
var globalsStartSymbol unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern _globals_end
|
||||||
|
var globalsEndSymbol unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern _stack_top
|
||||||
|
var stackTopSymbol unsafe.Pointer
|
||||||
|
|
||||||
|
var (
|
||||||
|
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
||||||
|
heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol))
|
||||||
|
globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol))
|
||||||
|
globalsEnd = uintptr(unsafe.Pointer(&globalsEndSymbol))
|
||||||
|
stackTop = uintptr(unsafe.Pointer(&stackTopSymbol))
|
||||||
|
)
|
||||||
+81
-10
@@ -28,24 +28,35 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type channel struct {
|
type channel struct {
|
||||||
state uint8
|
elementSize uint16 // the size of one value in this channel
|
||||||
blocked *coroutine
|
state chanState
|
||||||
|
blocked *coroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type chanState uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
chanStateEmpty = iota
|
chanStateEmpty chanState = iota
|
||||||
chanStateRecv
|
chanStateRecv
|
||||||
chanStateSend
|
chanStateSend
|
||||||
chanStateClosed
|
chanStateClosed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// chanSelectState is a single channel operation (send/recv) in a select
|
||||||
|
// statement. The value pointer is either nil (for receives) or points to the
|
||||||
|
// value to send (for sends).
|
||||||
|
type chanSelectState struct {
|
||||||
|
ch *channel
|
||||||
|
value unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
func deadlockStub()
|
func deadlockStub()
|
||||||
|
|
||||||
// chanSend sends a single value over the channel. If this operation can
|
// chanSend sends a single value over the channel. If this operation can
|
||||||
// complete immediately (there is a goroutine waiting for a value), it sends the
|
// complete immediately (there is a goroutine waiting for a value), it sends the
|
||||||
// value and re-activates both goroutines. If not, it sets itself as waiting on
|
// value and re-activates both goroutines. If not, it sets itself as waiting on
|
||||||
// a value.
|
// a value.
|
||||||
func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer, size uintptr) {
|
func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer) {
|
||||||
if ch == nil {
|
if ch == nil {
|
||||||
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
||||||
return
|
return
|
||||||
@@ -58,7 +69,7 @@ func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer, size uintptr
|
|||||||
case chanStateRecv:
|
case chanStateRecv:
|
||||||
receiver := ch.blocked
|
receiver := ch.blocked
|
||||||
receiverPromise := receiver.promise()
|
receiverPromise := receiver.promise()
|
||||||
memcpy(receiverPromise.ptr, value, size)
|
memcpy(receiverPromise.ptr, value, uintptr(ch.elementSize))
|
||||||
receiverPromise.data = 1 // commaOk = true
|
receiverPromise.data = 1 // commaOk = true
|
||||||
ch.blocked = receiverPromise.next
|
ch.blocked = receiverPromise.next
|
||||||
receiverPromise.next = nil
|
receiverPromise.next = nil
|
||||||
@@ -80,7 +91,7 @@ func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer, size uintptr
|
|||||||
// sender, it receives the value immediately and re-activates both coroutines.
|
// sender, it receives the value immediately and re-activates both coroutines.
|
||||||
// If not, it sets itself as available for receiving. If the channel is closed,
|
// If not, it sets itself as available for receiving. If the channel is closed,
|
||||||
// it immediately activates itself with a zero value as the result.
|
// it immediately activates itself with a zero value as the result.
|
||||||
func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintptr) {
|
func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer) {
|
||||||
if ch == nil {
|
if ch == nil {
|
||||||
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
||||||
return
|
return
|
||||||
@@ -89,7 +100,7 @@ func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintp
|
|||||||
case chanStateSend:
|
case chanStateSend:
|
||||||
sender := ch.blocked
|
sender := ch.blocked
|
||||||
senderPromise := sender.promise()
|
senderPromise := sender.promise()
|
||||||
memcpy(value, senderPromise.ptr, size)
|
memcpy(value, senderPromise.ptr, uintptr(ch.elementSize))
|
||||||
receiver.promise().data = 1 // commaOk = true
|
receiver.promise().data = 1 // commaOk = true
|
||||||
ch.blocked = senderPromise.next
|
ch.blocked = senderPromise.next
|
||||||
senderPromise.next = nil
|
senderPromise.next = nil
|
||||||
@@ -103,7 +114,7 @@ func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintp
|
|||||||
ch.state = chanStateRecv
|
ch.state = chanStateRecv
|
||||||
ch.blocked = receiver
|
ch.blocked = receiver
|
||||||
case chanStateClosed:
|
case chanStateClosed:
|
||||||
memzero(value, size)
|
memzero(value, uintptr(ch.elementSize))
|
||||||
receiver.promise().data = 0 // commaOk = false
|
receiver.promise().data = 0 // commaOk = false
|
||||||
activateTask(receiver)
|
activateTask(receiver)
|
||||||
case chanStateRecv:
|
case chanStateRecv:
|
||||||
@@ -115,7 +126,7 @@ func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer, size uintp
|
|||||||
|
|
||||||
// chanClose closes the given channel. If this channel has a receiver or is
|
// chanClose closes the given channel. If this channel has a receiver or is
|
||||||
// empty, it closes the channel. Else, it panics.
|
// empty, it closes the channel. Else, it panics.
|
||||||
func chanClose(ch *channel, size uintptr) {
|
func chanClose(ch *channel) {
|
||||||
if ch == nil {
|
if ch == nil {
|
||||||
// Not allowed by the language spec.
|
// Not allowed by the language spec.
|
||||||
runtimePanic("close of nil channel")
|
runtimePanic("close of nil channel")
|
||||||
@@ -133,7 +144,7 @@ func chanClose(ch *channel, size uintptr) {
|
|||||||
case chanStateRecv:
|
case chanStateRecv:
|
||||||
// The receiver must be re-activated with a zero value.
|
// The receiver must be re-activated with a zero value.
|
||||||
receiverPromise := ch.blocked.promise()
|
receiverPromise := ch.blocked.promise()
|
||||||
memzero(receiverPromise.ptr, size)
|
memzero(receiverPromise.ptr, uintptr(ch.elementSize))
|
||||||
receiverPromise.data = 0 // commaOk = false
|
receiverPromise.data = 0 // commaOk = false
|
||||||
activateTask(ch.blocked)
|
activateTask(ch.blocked)
|
||||||
ch.state = chanStateClosed
|
ch.state = chanStateClosed
|
||||||
@@ -143,3 +154,63 @@ func chanClose(ch *channel, size uintptr) {
|
|||||||
ch.state = chanStateClosed
|
ch.state = chanStateClosed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chanSelect is the runtime implementation of the select statement. This is
|
||||||
|
// perhaps the most complicated statement in the Go spec. It returns the
|
||||||
|
// selected index and the 'comma-ok' value.
|
||||||
|
//
|
||||||
|
// TODO: do this in a round-robin fashion (as specified in the Go spec) instead
|
||||||
|
// of picking the first one that can proceed.
|
||||||
|
func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, blocking bool) (uintptr, bool) {
|
||||||
|
// See whether we can receive from one of the channels.
|
||||||
|
for i, state := range states {
|
||||||
|
if state.ch == nil {
|
||||||
|
// A nil channel blocks forever, so don't consider it here.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if state.value == nil {
|
||||||
|
// A receive operation.
|
||||||
|
switch state.ch.state {
|
||||||
|
case chanStateSend:
|
||||||
|
// We can receive immediately.
|
||||||
|
sender := state.ch.blocked
|
||||||
|
senderPromise := sender.promise()
|
||||||
|
memcpy(recvbuf, senderPromise.ptr, uintptr(state.ch.elementSize))
|
||||||
|
state.ch.blocked = senderPromise.next
|
||||||
|
senderPromise.next = nil
|
||||||
|
activateTask(sender)
|
||||||
|
if state.ch.blocked == nil {
|
||||||
|
state.ch.state = chanStateEmpty
|
||||||
|
}
|
||||||
|
return uintptr(i), true // commaOk = true
|
||||||
|
case chanStateClosed:
|
||||||
|
// Receive the zero value.
|
||||||
|
memzero(recvbuf, uintptr(state.ch.elementSize))
|
||||||
|
return uintptr(i), false // commaOk = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// A send operation: state.value is not nil.
|
||||||
|
switch state.ch.state {
|
||||||
|
case chanStateRecv:
|
||||||
|
receiver := state.ch.blocked
|
||||||
|
receiverPromise := receiver.promise()
|
||||||
|
memcpy(receiverPromise.ptr, state.value, uintptr(state.ch.elementSize))
|
||||||
|
receiverPromise.data = 1 // commaOk = true
|
||||||
|
state.ch.blocked = receiverPromise.next
|
||||||
|
receiverPromise.next = nil
|
||||||
|
activateTask(receiver)
|
||||||
|
if state.ch.blocked == nil {
|
||||||
|
state.ch.state = chanStateEmpty
|
||||||
|
}
|
||||||
|
return uintptr(i), false
|
||||||
|
case chanStateClosed:
|
||||||
|
runtimePanic("send on closed channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !blocking {
|
||||||
|
return ^uintptr(0), false
|
||||||
|
}
|
||||||
|
panic("unimplemented: blocking select")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build gc.marksweep
|
// +build gc.conservative
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ func (b gcBlock) findHead() gcBlock {
|
|||||||
// findNext returns the first block just past the end of the tail. This may or
|
// findNext returns the first block just past the end of the tail. This may or
|
||||||
// may not be the head of an object.
|
// may not be the head of an object.
|
||||||
func (b gcBlock) findNext() gcBlock {
|
func (b gcBlock) findNext() gcBlock {
|
||||||
if b.state() == blockStateHead {
|
if b.state() == blockStateHead || b.state() == blockStateMark {
|
||||||
b++
|
b++
|
||||||
}
|
}
|
||||||
for b.state() == blockStateTail {
|
for b.state() == blockStateTail {
|
||||||
@@ -203,6 +203,7 @@ func init() {
|
|||||||
|
|
||||||
// alloc tries to find some free space on the heap, possibly doing a garbage
|
// alloc tries to find some free space on the heap, possibly doing a garbage
|
||||||
// collection cycle if needed. If no space is free, it panics.
|
// collection cycle if needed. If no space is free, it panics.
|
||||||
|
//go:noinline
|
||||||
func alloc(size uintptr) unsafe.Pointer {
|
func alloc(size uintptr) unsafe.Pointer {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return unsafe.Pointer(&zeroSizedAlloc)
|
return unsafe.Pointer(&zeroSizedAlloc)
|
||||||
@@ -282,8 +283,8 @@ func GC() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mark phase: mark all reachable objects, recursively.
|
// Mark phase: mark all reachable objects, recursively.
|
||||||
markRoots(globalsStart, globalsEnd)
|
markGlobals()
|
||||||
markRoots(getCurrentStackPointer(), stackTop) // assume a descending stack
|
markStack()
|
||||||
|
|
||||||
// Sweep phase: free all non-marked objects and unmark marked objects for
|
// Sweep phase: free all non-marked objects and unmark marked objects for
|
||||||
// the next collection cycle.
|
// the next collection cycle.
|
||||||
@@ -303,21 +304,30 @@ func markRoots(start, end uintptr) {
|
|||||||
if gcDebug {
|
if gcDebug {
|
||||||
println("mark from", start, "to", end, int(end-start))
|
println("mark from", start, "to", end, int(end-start))
|
||||||
}
|
}
|
||||||
|
if gcAsserts {
|
||||||
|
if start >= end {
|
||||||
|
runtimePanic("gc: unexpected range to mark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
||||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||||
if looksLikePointer(root) {
|
markRoot(addr, root)
|
||||||
block := blockFromAddr(root)
|
}
|
||||||
head := block.findHead()
|
}
|
||||||
if head.state() != blockStateMark {
|
|
||||||
if gcDebug {
|
func markRoot(addr, root uintptr) {
|
||||||
println("found unmarked pointer", root, "at address", addr)
|
if looksLikePointer(root) {
|
||||||
}
|
block := blockFromAddr(root)
|
||||||
head.setState(blockStateMark)
|
head := block.findHead()
|
||||||
next := block.findNext()
|
if head.state() != blockStateMark {
|
||||||
// TODO: avoid recursion as much as possible
|
if gcDebug {
|
||||||
markRoots(head.address(), next.address())
|
println("found unmarked pointer", root, "at address", addr)
|
||||||
}
|
}
|
||||||
|
head.setState(blockStateMark)
|
||||||
|
next := block.findNext()
|
||||||
|
// TODO: avoid recursion as much as possible
|
||||||
|
markRoots(head.address(), next.address())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// +build gc.conservative
|
||||||
|
// +build cortexm tinygo.riscv
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
// markGlobals marks all globals, which are reachable by definition.
|
||||||
|
//
|
||||||
|
// This implementation marks all globals conservatively and assumes it can use
|
||||||
|
// linker-defined symbols for the start and end of the .data section.
|
||||||
|
func markGlobals() {
|
||||||
|
markRoots(globalsStart, globalsEnd)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// +build gc.conservative
|
||||||
|
// +build !cortexm,!tinygo.riscv
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:extern runtime.trackedGlobalsStart
|
||||||
|
var trackedGlobalsStart uintptr
|
||||||
|
|
||||||
|
//go:extern runtime.trackedGlobalsLength
|
||||||
|
var trackedGlobalsLength uintptr
|
||||||
|
|
||||||
|
//go:extern runtime.trackedGlobalsBitmap
|
||||||
|
var trackedGlobalsBitmap [0]uint8
|
||||||
|
|
||||||
|
// markGlobals marks all globals, which are reachable by definition.
|
||||||
|
//
|
||||||
|
// This implementation relies on a compiler pass that stores all globals in a
|
||||||
|
// single global (adjusting all uses of them accordingly) and creates a bit
|
||||||
|
// vector with the locations of each pointer. This implementation then walks the
|
||||||
|
// bit vector and for each pointer it indicates, it marks the root.
|
||||||
|
//
|
||||||
|
//go:nobounds
|
||||||
|
func markGlobals() {
|
||||||
|
for i := uintptr(0); i < trackedGlobalsLength; i++ {
|
||||||
|
if trackedGlobalsBitmap[i/8]&(1<<(i%8)) != 0 {
|
||||||
|
addr := trackedGlobalsStart + i*unsafe.Alignof(uintptr(0))
|
||||||
|
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||||
|
markRoot(addr, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build gc.dumb
|
// +build gc.leaking
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// +build gc.conservative
|
||||||
|
// +build !cortexm,!tinygo.riscv
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:extern runtime.stackChainStart
|
||||||
|
var stackChainStart *stackChainObject
|
||||||
|
|
||||||
|
type stackChainObject struct {
|
||||||
|
parent *stackChainObject
|
||||||
|
numSlots uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// markStack marks all root pointers found on the stack.
|
||||||
|
//
|
||||||
|
// This implementation is conservative and relies on the compiler inserting code
|
||||||
|
// to manually push/pop stack objects that are stored in a linked list starting
|
||||||
|
// with stackChainStart. Manually keeping track of stack values is _much_ more
|
||||||
|
// expensive than letting the compiler do it and it inhibits a few important
|
||||||
|
// optimizations, but it has the big advantage of being portable to basically
|
||||||
|
// any ISA, including WebAssembly.
|
||||||
|
func markStack() {
|
||||||
|
stackObject := stackChainStart
|
||||||
|
for stackObject != nil {
|
||||||
|
start := uintptr(unsafe.Pointer(stackObject)) + unsafe.Sizeof(uintptr(0))*2
|
||||||
|
end := start + stackObject.numSlots*unsafe.Alignof(uintptr(0))
|
||||||
|
markRoots(start, end)
|
||||||
|
stackObject = stackObject.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trackPointer is a stub function call inserted by the compiler during IR
|
||||||
|
// construction. Calls to it are later replaced with regular stack bookkeeping
|
||||||
|
// code.
|
||||||
|
func trackPointer(ptr unsafe.Pointer)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// +build gc.conservative
|
||||||
|
// +build cortexm tinygo.riscv
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
// markStack marks all root pointers found on the stack.
|
||||||
|
//
|
||||||
|
// This implementation is conservative and relies on the stack top (provided by
|
||||||
|
// the linker) and getting the current stack pointer from a register. Also, it
|
||||||
|
// assumes a descending stack. Thus, it is not very portable.
|
||||||
|
func markStack() {
|
||||||
|
markRoots(getCurrentStackPointer(), stackTop)
|
||||||
|
}
|
||||||
+54
-6
@@ -56,7 +56,15 @@ func math_Cbrt(x float64) float64 { return math_cbrt(x) }
|
|||||||
func math_cbrt(x float64) float64
|
func math_cbrt(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_Ceil math.Ceil
|
//go:linkname math_Ceil math.Ceil
|
||||||
func math_Ceil(x float64) float64 { return math_ceil(x) }
|
func math_Ceil(x float64) float64 {
|
||||||
|
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||||
|
return llvm_ceil(x)
|
||||||
|
}
|
||||||
|
return math_ceil(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export llvm.ceil.f64
|
||||||
|
func llvm_ceil(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_ceil math.ceil
|
//go:linkname math_ceil math.ceil
|
||||||
func math_ceil(x float64) float64
|
func math_ceil(x float64) float64
|
||||||
@@ -104,7 +112,15 @@ func math_Exp2(x float64) float64 { return math_exp2(x) }
|
|||||||
func math_exp2(x float64) float64
|
func math_exp2(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_Floor math.Floor
|
//go:linkname math_Floor math.Floor
|
||||||
func math_Floor(x float64) float64 { return math_floor(x) }
|
func math_Floor(x float64) float64 {
|
||||||
|
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||||
|
return llvm_floor(x)
|
||||||
|
}
|
||||||
|
return math_floor(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export llvm.floor.f64
|
||||||
|
func llvm_floor(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_floor math.floor
|
//go:linkname math_floor math.floor
|
||||||
func math_floor(x float64) float64
|
func math_floor(x float64) float64
|
||||||
@@ -152,13 +168,29 @@ func math_Log2(x float64) float64 { return math_log2(x) }
|
|||||||
func math_log2(x float64) float64
|
func math_log2(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_Max math.Max
|
//go:linkname math_Max math.Max
|
||||||
func math_Max(x, y float64) float64 { return math_max(x, y) }
|
func math_Max(x, y float64) float64 {
|
||||||
|
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||||
|
return llvm_maximum(x, y)
|
||||||
|
}
|
||||||
|
return math_max(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export llvm.maximum.f64
|
||||||
|
func llvm_maximum(x, y float64) float64
|
||||||
|
|
||||||
//go:linkname math_max math.max
|
//go:linkname math_max math.max
|
||||||
func math_max(x, y float64) float64
|
func math_max(x, y float64) float64
|
||||||
|
|
||||||
//go:linkname math_Min math.Min
|
//go:linkname math_Min math.Min
|
||||||
func math_Min(x, y float64) float64 { return math_min(x, y) }
|
func math_Min(x, y float64) float64 {
|
||||||
|
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||||
|
return llvm_minimum(x, y)
|
||||||
|
}
|
||||||
|
return math_min(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export llvm.minimum.f64
|
||||||
|
func llvm_minimum(x, y float64) float64
|
||||||
|
|
||||||
//go:linkname math_min math.min
|
//go:linkname math_min math.min
|
||||||
func math_min(x, y float64) float64
|
func math_min(x, y float64) float64
|
||||||
@@ -200,7 +232,15 @@ func math_Sinh(x float64) float64 { return math_sinh(x) }
|
|||||||
func math_sinh(x float64) float64
|
func math_sinh(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_Sqrt math.Sqrt
|
//go:linkname math_Sqrt math.Sqrt
|
||||||
func math_Sqrt(x float64) float64 { return math_sqrt(x) }
|
func math_Sqrt(x float64) float64 {
|
||||||
|
if GOARCH == "x86" || GOARCH == "amd64" || GOARCH == "wasm" {
|
||||||
|
return llvm_sqrt(x)
|
||||||
|
}
|
||||||
|
return math_sqrt(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export llvm.sqrt.f64
|
||||||
|
func llvm_sqrt(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_sqrt math.sqrt
|
//go:linkname math_sqrt math.sqrt
|
||||||
func math_sqrt(x float64) float64
|
func math_sqrt(x float64) float64
|
||||||
@@ -218,7 +258,15 @@ func math_Tanh(x float64) float64 { return math_tanh(x) }
|
|||||||
func math_tanh(x float64) float64
|
func math_tanh(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_Trunc math.Trunc
|
//go:linkname math_Trunc math.Trunc
|
||||||
func math_Trunc(x float64) float64 { return math_trunc(x) }
|
func math_Trunc(x float64) float64 {
|
||||||
|
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||||
|
return llvm_trunc(x)
|
||||||
|
}
|
||||||
|
return math_trunc(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export llvm.trunc.f64
|
||||||
|
func llvm_trunc(x float64) float64
|
||||||
|
|
||||||
//go:linkname math_trunc math.trunc
|
//go:linkname math_trunc math.trunc
|
||||||
func math_trunc(x float64) float64
|
func math_trunc(x float64) float64
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/sam"
|
"device/sam"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -230,10 +231,7 @@ var (
|
|||||||
timerLastCounter uint64
|
timerLastCounter uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:volatile
|
var timerWakeup volatile.Register8
|
||||||
type isrFlag bool
|
|
||||||
|
|
||||||
var timerWakeup isrFlag
|
|
||||||
|
|
||||||
const asyncScheduler = false
|
const asyncScheduler = false
|
||||||
|
|
||||||
@@ -262,7 +260,7 @@ func ticks() timeUnit {
|
|||||||
|
|
||||||
// ticks are in microseconds
|
// ticks are in microseconds
|
||||||
func timerSleep(ticks uint32) {
|
func timerSleep(ticks uint32) {
|
||||||
timerWakeup = false
|
timerWakeup.Set(0)
|
||||||
if ticks < 30 {
|
if ticks < 30 {
|
||||||
// have to have at least one clock count
|
// have to have at least one clock count
|
||||||
ticks = 30
|
ticks = 30
|
||||||
@@ -280,7 +278,7 @@ func timerSleep(ticks uint32) {
|
|||||||
// enable IRQ for CMP0 compare
|
// enable IRQ for CMP0 compare
|
||||||
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||||
|
|
||||||
for !timerWakeup {
|
for timerWakeup.Get() == 0 {
|
||||||
arm.Asm("wfi")
|
arm.Asm("wfi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -290,7 +288,7 @@ func handleRTC() {
|
|||||||
// disable IRQ for CMP0 compare
|
// disable IRQ for CMP0 compare
|
||||||
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||||
|
|
||||||
timerWakeup = true
|
timerWakeup.Set(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initUSBClock() {
|
func initUSBClock() {
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// +build fe310
|
||||||
|
|
||||||
|
// This file implements target-specific things for the FE310 chip as used in the
|
||||||
|
// HiFive1.
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"device/riscv"
|
||||||
|
"device/sifive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeUnit int64
|
||||||
|
|
||||||
|
const tickMicros = 32768 // RTC runs at 32.768kHz
|
||||||
|
|
||||||
|
//go:extern _sbss
|
||||||
|
var _sbss unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern _ebss
|
||||||
|
var _ebss unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern _sdata
|
||||||
|
var _sdata unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern _sidata
|
||||||
|
var _sidata unsafe.Pointer
|
||||||
|
|
||||||
|
//go:extern _edata
|
||||||
|
var _edata unsafe.Pointer
|
||||||
|
|
||||||
|
//go:export main
|
||||||
|
func main() {
|
||||||
|
preinit()
|
||||||
|
initAll()
|
||||||
|
callMain()
|
||||||
|
abort()
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pric_init()
|
||||||
|
machine.UART0.Configure(machine.UARTConfig{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func pric_init() {
|
||||||
|
// Make sure the HFROSC is on
|
||||||
|
sifive.PRIC.HFROSCCFG.SetBits(sifive.PRIC_HFROSCCFG_ENABLE)
|
||||||
|
|
||||||
|
// Run off 16 MHz Crystal for accuracy.
|
||||||
|
sifive.PRIC.PLLCFG.SetBits(sifive.PRIC_PLLCFG_REFSEL | sifive.PRIC_PLLCFG_BYPASS)
|
||||||
|
sifive.PRIC.PLLCFG.SetBits(sifive.PRIC_PLLCFG_SEL)
|
||||||
|
|
||||||
|
// Turn off HFROSC to save power
|
||||||
|
sifive.PRIC.HFROSCCFG.ClearBits(sifive.PRIC_HFROSCCFG_ENABLE)
|
||||||
|
|
||||||
|
// Enable the RTC.
|
||||||
|
sifive.RTC.CONFIG.Set(sifive.RTC_CONFIG_ENALWAYS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func preinit() {
|
||||||
|
// Initialize .bss: zero-initialized global variables.
|
||||||
|
ptr := uintptr(unsafe.Pointer(&_sbss))
|
||||||
|
for ptr != uintptr(unsafe.Pointer(&_ebss)) {
|
||||||
|
*(*uint32)(unsafe.Pointer(ptr)) = 0
|
||||||
|
ptr += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize .data: global variables initialized from flash.
|
||||||
|
src := uintptr(unsafe.Pointer(&_sidata))
|
||||||
|
dst := uintptr(unsafe.Pointer(&_sdata))
|
||||||
|
for dst != uintptr(unsafe.Pointer(&_edata)) {
|
||||||
|
*(*uint32)(unsafe.Pointer(dst)) = *(*uint32)(unsafe.Pointer(src))
|
||||||
|
dst += 4
|
||||||
|
src += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func putchar(c byte) {
|
||||||
|
machine.UART0.WriteByte(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ticks() timeUnit {
|
||||||
|
// Combining the low bits and the high bits yields a time span of over 270
|
||||||
|
// years without counter rollover.
|
||||||
|
highBits := sifive.RTC.HI.Get()
|
||||||
|
for {
|
||||||
|
lowBits := sifive.RTC.LO.Get()
|
||||||
|
newHighBits := sifive.RTC.HI.Get()
|
||||||
|
if newHighBits == highBits {
|
||||||
|
// High bits stayed the same.
|
||||||
|
return timeUnit(lowBits) | (timeUnit(highBits) << 32)
|
||||||
|
}
|
||||||
|
// Retry, because there was a rollover in the low bits (happening every
|
||||||
|
// 1.5 days).
|
||||||
|
highBits = newHighBits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const asyncScheduler = false
|
||||||
|
|
||||||
|
func sleepTicks(d timeUnit) {
|
||||||
|
target := ticks() + d
|
||||||
|
for ticks() < target {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func abort() {
|
||||||
|
// lock up forever
|
||||||
|
for {
|
||||||
|
riscv.Asm("wfi")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
type timeUnit int64
|
type timeUnit int64
|
||||||
@@ -79,14 +80,11 @@ func ticks() timeUnit {
|
|||||||
return timestamp
|
return timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:volatile
|
var rtc_wakeup volatile.Register8
|
||||||
type isrFlag bool
|
|
||||||
|
|
||||||
var rtc_wakeup isrFlag
|
|
||||||
|
|
||||||
func rtc_sleep(ticks uint32) {
|
func rtc_sleep(ticks uint32) {
|
||||||
nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_COMPARE0)
|
nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||||
rtc_wakeup = false
|
rtc_wakeup.Set(0)
|
||||||
if ticks == 1 {
|
if ticks == 1 {
|
||||||
// Race condition (even in hardware) at ticks == 1.
|
// Race condition (even in hardware) at ticks == 1.
|
||||||
// TODO: fix this in a better way by detecting it, like the manual
|
// TODO: fix this in a better way by detecting it, like the manual
|
||||||
@@ -94,7 +92,7 @@ func rtc_sleep(ticks uint32) {
|
|||||||
ticks = 2
|
ticks = 2
|
||||||
}
|
}
|
||||||
nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff)
|
nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff)
|
||||||
for !rtc_wakeup {
|
for rtc_wakeup.Get() == 0 {
|
||||||
arm.Asm("wfi")
|
arm.Asm("wfi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,5 +101,5 @@ func rtc_sleep(ticks uint32) {
|
|||||||
func handleRTC1() {
|
func handleRTC1() {
|
||||||
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||||
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||||
rtc_wakeup = true
|
rtc_wakeup.Set(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
"device/arm"
|
||||||
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,12 +37,9 @@ func ticks() timeUnit {
|
|||||||
return timestamp
|
return timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:volatile
|
|
||||||
type regValue uint32
|
|
||||||
|
|
||||||
// UART0 output register.
|
// UART0 output register.
|
||||||
var stdoutWrite *regValue = (*regValue)(unsafe.Pointer(uintptr(0x4000c000)))
|
var stdoutWrite = (*volatile.Register8)(unsafe.Pointer(uintptr(0x4000c000)))
|
||||||
|
|
||||||
func putchar(c byte) {
|
func putchar(c byte) {
|
||||||
*stdoutWrite = regValue(c)
|
stdoutWrite.Set(uint8(c))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -58,10 +59,7 @@ var (
|
|||||||
timerLastCounter uint64
|
timerLastCounter uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:volatile
|
var timerWakeup volatile.Register8
|
||||||
type isrFlag bool
|
|
||||||
|
|
||||||
var timerWakeup isrFlag
|
|
||||||
|
|
||||||
func initRTC() {
|
func initRTC() {
|
||||||
// Enable the PWR and BKP.
|
// Enable the PWR and BKP.
|
||||||
@@ -136,7 +134,7 @@ func ticks() timeUnit {
|
|||||||
|
|
||||||
// ticks are in microseconds
|
// ticks are in microseconds
|
||||||
func timerSleep(ticks uint32) {
|
func timerSleep(ticks uint32) {
|
||||||
timerWakeup = false
|
timerWakeup.Set(0)
|
||||||
|
|
||||||
// STM32 timer update event period is calculated as follows:
|
// STM32 timer update event period is calculated as follows:
|
||||||
//
|
//
|
||||||
@@ -177,7 +175,7 @@ func timerSleep(ticks uint32) {
|
|||||||
stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN)
|
stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN)
|
||||||
|
|
||||||
// wait till timer wakes up
|
// wait till timer wakes up
|
||||||
for !timerWakeup {
|
for timerWakeup.Get() == 0 {
|
||||||
arm.Asm("wfi")
|
arm.Asm("wfi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,6 +190,6 @@ func handleTIM3() {
|
|||||||
stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF)
|
stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF)
|
||||||
|
|
||||||
// timer was triggered
|
// timer was triggered
|
||||||
timerWakeup = true
|
timerWakeup.Set(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -114,10 +115,7 @@ var (
|
|||||||
tickCount timeUnit
|
tickCount timeUnit
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:volatile
|
var timerWakeup volatile.Register8
|
||||||
type isrFlag bool
|
|
||||||
|
|
||||||
var timerWakeup isrFlag
|
|
||||||
|
|
||||||
// Enable the TIM3 clock.(sleep count)
|
// Enable the TIM3 clock.(sleep count)
|
||||||
func initTIM3() {
|
func initTIM3() {
|
||||||
@@ -160,7 +158,7 @@ func ticks() timeUnit {
|
|||||||
|
|
||||||
// ticks are in microseconds
|
// ticks are in microseconds
|
||||||
func timerSleep(ticks uint32) {
|
func timerSleep(ticks uint32) {
|
||||||
timerWakeup = false
|
timerWakeup.Set(0)
|
||||||
|
|
||||||
// CK_INT = APB1 x2 = 84mhz
|
// CK_INT = APB1 x2 = 84mhz
|
||||||
// prescale counter down from 84mhz to 10khz aka 0.1 ms frequency.
|
// prescale counter down from 84mhz to 10khz aka 0.1 ms frequency.
|
||||||
@@ -180,7 +178,7 @@ func timerSleep(ticks uint32) {
|
|||||||
stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN)
|
stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN)
|
||||||
|
|
||||||
// wait till timer wakes up
|
// wait till timer wakes up
|
||||||
for !timerWakeup {
|
for timerWakeup.Get() == 0 {
|
||||||
arm.Asm("wfi")
|
arm.Asm("wfi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +193,7 @@ func handleTIM3() {
|
|||||||
stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF)
|
stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF)
|
||||||
|
|
||||||
// timer was triggered
|
// timer was triggered
|
||||||
timerWakeup = true
|
timerWakeup.Set(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// +build tinygo.riscv
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// Implement memset for LLVM.
|
||||||
|
//go:export memset
|
||||||
|
func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) {
|
||||||
|
for i := uintptr(0); i < size; i++ {
|
||||||
|
*(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement memmove for LLVM.
|
||||||
|
//go:export memmove
|
||||||
|
func libc_memmove(dst, src unsafe.Pointer, size uintptr) {
|
||||||
|
memmove(dst, src, size)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build darwin linux,!avr,!cortexm
|
// +build darwin linux,!avr,!cortexm,!tinygo.riscv
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,21 @@ func stringToBytes(x _string) (slice struct {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a string to []rune slice.
|
||||||
|
func stringToRunes(s string) []rune {
|
||||||
|
var n = 0
|
||||||
|
for range s {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
var r = make([]rune, n)
|
||||||
|
n = 0
|
||||||
|
for _, e := range s {
|
||||||
|
r[n] = e
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
// Create a string from a Unicode code point.
|
// Create a string from a Unicode code point.
|
||||||
func stringFromUnicode(x rune) _string {
|
func stringFromUnicode(x rune) _string {
|
||||||
array, length := encodeUTF8(x)
|
array, length := encodeUTF8(x)
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package volatile
|
||||||
|
|
||||||
|
// This file defines Register{8,16,32} types, which are convenience types for
|
||||||
|
// volatile register accesses.
|
||||||
|
|
||||||
|
// Special types that causes loads/stores to be volatile (necessary for
|
||||||
|
// memory-mapped registers).
|
||||||
|
type Register8 struct {
|
||||||
|
Reg uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value in the register. It is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// *r.Reg
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register8) Get() uint8 {
|
||||||
|
return LoadUint8(&r.Reg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set updates the register value. It is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// *r.Reg = value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register8) Set(value uint8) {
|
||||||
|
StoreUint8(&r.Reg, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBits reads the register, sets the given bits, and writes it back. It is
|
||||||
|
// the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// r.Reg |= value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register8) SetBits(value uint8) {
|
||||||
|
StoreUint8(&r.Reg, LoadUint8(&r.Reg)|value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearBits reads the register, clears the given bits, and writes it back. It
|
||||||
|
// is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// r.Reg &^= value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register8) ClearBits(value uint8) {
|
||||||
|
StoreUint8(&r.Reg, LoadUint8(&r.Reg)&^value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasBits reads the register and then checks to see if the passed bits are set. It
|
||||||
|
// is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// (*r.Reg & value) > 0
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register8) HasBits(value uint8) bool {
|
||||||
|
return (r.Get() & value) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Register16 struct {
|
||||||
|
Reg uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value in the register. It is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// *r.Reg
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register16) Get() uint16 {
|
||||||
|
return LoadUint16(&r.Reg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set updates the register value. It is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// *r.Reg = value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register16) Set(value uint16) {
|
||||||
|
StoreUint16(&r.Reg, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBits reads the register, sets the given bits, and writes it back. It is
|
||||||
|
// the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// r.Reg |= value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register16) SetBits(value uint16) {
|
||||||
|
StoreUint16(&r.Reg, LoadUint16(&r.Reg)|value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearBits reads the register, clears the given bits, and writes it back. It
|
||||||
|
// is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// r.Reg &^= value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register16) ClearBits(value uint16) {
|
||||||
|
StoreUint16(&r.Reg, LoadUint16(&r.Reg)&^value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasBits reads the register and then checks to see if the passed bits are set. It
|
||||||
|
// is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// (*r.Reg & value) > 0
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register16) HasBits(value uint16) bool {
|
||||||
|
return (r.Get() & value) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Register32 struct {
|
||||||
|
Reg uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value in the register. It is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// *r.Reg
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register32) Get() uint32 {
|
||||||
|
return LoadUint32(&r.Reg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set updates the register value. It is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// *r.Reg = value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register32) Set(value uint32) {
|
||||||
|
StoreUint32(&r.Reg, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBits reads the register, sets the given bits, and writes it back. It is
|
||||||
|
// the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// r.Reg |= value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register32) SetBits(value uint32) {
|
||||||
|
StoreUint32(&r.Reg, LoadUint32(&r.Reg)|value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearBits reads the register, clears the given bits, and writes it back. It
|
||||||
|
// is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// r.Reg &^= value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register32) ClearBits(value uint32) {
|
||||||
|
StoreUint32(&r.Reg, LoadUint32(&r.Reg)&^value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasBits reads the register and then checks to see if the passed bits are set. It
|
||||||
|
// is the volatile equivalent of:
|
||||||
|
//
|
||||||
|
// (*r.Reg & value) > 0
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func (r *Register32) HasBits(value uint32) bool {
|
||||||
|
return (r.Get() & value) > 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package syscall
|
||||||
|
|
||||||
|
func Exit(code int)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package testing
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is a sad stub of the upstream testing package because it doesn't compile
|
||||||
|
with tinygo right now.
|
||||||
|
*/
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// T is a test helper.
|
||||||
|
type T struct {
|
||||||
|
name string
|
||||||
|
output io.Writer
|
||||||
|
|
||||||
|
// flags the test as having failed when non-zero
|
||||||
|
failed int
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestToCall is a reference to a test that should be called during a test suite run.
|
||||||
|
type TestToCall struct {
|
||||||
|
// Name of the test to call.
|
||||||
|
Name string
|
||||||
|
// Function reference to the test.
|
||||||
|
Func func(*T)
|
||||||
|
}
|
||||||
|
|
||||||
|
// M is a test suite.
|
||||||
|
type M struct {
|
||||||
|
// tests is a list of the test names to execute
|
||||||
|
Tests []TestToCall
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the test suite.
|
||||||
|
func (m *M) Run() int {
|
||||||
|
failures := 0
|
||||||
|
for _, test := range m.Tests {
|
||||||
|
t := &T{
|
||||||
|
name: test.Name,
|
||||||
|
output: &bytes.Buffer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("=== RUN %s\n", test.Name)
|
||||||
|
test.Func(t)
|
||||||
|
|
||||||
|
if t.failed == 0 {
|
||||||
|
fmt.Printf("--- PASS: %s\n", test.Name)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("--- FAIL: %s\n", test.Name)
|
||||||
|
}
|
||||||
|
fmt.Println(t.output)
|
||||||
|
|
||||||
|
failures += t.failed
|
||||||
|
}
|
||||||
|
|
||||||
|
if failures > 0 {
|
||||||
|
fmt.Printf("exit status %d\n", failures)
|
||||||
|
fmt.Println("FAIL")
|
||||||
|
}
|
||||||
|
return failures
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *M) {
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error is equivalent to Log followed by Fail
|
||||||
|
func (t *T) Error(args ...interface{}) {
|
||||||
|
// This doesn't print the same as in upstream go, but works good enough
|
||||||
|
// TODO: buffer test output like go does
|
||||||
|
fmt.Fprintf(t.output, "\t")
|
||||||
|
fmt.Fprintln(t.output, args...)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *T) Fail() {
|
||||||
|
t.failed = 1
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user