mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 15:18:41 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c68da89af | |||
| 4424fe087d | |||
| 34939ab422 | |||
| c56b2a45fa | |||
| b1c70d85f7 | |||
| 714d98354c | |||
| 6e8df2fc40 | |||
| 902f40867f | |||
| 5438f16fcb | |||
| 7f027ddd33 | |||
| acaf096586 | |||
| 942d4903ce | |||
| 0b212cf2f6 | |||
| 2d5bc836f5 | |||
| 856e5fa179 | |||
| 07733ca056 | |||
| 92d9b780b5 | |||
| da345e8723 | |||
| fab38a0749 | |||
| 0a3dbbd1cb | |||
| 4c29f0fdb6 | |||
| fbc2099ee3 | |||
| 95d895646a | |||
| 35fb594f8f | |||
| 01f6aff422 | |||
| 7dd5839f47 | |||
| 403fee7e06 | |||
| 7657238c24 | |||
| 3cba36f2ba | |||
| 93d5269fef | |||
| 4b477fad55 | |||
| 6360e318a7 | |||
| 0757eb5919 | |||
| 013a71aa3d | |||
| f0091b31b5 | |||
| 709a296150 | |||
| f7b2a2c977 | |||
| 6ae4b43eb2 | |||
| bece6b9648 | |||
| 553f00bdb8 | |||
| a789108926 | |||
| d63ce0646c | |||
| 003211b4ff | |||
| e6720d7ddc | |||
| dfef168139 | |||
| 101f2e519b | |||
| eb34afde4b | |||
| 63f2a3dfe9 | |||
| fb23e9c212 | |||
| 222c4c75b1 | |||
| 5a509f5bfe | |||
| 2e46275c45 | |||
| 1d34f868da | |||
| f0904779a5 | |||
| 70f1064f36 |
+49
-22
@@ -1,16 +1,42 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11"
|
||||
matrix:
|
||||
include:
|
||||
- dist: xenial
|
||||
go: "1.11"
|
||||
- os: osx
|
||||
go: "1.11"
|
||||
env: PATH="/usr/local/opt/llvm/bin:$PATH"
|
||||
|
||||
before_install:
|
||||
- echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-7 main" | sudo tee -a /etc/apt/sources.list
|
||||
- echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" | sudo tee -a /etc/apt/sources.list
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install llvm-7-dev clang-7 libclang-7-dev binutils-arm-none-eabi qemu-system-arm --allow-unauthenticated -y
|
||||
- sudo ln -s /usr/bin/clang-7 /usr/local/bin/cc # work around missing -no-pie in old GCC version
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:ubuntu-toolchain-r'
|
||||
- sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-7 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
packages:
|
||||
- llvm-7-dev
|
||||
- clang-7
|
||||
- libclang-7-dev
|
||||
- gcc-arm-linux-gnueabi
|
||||
- binutils-arm-none-eabi
|
||||
- libc6-dev-armel-cross
|
||||
- gcc-aarch64-linux-gnu
|
||||
- libc6-dev-arm64-cross
|
||||
- qemu-system-arm
|
||||
- qemu-user
|
||||
- gcc-avr
|
||||
- avr-libc
|
||||
homebrew:
|
||||
update: true
|
||||
taps: ArmMbed/homebrew-formulae
|
||||
packages:
|
||||
- llvm@7
|
||||
- qemu
|
||||
- arm-none-eabi-gcc
|
||||
|
||||
install:
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then mkdir -p /Users/travis/gopath/bin; fi
|
||||
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
- dep ensure --vendor-only
|
||||
|
||||
@@ -18,17 +44,18 @@ script:
|
||||
- go install github.com/tinygo-org/tinygo
|
||||
- go test -v .
|
||||
- make gen-device
|
||||
- tinygo build -o blinky1.nrf.elf -target=pca10040 examples/blinky1
|
||||
- tinygo build -o blinky2.nrf.elf -target=pca10040 examples/blinky2
|
||||
- tinygo build -o blinky2 examples/blinky2
|
||||
- tinygo build -o test.nrf.elf -target=pca10040 examples/test
|
||||
- tinygo build -o blinky1.nrf51.elf -target=microbit examples/echo
|
||||
- tinygo build -o test.nrf.elf -target=nrf52840-mdk examples/blinky1
|
||||
- tinygo build -o blinky1.nrf51d.elf -target=pca10031 examples/blinky1
|
||||
- tinygo build -o blinky1.stm32.elf -target=bluepill examples/blinky1
|
||||
- tinygo build -o blinky1.avr.o -target=arduino examples/blinky1 # TODO: avr-as/avr-gcc doesn't work
|
||||
- tinygo build -o blinky1.reel.elf -target=reelboard examples/blinky1
|
||||
- tinygo build -o blinky2.reel.elf -target=reelboard examples/blinky2
|
||||
- tinygo build -o blinky1.pca10056.elf -target=pca10056 examples/blinky1
|
||||
- tinygo build -o blinky2.pca10056.elf -target=pca10056 examples/blinky2
|
||||
- tinygo build -o blinky1.samd21.elf -target=itsybitsy-m0 examples/blinky1
|
||||
- tinygo build -size short -o blinky1.nrf.elf -target=pca10040 examples/blinky1
|
||||
- tinygo build -size short -o blinky2.nrf.elf -target=pca10040 examples/blinky2
|
||||
- tinygo build -o blinky2 examples/blinky2 # TODO: re-enable -size flag with MachO support
|
||||
- tinygo build -size short -o test.nrf.elf -target=pca10040 examples/test
|
||||
- tinygo build -size short -o blinky1.nrf51.elf -target=microbit examples/echo
|
||||
- tinygo build -size short -o test.nrf.elf -target=nrf52840-mdk examples/blinky1
|
||||
- tinygo build -size short -o blinky1.nrf51d.elf -target=pca10031 examples/blinky1
|
||||
- tinygo build -size short -o blinky1.stm32.elf -target=bluepill examples/blinky1
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then tinygo build -size short -o blinky1.avr.elf -target=arduino examples/blinky1; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then tinygo build -size short -o blinky1.avr.elf -target=digispark examples/blinky1; fi
|
||||
- tinygo build -size short -o blinky1.reel.elf -target=reelboard examples/blinky1
|
||||
- tinygo build -size short -o blinky2.reel.elf -target=reelboard examples/blinky2
|
||||
- tinygo build -size short -o blinky1.pca10056.elf -target=pca10056 examples/blinky1
|
||||
- tinygo build -size short -o blinky2.pca10056.elf -target=pca10056 examples/blinky2
|
||||
- tinygo build -size short -o blinky1.samd21.elf -target=itsybitsy-m0 examples/blinky1
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
0.3.0
|
||||
---
|
||||
- **compiler**
|
||||
- remove old `-initinterp` flag
|
||||
- add support for macOS
|
||||
- **cgo**
|
||||
- add support for bool/float/complex types
|
||||
- **standard library**
|
||||
- `device/arm`: add support to disable/enable hardware interrupts
|
||||
- `machine`: add CPU frequency for nrf-based boards
|
||||
- `syscall`: add support for darwin/amd64
|
||||
- **targets**
|
||||
- `circuitplay_express`: add support for this board
|
||||
- `microbit`: add regular pin constants
|
||||
- `samd21`: fix time function for goroutine support
|
||||
- `samd21`: add support for USB-CDC (serial over USB)
|
||||
- `samd21`: add support for pins in port B
|
||||
- `samd21`: add support for pullup and pulldown pins
|
||||
- `wasm`: add support for Safari in example
|
||||
|
||||
|
||||
0.2.0
|
||||
---
|
||||
- **command line**
|
||||
- add version subcommand
|
||||
- **compiler**
|
||||
- fix a bug in floating point comparisons with NaN values
|
||||
- fix a bug when calling `panic` in package initialization code
|
||||
- add support for comparing `complex64` and `complex128`
|
||||
- **cgo**
|
||||
- add support for external globals
|
||||
- add support for pointers and function pointers
|
||||
- **standard library**
|
||||
- `fmt`: initial support, `fmt.Println` works
|
||||
- `math`: support for most/all functions
|
||||
- `os`: initial support (only stdin/stdout/stderr)
|
||||
- `reflect`: initial support
|
||||
- `syscall`: add support for amd64, arm, and arm64
|
||||
@@ -0,0 +1,48 @@
|
||||
# How to contribute
|
||||
|
||||
Thank you for your interest in improving TinyGo.
|
||||
|
||||
We would like your help to make this project better, so we appreciate any contributions. See if one of the following descriptions matches your situation:
|
||||
|
||||
### New to TinyGo
|
||||
|
||||
We'd love to get your feedback on getting started with TinyGo. Run into any difficulty, confusion, or anything else? You are not alone. We want to know about your experience, so we can help the next people. Please open a Github issue with your questions, or you can also get in touch directly with us on our Slack channel at [https://gophers.slack.com/messages/CDJD3SUP6](https://gophers.slack.com/messages/CDJD3SUP6).
|
||||
|
||||
### Something in TinyGo is not working as you expect
|
||||
|
||||
Please open a Github issue with your problem, and we will be happy to assist.
|
||||
|
||||
### Something in Go that you want/need does not appear to be in TinyGo
|
||||
|
||||
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.
|
||||
|
||||
A long tail of small (and large) language features haven't been implemented yet. In almost all cases, the compiler will show a `todo:` error from `compiler/compiler.go` when you try to use it. You can try implementing it, or open a bug report with a small code sample that fails to compile.
|
||||
|
||||
### Some specific hardware you want to use does not appear to be in TinyGo
|
||||
|
||||
As above, we probably have not implemented it yet. Your contribution adding the hardware support to TinyGo would be greatly appreciated.
|
||||
|
||||
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.
|
||||
|
||||
## How to use our Github repository
|
||||
|
||||
The `master` branch of this repo will always have the latest released version of TinyGo. All of the active development work for the next release will take place in the `dev` branch. TinyGo will use semantic versioning and will create a tag/release for each release.
|
||||
|
||||
Here is how to contribute back some code or documentation:
|
||||
|
||||
- Fork repo
|
||||
- Create a feature branch off of the `dev` branch
|
||||
- Make some useful change
|
||||
- Make sure the tests still pass
|
||||
- Submit a pull request against the `dev` branch.
|
||||
- Be kind
|
||||
|
||||
## How to run tests
|
||||
|
||||
To run the tests:
|
||||
|
||||
```
|
||||
make test
|
||||
```
|
||||
+4
-1
@@ -4,12 +4,15 @@ FROM golang:latest AS tinygo-base
|
||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||
echo "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-7 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y llvm-7-dev libclang-7-dev
|
||||
apt-get install -y llvm-7-dev libclang-7-dev git
|
||||
|
||||
RUN wget -O- https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
|
||||
COPY . /go/src/github.com/tinygo-org/tinygo
|
||||
|
||||
RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
||||
git submodule update --init
|
||||
|
||||
RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
||||
dep ensure --vendor-only && \
|
||||
go install /go/src/github.com/tinygo-org/tinygo/
|
||||
|
||||
Generated
+5
-4
@@ -3,7 +3,7 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:84316faef4ea12d34dde3b3e6dab682715a23b1c2bb8ab82cec9ab619766e214"
|
||||
digest = "1:ba70784a3deee74c0ca3c87bcac3c2f93d3b2d27d8f237b768c358b45ba47da8"
|
||||
name = "golang.org/x/tools"
|
||||
packages = [
|
||||
"go/ast/astutil",
|
||||
@@ -11,20 +11,21 @@
|
||||
"go/types/typeutil",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3e7aa9e59977626dc60433e9aeadf1bb63d28295"
|
||||
revision = "3744606dbb67b99c60d3f11cb10bd3f9e6dad472"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3611159788efdd4e0cfae18b6ebcccbad25a2815968b0e4323b42647d201031a"
|
||||
digest = "1:a6a25fd8906c74978f1ed811bc9fd3422da8093be863b458874b02a782b6ae3e"
|
||||
name = "tinygo.org/x/go-llvm"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "f420620d1a0f54417a5712260153fe861780d030"
|
||||
revision = "d5f730401f5069618b275a5241c6417eb0c38a65"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"golang.org/x/tools/go/ast/astutil",
|
||||
"golang.org/x/tools/go/ssa",
|
||||
"tinygo.org/x/go-llvm",
|
||||
]
|
||||
|
||||
@@ -82,7 +82,7 @@ clean:
|
||||
@rm -rf build
|
||||
|
||||
fmt:
|
||||
@go fmt . ./compiler ./interp ./loader ./ir ./src/device/arm ./src/examples/* ./src/machine ./src/runtime ./src/sync
|
||||
@go fmt . ./compiler ./interp ./loader ./ir ./src/device/arm ./src/examples/* ./src/machine ./src/os ./src/runtime ./src/sync
|
||||
@go fmt ./testdata/*.go
|
||||
|
||||
test:
|
||||
@@ -108,7 +108,7 @@ gen-device-stm32:
|
||||
go fmt ./src/device/stm32
|
||||
|
||||
# Build the Go compiler.
|
||||
tinygo:
|
||||
build/tinygo:
|
||||
@mkdir -p build
|
||||
go build -o build/tinygo .
|
||||
|
||||
|
||||
@@ -1,145 +1,70 @@
|
||||
# TinyGo - Go compiler for microcontrollers
|
||||
# TinyGo - Go compiler for small places
|
||||
|
||||
[](https://travis-ci.com/tinygo-org/tinygo)
|
||||
[](https://travis-ci.com/tinygo-org/tinygo)
|
||||
|
||||
> We never expected Go to be an embedded language and so it's got serious
|
||||
> problems [...].
|
||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (WASM), and command-line tools.
|
||||
|
||||
-- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799)
|
||||
It reuses libraries used by the [Go language tools](https://golang.org/pkg/go/) alongside [LLVM](http://llvm.org) to provide an alternative way to compile programs written in the Go programming language.
|
||||
|
||||
TinyGo is a project to bring Go to microcontrollers and small systems with a
|
||||
single processor core. It is similar to [emgo](https://github.com/ziutek/emgo)
|
||||
but a major difference is that I want to keep the Go memory model (which implies
|
||||
garbage collection of some sort). Another difference is that TinyGo uses LLVM
|
||||
internally instead of emitting C, which hopefully leads to smaller and more
|
||||
efficient code and certainly leads to more flexibility.
|
||||
|
||||
My original reasoning was: if [Python](https://micropython.org/) can run on
|
||||
microcontrollers, then certainly [Go](https://golang.org/) should be able to and
|
||||
run on even lower level micros.
|
||||
|
||||
Example program (blinky):
|
||||
Here is an example program that blinks the built-in LED when run directly on any supported board with onboard LED:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
led := machine.GPIO{machine.LED}
|
||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
for {
|
||||
led.Low()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
led := machine.GPIO{machine.LED}
|
||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
||||
for {
|
||||
led.Low()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
led.High()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
led.High()
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Currently supported features:
|
||||
The above program can be compiled and run without modification on an Arduino Uno, an Adafruit ItsyBitsy M0, or any of the supported boards that have a built-in LED, just by setting the correct TinyGo compiler target. For example, this compiles and flashes an Arduino Uno:
|
||||
|
||||
* control flow
|
||||
* many (but not all) basic types: most ints, floats, strings, structs
|
||||
* function calling
|
||||
* interfaces for basic types (with type switches and asserts)
|
||||
* goroutines (very initial support)
|
||||
* function pointers (non-blocking)
|
||||
* interface methods
|
||||
* standard library (but most packages won't work due to missing language
|
||||
features)
|
||||
* slices (partially)
|
||||
* maps (very rough, unfinished)
|
||||
* defer
|
||||
* closures
|
||||
* bound methods
|
||||
* complex numbers (except for arithmetic)
|
||||
* channels (with some limitations)
|
||||
|
||||
Not yet supported:
|
||||
|
||||
* select
|
||||
* complex arithmetic
|
||||
* garbage collection
|
||||
* recover
|
||||
* introspection (if it ever gets implemented)
|
||||
* ...
|
||||
```shell
|
||||
tinygo flash -target arduino examples/blinky1
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
See the [getting started instructions](https://tinygo.org/getting-started/).
|
||||
See the [getting started instructions](https://tinygo.org/getting-started/) for information on how to install TinyGo, as well as how to run the TinyGo compiler using our Docker container.
|
||||
|
||||
### Running with Docker
|
||||
## Supported boards/targets
|
||||
|
||||
A docker container exists for easy access to the `tinygo` CLI:
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
```sh
|
||||
$ docker run --rm -v $(pwd):/src tinygo/tinygo tinygo build -o /src/wasm.wasm -target wasm examples/wasm
|
||||
```
|
||||
The following microcontroller boards are currently supported:
|
||||
|
||||
Note that you cannot run `tinygo flash` from inside the docker container,
|
||||
so it is less useful for microcontroller development.
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
* [BBC:Microbit](https://microbit.org/)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
* [Digispark](http://digistump.com/products/1)
|
||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||
|
||||
## Supported targets
|
||||
For more information, see [this list of boards](https://tinygo.org/microcontrollers/). Pull requests for additional support are welcome!
|
||||
|
||||
The following architectures/systems are currently supported:
|
||||
## Currently supported features:
|
||||
|
||||
* ARM (Cortex-M)
|
||||
* AVR (Arduino Uno)
|
||||
* Linux
|
||||
* WebAssembly
|
||||
|
||||
For more information, see [this list of targets and
|
||||
boards](https://tinygo.org/targets/). Pull requests for
|
||||
broader support are welcome!
|
||||
|
||||
## Analysis and optimizations
|
||||
|
||||
The goal is to reduce code size (and increase performance) by performing all
|
||||
kinds of whole-program analysis passes. The official Go compiler doesn't do a
|
||||
whole lot of analysis (except for escape analysis) because it needs to be fast,
|
||||
but embedded programs are necessarily smaller so it becomes practical. And I
|
||||
think especially program size can be reduced by a large margin when actually
|
||||
trying to optimize for it.
|
||||
|
||||
Implemented compiler passes:
|
||||
|
||||
* Analyse which functions are blocking. Blocking functions are functions that
|
||||
call sleep, chan send, etc. Its parents are also blocking.
|
||||
* Analyse whether the scheduler is needed. It is only needed when there are
|
||||
`go` statements for blocking functions.
|
||||
* Analyse whether a given type switch or type assert is possible with
|
||||
[type-based alias analysis](https://en.wikipedia.org/wiki/Alias_analysis#Type-based_alias_analysis).
|
||||
I would like to use flow-based alias analysis in the future, if feasible.
|
||||
* Do basic dead code elimination of functions. This pass makes later passes
|
||||
better and probably improves compile time as well.
|
||||
|
||||
## Scope
|
||||
|
||||
Goals:
|
||||
|
||||
* Have very small binary sizes. Don't pay for what you don't use.
|
||||
* Support for most common microcontroller boards.
|
||||
* Be usable on the web using WebAssembly.
|
||||
* Good CGo support, with no more overhead than a regular function call.
|
||||
* Support most standard library packages and compile most Go code without
|
||||
modification.
|
||||
|
||||
Non-goals:
|
||||
|
||||
* Using more than one core.
|
||||
* Be efficient while using zillions of goroutines. However, good goroutine
|
||||
support is certainly a goal.
|
||||
* Be as fast as `gc`. However, LLVM will probably be better at optimizing
|
||||
certain things so TinyGo might actually turn out to be faster for number
|
||||
crunching.
|
||||
* Be able to compile every Go program out there.
|
||||
For a description of currently supported Go language features, please see [https://tinygo.org/lang-support/](https://tinygo.org/lang-support/).
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is currently maintained on a dedicated web site located at [https://tinygo.org/](https://tinygo.org/).
|
||||
Documentation is located on our web site at [https://tinygo.org/](https://tinygo.org/).
|
||||
|
||||
You can find the web site code at [https://github.com/tinygo-org/tinygo-site](https://github.com/tinygo-org/tinygo-site).
|
||||
|
||||
@@ -154,26 +79,37 @@ should arrive fairly quickly (under 1 min): https://invite.slack.golangbridge.or
|
||||
|
||||
## Contributing
|
||||
|
||||
Patches are welcome!
|
||||
Your contributions are welcome!
|
||||
|
||||
If you want to contribute, here are some suggestions:
|
||||
Please take a look at our [CONTRIBUTING.md](./CONTRIBUTING.md) document for details.
|
||||
|
||||
* 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.
|
||||
* 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 and many don't have an
|
||||
implementation yet in the `machine` package. Adding support for new
|
||||
peripherals is very useful.
|
||||
* Just raising bugs for things you'd like to see implemented is also a form of
|
||||
contributing! It helps prioritization.
|
||||
## Project Scope
|
||||
|
||||
Goals:
|
||||
|
||||
* Have very small binary sizes. Don't pay for what you don't use.
|
||||
* Support for most common microcontroller boards.
|
||||
* Be usable on the web using WebAssembly.
|
||||
* Good CGo support, with no more overhead than a regular function call.
|
||||
* Support most standard library packages and compile most Go code without modification.
|
||||
|
||||
Non-goals:
|
||||
|
||||
* Using more than one core.
|
||||
* Be efficient while using zillions of goroutines. However, good goroutine support is certainly a goal.
|
||||
* Be as fast as `gc`. However, LLVM will probably be better at optimizing certain things so TinyGo might actually turn out to be faster for number crunching.
|
||||
* Be able to compile every Go program out there.
|
||||
|
||||
## Why this project exists
|
||||
|
||||
> We never expected Go to be an embedded language and so its got serious problems...
|
||||
|
||||
-- Rob Pike, [GopherCon 2014 Opening Keynote](https://www.youtube.com/watch?v=VoS7DsT1rdM&feature=youtu.be&t=2799)
|
||||
|
||||
TinyGo is a project to bring Go to microcontrollers and small systems with a single processor core. It is similar to [emgo](https://github.com/ziutek/emgo) but a major difference is that we want to keep the Go memory model (which implies garbage collection of some sort). Another difference is that TinyGo uses LLVM internally instead of emitting C, which hopefully leads to smaller and more efficient code and certainly leads to more flexibility.
|
||||
|
||||
The original reasoning was: if [Python](https://micropython.org/) can run on microcontrollers, then certainly [Go](https://golang.org/) should be able to run on even lower level micros.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the BSD 3-clause license, just like the
|
||||
[Go project](https://golang.org/LICENSE) itself.
|
||||
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
|
||||
|
||||
+125
-409
@@ -30,19 +30,18 @@ func init() {
|
||||
|
||||
// Configure the compiler.
|
||||
type Config struct {
|
||||
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
||||
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
|
||||
GOOS string //
|
||||
GOARCH string //
|
||||
GC string // garbage collection strategy
|
||||
CFlags []string // cflags to pass to cgo
|
||||
LDFlags []string // ldflags to pass to cgo
|
||||
DumpSSA bool // dump Go SSA, for compiler debugging
|
||||
Debug bool // add debug symbols for gdb
|
||||
RootDir string // GOROOT for TinyGo
|
||||
GOPATH string // GOPATH, like `go env GOPATH`
|
||||
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
|
||||
InitInterp bool // use new init interpretation, meaning the old one is disabled
|
||||
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
||||
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
|
||||
GOOS string //
|
||||
GOARCH string //
|
||||
GC string // garbage collection strategy
|
||||
CFlags []string // cflags to pass to cgo
|
||||
LDFlags []string // ldflags to pass to cgo
|
||||
DumpSSA bool // dump Go SSA, for compiler debugging
|
||||
Debug bool // add debug symbols for gdb
|
||||
RootDir string // GOROOT for TinyGo
|
||||
GOPATH string // GOPATH, like `go env GOPATH`
|
||||
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
@@ -110,7 +109,9 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
|
||||
c.mod.SetTarget(config.Triple)
|
||||
c.mod.SetDataLayout(c.targetData.String())
|
||||
c.builder = c.ctx.NewBuilder()
|
||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||
if c.Debug {
|
||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||
}
|
||||
|
||||
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
||||
if c.targetData.PointerSize() <= 4 {
|
||||
@@ -214,13 +215,15 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||
c.ir.SimpleDCE()
|
||||
|
||||
// Initialize debug information.
|
||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
Language: llvm.DW_LANG_Go,
|
||||
File: mainPath,
|
||||
Dir: "",
|
||||
Producer: "TinyGo",
|
||||
Optimized: true,
|
||||
})
|
||||
if c.Debug {
|
||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
Language: llvm.DW_LANG_Go,
|
||||
File: mainPath,
|
||||
Dir: "",
|
||||
Producer: "TinyGo",
|
||||
Optimized: true,
|
||||
})
|
||||
}
|
||||
|
||||
var frames []*Frame
|
||||
|
||||
@@ -246,8 +249,7 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Declare all globals. These will get an initializer when parsing "package
|
||||
// initializer" functions.
|
||||
// Declare all globals.
|
||||
for _, g := range c.ir.Globals {
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
llvmType, err := c.getLLVMType(typ)
|
||||
@@ -278,54 +280,18 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||
frames = append(frames, frame)
|
||||
}
|
||||
|
||||
// Find and interpret package initializers.
|
||||
// Add definitions to declarations.
|
||||
for _, frame := range frames {
|
||||
if frame.fn.Synthetic == "package initializer" {
|
||||
c.initFuncs = append(c.initFuncs, frame.fn.LLVMFn)
|
||||
// Try to interpret as much as possible of the init() function.
|
||||
// Whenever it hits an instruction that it doesn't understand, it
|
||||
// bails out and leaves the rest to the compiler (so initialization
|
||||
// continues at runtime).
|
||||
// This should only happen when it hits a function call or the end
|
||||
// of the block, ideally.
|
||||
if !c.InitInterp {
|
||||
err := c.ir.Interpret(frame.fn.Blocks[0], c.DumpSSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = c.parseFunc(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set values for globals (after package initializer has been interpreted).
|
||||
for _, g := range c.ir.Globals {
|
||||
if g.Initializer() == nil {
|
||||
continue
|
||||
}
|
||||
err := c.parseGlobalInitializer(g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add definitions to declarations.
|
||||
for _, frame := range frames {
|
||||
if frame.fn.CName() != "" {
|
||||
continue
|
||||
}
|
||||
if frame.fn.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
var err error
|
||||
if frame.fn.Synthetic == "package initializer" {
|
||||
continue // already done
|
||||
} else {
|
||||
err = c.parseFunc(frame)
|
||||
}
|
||||
err := c.parseFunc(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -373,14 +339,16 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||
c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.ExternalLinkage)
|
||||
|
||||
// see: https://reviews.llvm.org/D18355
|
||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
c.ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
||||
llvm.GlobalContext().MDString("Debug Info Version"),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||
}),
|
||||
)
|
||||
c.dibuilder.Finalize()
|
||||
if c.Debug {
|
||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
c.ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
||||
llvm.GlobalContext().MDString("Debug Info Version"),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||
}),
|
||||
)
|
||||
c.dibuilder.Finalize()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -412,9 +380,9 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
|
||||
case types.Float64:
|
||||
return c.ctx.DoubleType(), nil
|
||||
case types.Complex64:
|
||||
return llvm.VectorType(c.ctx.FloatType(), 2), nil
|
||||
return c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false), nil
|
||||
case types.Complex128:
|
||||
return llvm.VectorType(c.ctx.DoubleType(), 2), nil
|
||||
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false), nil
|
||||
case types.String, types.UntypedString:
|
||||
return c.mod.GetTypeByName("runtime._string"), nil
|
||||
case types.Uintptr:
|
||||
@@ -561,16 +529,6 @@ func (c *Compiler) getZeroValue(typ llvm.Type) (llvm.Value, error) {
|
||||
} else {
|
||||
return c.ctx.ConstStruct(vals, false), nil
|
||||
}
|
||||
case llvm.VectorTypeKind:
|
||||
zero, err := c.getZeroValue(typ.ElementType())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
vals := make([]llvm.Value, typ.VectorSize())
|
||||
for i := range vals {
|
||||
vals[i] = zero
|
||||
}
|
||||
return llvm.ConstVector(vals, false), nil
|
||||
default:
|
||||
return llvm.Value{}, errors.New("todo: LLVM zero initializer: " + typ.String())
|
||||
}
|
||||
@@ -729,282 +687,6 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
||||
return difunc, nil
|
||||
}
|
||||
|
||||
// Create a new global hashmap bucket, for map initialization.
|
||||
func (c *Compiler) initMapNewBucket(prefix string, mapType *types.Map) (llvm.Value, uint64, uint64, error) {
|
||||
llvmKeyType, err := c.getLLVMType(mapType.Key().Underlying())
|
||||
if err != nil {
|
||||
return llvm.Value{}, 0, 0, err
|
||||
}
|
||||
llvmValueType, err := c.getLLVMType(mapType.Elem().Underlying())
|
||||
if err != nil {
|
||||
return llvm.Value{}, 0, 0, err
|
||||
}
|
||||
keySize := c.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||
bucketType := c.ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(c.ctx.Int8Type(), 8), // tophash
|
||||
c.i8ptrType, // next bucket
|
||||
llvm.ArrayType(llvmKeyType, 8), // key type
|
||||
llvm.ArrayType(llvmValueType, 8), // value type
|
||||
}, false)
|
||||
bucketValue, err := c.getZeroValue(bucketType)
|
||||
if err != nil {
|
||||
return llvm.Value{}, 0, 0, err
|
||||
}
|
||||
bucket := llvm.AddGlobal(c.mod, bucketType, prefix+"$hashmap$bucket")
|
||||
bucket.SetInitializer(bucketValue)
|
||||
bucket.SetLinkage(llvm.InternalLinkage)
|
||||
return bucket, keySize, valueSize, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) parseGlobalInitializer(g *ir.Global) error {
|
||||
if g.IsExtern() {
|
||||
return nil
|
||||
}
|
||||
llvmValue, err := c.getInterpretedValue(g.LinkName(), g.Initializer())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.LLVMGlobal.SetInitializer(llvmValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Turn a computed Value type (ConstValue, ArrayValue, etc.) into a LLVM value.
|
||||
// This is used to set the initializer of globals after they have been
|
||||
// calculated by the package initializer interpreter.
|
||||
func (c *Compiler) getInterpretedValue(prefix string, value ir.Value) (llvm.Value, error) {
|
||||
switch value := value.(type) {
|
||||
case *ir.ArrayValue:
|
||||
vals := make([]llvm.Value, len(value.Elems))
|
||||
for i, elem := range value.Elems {
|
||||
val, err := c.getInterpretedValue(prefix+"$arrayval", elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
vals[i] = val
|
||||
}
|
||||
subTyp, err := c.getLLVMType(value.ElemType)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstArray(subTyp, vals), nil
|
||||
|
||||
case *ir.ConstValue:
|
||||
return c.parseConst(prefix, value.Expr)
|
||||
|
||||
case *ir.FunctionValue:
|
||||
if value.Elem == nil {
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return c.getZeroValue(llvmType)
|
||||
}
|
||||
fn := c.ir.GetFunction(value.Elem)
|
||||
ptr := fn.LLVMFn
|
||||
// Create closure value: {context, function pointer}
|
||||
ptr = c.ctx.ConstStruct([]llvm.Value{llvm.ConstPointerNull(c.i8ptrType), ptr}, false)
|
||||
return ptr, nil
|
||||
|
||||
case *ir.GlobalValue:
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
ptr := llvm.ConstInBoundsGEP(value.Global.LLVMGlobal, []llvm.Value{zero})
|
||||
return ptr, nil
|
||||
|
||||
case *ir.MapValue:
|
||||
// Create initial bucket.
|
||||
firstBucketGlobal, keySize, valueSize, err := c.initMapNewBucket(prefix, value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
|
||||
// Insert each key/value pair in the hashmap.
|
||||
bucketGlobal := firstBucketGlobal
|
||||
for i, key := range value.Keys {
|
||||
llvmKey, err := c.getInterpretedValue(prefix, key)
|
||||
if err != nil {
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
llvmValue, err := c.getInterpretedValue(prefix, value.Values[i])
|
||||
if err != nil {
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
|
||||
constVal := key.(*ir.ConstValue).Expr
|
||||
var keyBuf []byte
|
||||
switch constVal.Type().Underlying().(*types.Basic).Kind() {
|
||||
case types.String, types.UntypedString:
|
||||
keyBuf = []byte(constant.StringVal(constVal.Value))
|
||||
case types.Int:
|
||||
keyBuf = make([]byte, c.targetData.TypeAllocSize(c.intType))
|
||||
n, _ := constant.Uint64Val(constVal.Value)
|
||||
for i := range keyBuf {
|
||||
keyBuf[i] = byte(n)
|
||||
n >>= 8
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, errors.New("todo: init: map key not implemented: " + constVal.Type().Underlying().String())
|
||||
}
|
||||
hash := hashmapHash(keyBuf)
|
||||
|
||||
if i%8 == 0 && i != 0 {
|
||||
// Bucket is full, create a new one.
|
||||
newBucketGlobal, _, _, err := c.initMapNewBucket(prefix, value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
newBucketPtr := llvm.ConstInBoundsGEP(newBucketGlobal, []llvm.Value{zero})
|
||||
newBucketPtrCast := llvm.ConstBitCast(newBucketPtr, c.i8ptrType)
|
||||
// insert pointer into old bucket
|
||||
bucket := bucketGlobal.Initializer()
|
||||
bucket = llvm.ConstInsertValue(bucket, newBucketPtrCast, []uint32{1})
|
||||
bucketGlobal.SetInitializer(bucket)
|
||||
// switch to next bucket
|
||||
bucketGlobal = newBucketGlobal
|
||||
}
|
||||
|
||||
tophashValue := llvm.ConstInt(c.ctx.Int8Type(), uint64(hashmapTopHash(hash)), false)
|
||||
bucket := bucketGlobal.Initializer()
|
||||
bucket = llvm.ConstInsertValue(bucket, tophashValue, []uint32{0, uint32(i % 8)})
|
||||
bucket = llvm.ConstInsertValue(bucket, llvmKey, []uint32{2, uint32(i % 8)})
|
||||
bucket = llvm.ConstInsertValue(bucket, llvmValue, []uint32{3, uint32(i % 8)})
|
||||
bucketGlobal.SetInitializer(bucket)
|
||||
}
|
||||
|
||||
// Create the hashmap itself.
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
bucketPtr := llvm.ConstInBoundsGEP(firstBucketGlobal, []llvm.Value{zero})
|
||||
hashmapType := c.mod.GetTypeByName("runtime.hashmap")
|
||||
hashmap := llvm.ConstNamedStruct(hashmapType, []llvm.Value{
|
||||
llvm.ConstPointerNull(llvm.PointerType(hashmapType, 0)), // next
|
||||
llvm.ConstBitCast(bucketPtr, c.i8ptrType), // buckets
|
||||
llvm.ConstInt(c.uintptrType, uint64(len(value.Keys)), false), // count
|
||||
llvm.ConstInt(c.ctx.Int8Type(), keySize, false), // keySize
|
||||
llvm.ConstInt(c.ctx.Int8Type(), valueSize, false), // valueSize
|
||||
llvm.ConstInt(c.ctx.Int8Type(), 0, false), // bucketBits
|
||||
})
|
||||
|
||||
// Create a pointer to this hashmap.
|
||||
hashmapPtr := llvm.AddGlobal(c.mod, hashmap.Type(), prefix+"$hashmap")
|
||||
hashmapPtr.SetInitializer(hashmap)
|
||||
hashmapPtr.SetLinkage(llvm.InternalLinkage)
|
||||
return llvm.ConstInBoundsGEP(hashmapPtr, []llvm.Value{zero}), nil
|
||||
|
||||
case *ir.PointerBitCastValue:
|
||||
elem, err := c.getInterpretedValue(prefix, value.Elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstBitCast(elem, llvmType), nil
|
||||
|
||||
case *ir.PointerToUintptrValue:
|
||||
elem, err := c.getInterpretedValue(prefix, value.Elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstPtrToInt(elem, c.uintptrType), nil
|
||||
|
||||
case *ir.PointerValue:
|
||||
if value.Elem == nil {
|
||||
typ, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstPointerNull(typ), nil
|
||||
}
|
||||
elem, err := c.getInterpretedValue(prefix, *value.Elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
|
||||
obj := llvm.AddGlobal(c.mod, elem.Type(), prefix+"$ptrvalue")
|
||||
obj.SetInitializer(elem)
|
||||
obj.SetLinkage(llvm.InternalLinkage)
|
||||
elem = obj
|
||||
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
ptr := llvm.ConstInBoundsGEP(elem, []llvm.Value{zero})
|
||||
return ptr, nil
|
||||
|
||||
case *ir.SliceValue:
|
||||
var globalPtr llvm.Value
|
||||
var arrayLength uint64
|
||||
if value.Array == nil {
|
||||
arrayType, err := c.getLLVMType(value.Type.Elem())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
globalPtr = llvm.ConstPointerNull(llvm.PointerType(arrayType, 0))
|
||||
} else {
|
||||
// make array
|
||||
array, err := c.getInterpretedValue(prefix, value.Array)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
// make global from array
|
||||
global := llvm.AddGlobal(c.mod, array.Type(), prefix+"$array")
|
||||
global.SetInitializer(array)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
|
||||
// get pointer to global
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
globalPtr = c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
|
||||
arrayLength = uint64(len(value.Array.Elems))
|
||||
}
|
||||
|
||||
// make slice
|
||||
sliceTyp, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
llvmLen := llvm.ConstInt(c.uintptrType, arrayLength, false)
|
||||
slice := llvm.ConstNamedStruct(sliceTyp, []llvm.Value{
|
||||
globalPtr, // ptr
|
||||
llvmLen, // len
|
||||
llvmLen, // cap
|
||||
})
|
||||
return slice, nil
|
||||
|
||||
case *ir.StructValue:
|
||||
fields := make([]llvm.Value, len(value.Fields))
|
||||
for i, elem := range value.Fields {
|
||||
field, err := c.getInterpretedValue(prefix, elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
fields[i] = field
|
||||
}
|
||||
switch value.Type.(type) {
|
||||
case *types.Named:
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstNamedStruct(llvmType, fields), nil
|
||||
case *types.Struct:
|
||||
return c.ctx.ConstStruct(fields, false), nil
|
||||
default:
|
||||
return llvm.Value{}, errors.New("init: unknown struct type: " + value.Type.String())
|
||||
}
|
||||
|
||||
case *ir.ZeroBasicValue:
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return c.getZeroValue(llvmType)
|
||||
|
||||
default:
|
||||
return llvm.Value{}, errors.New("init: unknown initializer type: " + fmt.Sprintf("%#v", value))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) parseFunc(frame *Frame) error {
|
||||
if c.DumpSSA {
|
||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||
@@ -1189,10 +871,6 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||
switch instr := instr.(type) {
|
||||
case ssa.Value:
|
||||
value, err := c.parseExpr(frame, instr)
|
||||
if err == ir.ErrCGoWrapper {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil
|
||||
}
|
||||
frame.locals[instr] = value
|
||||
return err
|
||||
case *ssa.DebugRef:
|
||||
@@ -1303,10 +981,6 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||
return c.emitChanSend(frame, instr)
|
||||
case *ssa.Store:
|
||||
llvmAddr, err := c.parseExpr(frame, instr.Addr)
|
||||
if err == ir.ErrCGoWrapper {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1319,7 +993,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||
return nil
|
||||
}
|
||||
store := c.builder.CreateStore(llvmVal, llvmAddr)
|
||||
valType := instr.Addr.Type().(*types.Pointer).Elem()
|
||||
valType := instr.Addr.Type().Underlying().(*types.Pointer).Elem()
|
||||
if c.ir.IsVolatile(valType) {
|
||||
// Volatile store, for memory-mapped registers.
|
||||
store.SetVolatile(true)
|
||||
@@ -1390,14 +1064,14 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string,
|
||||
var cplx llvm.Value
|
||||
switch t.Kind() {
|
||||
case types.Float32:
|
||||
cplx = llvm.Undef(llvm.VectorType(c.ctx.FloatType(), 2))
|
||||
cplx = llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
case types.Float64:
|
||||
cplx = llvm.Undef(llvm.VectorType(c.ctx.DoubleType(), 2))
|
||||
cplx = llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "unsupported type in complex builtin: "+t.String())
|
||||
}
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
case "copy":
|
||||
dst, err := c.parseExpr(frame, args[0])
|
||||
@@ -1432,8 +1106,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string,
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
index := llvm.ConstInt(c.ctx.Int32Type(), 1, false)
|
||||
return c.builder.CreateExtractElement(cplx, index, "imag"), nil
|
||||
return c.builder.CreateExtractValue(cplx, 1, "imag"), nil
|
||||
case "len":
|
||||
value, err := c.parseExpr(frame, args[0])
|
||||
if err != nil {
|
||||
@@ -1522,8 +1195,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string,
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
index := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
return c.builder.CreateExtractElement(cplx, index, "real"), nil
|
||||
return c.builder.CreateExtractValue(cplx, 0, "real"), nil
|
||||
case "recover":
|
||||
return c.createRuntimeCall("_recover", nil, ""), nil
|
||||
case "ssa:wrapnilchk":
|
||||
@@ -1680,6 +1352,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
||||
return c.builder.CreateCall(target, args, ""), nil
|
||||
}
|
||||
|
||||
switch fn.RelString(nil) {
|
||||
case "syscall.Syscall", "syscall.Syscall6", "syscall.Syscall9":
|
||||
return c.emitSyscall(frame, instr)
|
||||
}
|
||||
|
||||
targetFunc := c.ir.GetFunction(fn)
|
||||
if targetFunc.LLVMFn.IsNil() {
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName())
|
||||
@@ -1901,10 +1578,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
fn.LLVMFn,
|
||||
}, false), nil
|
||||
case *ssa.Global:
|
||||
if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return llvm.Value{}, ir.ErrCGoWrapper
|
||||
}
|
||||
value := c.ir.GetGlobal(expr).LLVMGlobal
|
||||
if value.IsNil() {
|
||||
return llvm.Value{}, c.makeError(expr.Pos(), "global not found: "+c.ir.GetGlobal(expr).LinkName())
|
||||
@@ -1946,7 +1619,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
var bufptr, buflen llvm.Value
|
||||
switch ptrTyp := expr.X.Type().Underlying().(type) {
|
||||
case *types.Pointer:
|
||||
typ := expr.X.Type().(*types.Pointer).Elem().Underlying()
|
||||
typ := expr.X.Type().Underlying().(*types.Pointer).Elem().Underlying()
|
||||
switch typ := typ.(type) {
|
||||
case *types.Array:
|
||||
bufptr = val
|
||||
@@ -2022,7 +1695,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return c.parseMakeInterface(val, expr.X.Type(), "", expr.Pos())
|
||||
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos())
|
||||
case *ssa.MakeMap:
|
||||
mapType := expr.Type().Underlying().(*types.Map)
|
||||
llvmKeyType, err := c.getLLVMType(mapType.Key().Underlying())
|
||||
@@ -2400,7 +2073,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
return c.builder.CreateICmp(llvm.IntUGE, x, y, ""), nil
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop on integer: "+op.String())
|
||||
panic("binop on integer: " + op.String())
|
||||
}
|
||||
} else if typ.Info()&types.IsFloat != 0 {
|
||||
// Operations on floats
|
||||
@@ -2413,22 +2086,38 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
return c.builder.CreateFMul(x, y, ""), nil
|
||||
case token.QUO: // /
|
||||
return c.builder.CreateFDiv(x, y, ""), nil
|
||||
case token.REM: // %
|
||||
return c.builder.CreateFRem(x, y, ""), nil
|
||||
case token.EQL: // ==
|
||||
return c.builder.CreateFCmp(llvm.FloatOEQ, x, y, ""), nil
|
||||
return c.builder.CreateFCmp(llvm.FloatUEQ, x, y, ""), nil
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateFCmp(llvm.FloatONE, x, y, ""), nil
|
||||
return c.builder.CreateFCmp(llvm.FloatUNE, x, y, ""), nil
|
||||
case token.LSS: // <
|
||||
return c.builder.CreateFCmp(llvm.FloatOLT, x, y, ""), nil
|
||||
return c.builder.CreateFCmp(llvm.FloatULT, x, y, ""), nil
|
||||
case token.LEQ: // <=
|
||||
return c.builder.CreateFCmp(llvm.FloatOLE, x, y, ""), nil
|
||||
return c.builder.CreateFCmp(llvm.FloatULE, x, y, ""), nil
|
||||
case token.GTR: // >
|
||||
return c.builder.CreateFCmp(llvm.FloatOGT, x, y, ""), nil
|
||||
return c.builder.CreateFCmp(llvm.FloatUGT, x, y, ""), nil
|
||||
case token.GEQ: // >=
|
||||
return c.builder.CreateFCmp(llvm.FloatOGE, x, y, ""), nil
|
||||
return c.builder.CreateFCmp(llvm.FloatUGE, x, y, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop on float: "+op.String())
|
||||
panic("binop on float: " + op.String())
|
||||
}
|
||||
} else if typ.Info()&types.IsComplex != 0 {
|
||||
r1 := c.builder.CreateExtractValue(x, 0, "r1")
|
||||
r2 := c.builder.CreateExtractValue(y, 0, "r2")
|
||||
i1 := c.builder.CreateExtractValue(x, 1, "i1")
|
||||
i2 := c.builder.CreateExtractValue(y, 1, "i2")
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
|
||||
ieq := c.builder.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
|
||||
return c.builder.CreateAnd(req, ieq, ""), nil
|
||||
case token.NEQ: // !=
|
||||
req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
|
||||
ieq := c.builder.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
|
||||
neq := c.builder.CreateAnd(req, ieq, "")
|
||||
return c.builder.CreateNot(neq, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop on complex number: "+op.String())
|
||||
}
|
||||
} else if typ.Info()&types.IsBoolean != 0 {
|
||||
// Operations on booleans
|
||||
@@ -2438,7 +2127,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop on boolean: "+op.String())
|
||||
panic("binop on bool: " + op.String())
|
||||
}
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
// Operations on pointers
|
||||
@@ -2448,7 +2137,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
case token.NEQ: // !=
|
||||
return c.builder.CreateICmp(llvm.IntNE, x, y, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop on pointer: "+op.String())
|
||||
panic("binop on pointer: " + op.String())
|
||||
}
|
||||
} else if typ.Info()&types.IsString != 0 {
|
||||
// Operations on strings
|
||||
@@ -2471,7 +2160,7 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
case token.GEQ: // >=
|
||||
return c.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop on string: "+op.String())
|
||||
panic("binop on string: " + op.String())
|
||||
}
|
||||
} else {
|
||||
return llvm.Value{}, c.makeError(pos, "todo: unknown basic type in binop: "+typ.String())
|
||||
@@ -2550,7 +2239,6 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String())
|
||||
}
|
||||
return result, nil
|
||||
case *types.Struct:
|
||||
// Compare each struct field and combine the result. From the spec:
|
||||
// Struct values are comparable if all their fields are comparable.
|
||||
@@ -2579,7 +2267,6 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String())
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop type: "+typ.String())
|
||||
}
|
||||
@@ -2627,6 +2314,19 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error
|
||||
} else if typ.Info()&types.IsFloat != 0 {
|
||||
n, _ := constant.Float64Val(expr.Value)
|
||||
return llvm.ConstFloat(llvmType, n), nil
|
||||
} else if typ.Kind() == types.Complex64 {
|
||||
r, err := c.parseConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
i, err := c.parseConst(prefix, ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
} else if typ.Kind() == types.Complex128 {
|
||||
r, err := c.parseConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||
if err != nil {
|
||||
@@ -2636,13 +2336,19 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
cplx := llvm.Undef(llvm.VectorType(c.ctx.DoubleType(), 2))
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
} else {
|
||||
return llvm.Value{}, errors.New("todo: unknown constant: " + expr.String())
|
||||
}
|
||||
case *types.Chan:
|
||||
sig, err := c.getLLVMType(expr.Type())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return c.getZeroValue(sig)
|
||||
case *types.Signature:
|
||||
if expr.Value != nil {
|
||||
return llvm.Value{}, errors.New("non-nil signature constant")
|
||||
@@ -2800,25 +2506,25 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p
|
||||
|
||||
if typeFrom.Kind() == types.Complex128 && typeTo.Kind() == types.Complex64 {
|
||||
// Conversion from complex128 to complex64.
|
||||
r := c.builder.CreateExtractElement(value, llvm.ConstInt(c.ctx.Int32Type(), 0, false), "real.f64")
|
||||
i := c.builder.CreateExtractElement(value, llvm.ConstInt(c.ctx.Int32Type(), 1, false), "imag.f64")
|
||||
r := c.builder.CreateExtractValue(value, 0, "real.f64")
|
||||
i := c.builder.CreateExtractValue(value, 1, "imag.f64")
|
||||
r = c.builder.CreateFPTrunc(r, c.ctx.FloatType(), "real.f32")
|
||||
i = c.builder.CreateFPTrunc(i, c.ctx.FloatType(), "imag.f32")
|
||||
cplx := llvm.Undef(llvm.VectorType(c.ctx.FloatType(), 2))
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
}
|
||||
|
||||
if typeFrom.Kind() == types.Complex64 && typeTo.Kind() == types.Complex128 {
|
||||
// Conversion from complex64 to complex128.
|
||||
r := c.builder.CreateExtractElement(value, llvm.ConstInt(c.ctx.Int32Type(), 0, false), "real.f32")
|
||||
i := c.builder.CreateExtractElement(value, llvm.ConstInt(c.ctx.Int32Type(), 1, false), "imag.f32")
|
||||
r := c.builder.CreateExtractValue(value, 0, "real.f32")
|
||||
i := c.builder.CreateExtractValue(value, 1, "imag.f32")
|
||||
r = c.builder.CreateFPExt(r, c.ctx.DoubleType(), "real.f64")
|
||||
i = c.builder.CreateFPExt(i, c.ctx.DoubleType(), "imag.f64")
|
||||
cplx := llvm.Undef(llvm.VectorType(c.ctx.DoubleType(), 2))
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
}
|
||||
|
||||
@@ -2938,10 +2644,20 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
||||
return llvm.Value{}, c.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
|
||||
}
|
||||
case token.MUL: // *x, dereference pointer
|
||||
valType := unop.X.Type().(*types.Pointer).Elem()
|
||||
valType := unop.X.Type().Underlying().(*types.Pointer).Elem()
|
||||
if c.targetData.TypeAllocSize(x.Type().ElementType()) == 0 {
|
||||
// zero-length data
|
||||
return c.getZeroValue(x.Type().ElementType())
|
||||
} else if strings.HasSuffix(unop.X.String(), "$funcaddr") {
|
||||
// CGo function pointer. The cgo part has rewritten CGo function
|
||||
// pointers as stub global variables of the form:
|
||||
// var C.add unsafe.Pointer
|
||||
// Instead of a load from the global, create a bitcast of the
|
||||
// function pointer itself.
|
||||
global := c.ir.GetGlobal(unop.X.(*ssa.Global))
|
||||
name := global.LinkName()[:len(global.LinkName())-len("$funcaddr")]
|
||||
fn := c.mod.NamedFunction(name)
|
||||
return c.builder.CreateBitCast(fn, c.i8ptrType, ""), nil
|
||||
} else {
|
||||
load := c.builder.CreateLoad(x, "")
|
||||
if c.ir.IsVolatile(valType) {
|
||||
|
||||
@@ -104,15 +104,6 @@ func (t *typeInfo) getMethod(signature *signatureInfo) *methodInfo {
|
||||
panic("could not find method")
|
||||
}
|
||||
|
||||
// id returns the fully-qualified type name including import path, removing the
|
||||
// $type suffix.
|
||||
func (t *typeInfo) id() string {
|
||||
if !strings.HasSuffix(t.name, "$type") {
|
||||
panic("concrete type does not have $type suffix: " + t.name)
|
||||
}
|
||||
return t.name[:len(t.name)-len("$type")]
|
||||
}
|
||||
|
||||
// typeInfoSlice implements sort.Slice, sorting the most commonly used types
|
||||
// first.
|
||||
type typeInfoSlice []*typeInfo
|
||||
@@ -415,7 +406,7 @@ func (p *lowerInterfacesPass) run() {
|
||||
for _, t := range p.types {
|
||||
typeSlice = append(typeSlice, t)
|
||||
}
|
||||
sort.Sort(typeSlice)
|
||||
sort.Sort(sort.Reverse(typeSlice))
|
||||
|
||||
// A type code must fit in 16 bits.
|
||||
if len(typeSlice) >= 1<<16 {
|
||||
@@ -423,9 +414,7 @@ func (p *lowerInterfacesPass) run() {
|
||||
}
|
||||
|
||||
// Assign a type code for each type.
|
||||
for i, t := range typeSlice {
|
||||
t.num = uint64(i + 1)
|
||||
}
|
||||
p.assignTypeCodes(typeSlice)
|
||||
|
||||
// Replace each call to runtime.makeInterface with the constant type code.
|
||||
for _, use := range makeInterfaceUses {
|
||||
@@ -691,7 +680,7 @@ func (p *lowerInterfacesPass) createInterfaceMethodFunc(itf *interfaceInfo, sign
|
||||
|
||||
// Define all possible functions that can be called.
|
||||
for _, typ := range itf.types {
|
||||
bb := llvm.AddBasicBlock(fn, typ.id())
|
||||
bb := llvm.AddBasicBlock(fn, typ.name)
|
||||
sw.AddCase(llvm.ConstInt(p.uintptrType, typ.num, false), bb)
|
||||
|
||||
// The function we will redirect to when the interface has this type.
|
||||
|
||||
+106
-22
@@ -8,6 +8,8 @@ package compiler
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/ir"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
@@ -20,28 +22,17 @@ import (
|
||||
// value field.
|
||||
//
|
||||
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global string, pos token.Pos) (llvm.Value, error) {
|
||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) (llvm.Value, error) {
|
||||
var itfValue llvm.Value
|
||||
size := c.targetData.TypeAllocSize(val.Type())
|
||||
if size > c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||
if global != "" {
|
||||
// Allocate in a global variable.
|
||||
global := llvm.AddGlobal(c.mod, val.Type(), global+"$itfvalue")
|
||||
global.SetInitializer(val)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
itfValueRaw := llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
|
||||
itfValue = llvm.ConstBitCast(itfValueRaw, c.i8ptrType)
|
||||
} else {
|
||||
// Allocate on the heap and put a pointer in the interface.
|
||||
// TODO: escape analysis.
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "makeinterface.alloc")
|
||||
itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "makeinterface.cast.value")
|
||||
c.builder.CreateStore(val, itfValueCast)
|
||||
itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "makeinterface.cast.i8ptr")
|
||||
}
|
||||
// Allocate on the heap and put a pointer in the interface.
|
||||
// TODO: escape analysis.
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "makeinterface.alloc")
|
||||
itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "makeinterface.cast.value")
|
||||
c.builder.CreateStore(val, itfValueCast)
|
||||
itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "makeinterface.cast.i8ptr")
|
||||
} else if size == 0 {
|
||||
itfValue = llvm.ConstPointerNull(c.i8ptrType)
|
||||
} else {
|
||||
@@ -51,7 +42,7 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global str
|
||||
itfValue = c.builder.CreateIntToPtr(val, c.i8ptrType, "makeinterface.cast.int")
|
||||
case llvm.PointerTypeKind:
|
||||
itfValue = c.builder.CreateBitCast(val, c.i8ptrType, "makeinterface.cast.ptr")
|
||||
case llvm.StructTypeKind:
|
||||
case llvm.StructTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
// A bitcast would be useful here, but bitcast doesn't allow
|
||||
// aggregate types. So we'll bitcast it using an alloca.
|
||||
// Hopefully this will get optimized away.
|
||||
@@ -79,14 +70,107 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global str
|
||||
// It returns a pointer to an external global which should be replaced with the
|
||||
// real type in the interface lowering pass.
|
||||
func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
|
||||
global := c.mod.NamedGlobal(typ.String() + "$type")
|
||||
globalName := "type:" + getTypeCodeName(typ)
|
||||
global := c.mod.NamedGlobal(globalName)
|
||||
if global.IsNil() {
|
||||
global = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), typ.String()+"$type")
|
||||
global = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName)
|
||||
global.SetGlobalConstant(true)
|
||||
}
|
||||
return global
|
||||
}
|
||||
|
||||
// getTypeCodeName returns a name for this type that can be used in the
|
||||
// interface lowering pass to assign type codes as expected by the reflect
|
||||
// package. See getTypeCodeNum.
|
||||
func getTypeCodeName(t types.Type) string {
|
||||
name := ""
|
||||
if named, ok := t.(*types.Named); ok {
|
||||
name = "~" + named.String() + ":"
|
||||
t = t.Underlying()
|
||||
}
|
||||
switch t := t.(type) {
|
||||
case *types.Array:
|
||||
return "array:" + name + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
|
||||
case *types.Basic:
|
||||
var kind string
|
||||
switch t.Kind() {
|
||||
case types.Bool:
|
||||
kind = "bool"
|
||||
case types.Int:
|
||||
kind = "int"
|
||||
case types.Int8:
|
||||
kind = "int8"
|
||||
case types.Int16:
|
||||
kind = "int16"
|
||||
case types.Int32:
|
||||
kind = "int32"
|
||||
case types.Int64:
|
||||
kind = "int64"
|
||||
case types.Uint:
|
||||
kind = "uint"
|
||||
case types.Uint8:
|
||||
kind = "uint8"
|
||||
case types.Uint16:
|
||||
kind = "uint16"
|
||||
case types.Uint32:
|
||||
kind = "uint32"
|
||||
case types.Uint64:
|
||||
kind = "uint64"
|
||||
case types.Uintptr:
|
||||
kind = "uintptr"
|
||||
case types.Float32:
|
||||
kind = "float32"
|
||||
case types.Float64:
|
||||
kind = "float64"
|
||||
case types.Complex64:
|
||||
kind = "complex64"
|
||||
case types.Complex128:
|
||||
kind = "complex128"
|
||||
case types.String:
|
||||
kind = "string"
|
||||
case types.UnsafePointer:
|
||||
kind = "unsafeptr"
|
||||
default:
|
||||
panic("unknown basic type: " + t.Name())
|
||||
}
|
||||
return "basic:" + name + kind
|
||||
case *types.Chan:
|
||||
return "chan:" + name + getTypeCodeName(t.Elem())
|
||||
case *types.Interface:
|
||||
methods := make([]string, t.NumMethods())
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
methods[i] = getTypeCodeName(t.Method(i).Type())
|
||||
}
|
||||
return "interface:" + name + "{" + strings.Join(methods, ",") + "}"
|
||||
case *types.Map:
|
||||
keyType := getTypeCodeName(t.Key())
|
||||
elemType := getTypeCodeName(t.Elem())
|
||||
return "map:" + name + "{" + keyType + "," + elemType + "}"
|
||||
case *types.Pointer:
|
||||
return "pointer:" + name + getTypeCodeName(t.Elem())
|
||||
case *types.Signature:
|
||||
params := make([]string, t.Params().Len())
|
||||
for i := 0; i < t.Params().Len(); i++ {
|
||||
params[i] = getTypeCodeName(t.Params().At(i).Type())
|
||||
}
|
||||
results := make([]string, t.Results().Len())
|
||||
for i := 0; i < t.Results().Len(); i++ {
|
||||
results[i] = getTypeCodeName(t.Results().At(i).Type())
|
||||
}
|
||||
return "func:" + name + "{" + strings.Join(params, ",") + "}{" + strings.Join(results, ",") + "}"
|
||||
case *types.Slice:
|
||||
return "slice:" + name + getTypeCodeName(t.Elem())
|
||||
case *types.Struct:
|
||||
elems := make([]string, t.NumFields())
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
elems[i] = getTypeCodeName(t.Field(i).Type())
|
||||
}
|
||||
return "struct:" + name + "{" + strings.Join(elems, ",") + "}"
|
||||
default:
|
||||
panic("unknown type: " + t.String())
|
||||
}
|
||||
}
|
||||
|
||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
||||
// method set should be unreferenced after the interface lowering pass.
|
||||
func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var basicTypes = map[string]int64{
|
||||
"bool": 1,
|
||||
"int": 2,
|
||||
"int8": 3,
|
||||
"int16": 4,
|
||||
"int32": 5,
|
||||
"int64": 6,
|
||||
"uint": 7,
|
||||
"uint8": 8,
|
||||
"uint16": 9,
|
||||
"uint32": 10,
|
||||
"uint64": 11,
|
||||
"uintptr": 12,
|
||||
"float32": 13,
|
||||
"float64": 14,
|
||||
"complex64": 15,
|
||||
"complex128": 16,
|
||||
"string": 17,
|
||||
"unsafeptr": 18,
|
||||
}
|
||||
|
||||
func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
|
||||
fn := c.mod.NamedFunction("reflect.ValueOf")
|
||||
if fn.IsNil() {
|
||||
// reflect.ValueOf is never used, so we can use the most efficient
|
||||
// encoding possible.
|
||||
for i, t := range typeSlice {
|
||||
t.num = uint64(i + 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Assign typecodes the way the reflect package expects.
|
||||
fallbackIndex := 1
|
||||
namedTypes := make(map[string]int)
|
||||
for _, t := range typeSlice {
|
||||
if t.name[:5] != "type:" {
|
||||
panic("expected type name to start with 'type:'")
|
||||
}
|
||||
num := c.getTypeCodeNum(t.name[5:], &fallbackIndex, namedTypes)
|
||||
if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() {
|
||||
// TODO: support this in some way, using a side table for example.
|
||||
// That's less efficient but better than not working at all.
|
||||
// Particularly important on systems with 16-bit pointers (e.g.
|
||||
// AVR).
|
||||
panic("compiler: could not store type code number inside interface type code")
|
||||
}
|
||||
t.num = num.Uint64()
|
||||
}
|
||||
}
|
||||
|
||||
// getTypeCodeNum returns the typecode for a given type as expected by the
|
||||
// reflect package. Also see getTypeCodeName, which serializes types to a string
|
||||
// based on a types.Type value for this function.
|
||||
func (c *Compiler) getTypeCodeNum(id string, fallbackIndex *int, namedTypes map[string]int) *big.Int {
|
||||
// Note: see src/reflect/type.go for bit allocations.
|
||||
// A type can be named or unnamed. Example of both:
|
||||
// basic:~foo:uint64
|
||||
// basic:uint64
|
||||
// Extract the class (basic, slice, pointer, etc.), the name, and the
|
||||
// contents of this type ID string. Allocate bits based on that, as
|
||||
// src/runtime/types.go expects.
|
||||
class := id[:strings.IndexByte(id, ':')]
|
||||
value := id[len(class)+1:]
|
||||
name := ""
|
||||
if value[0] == '~' {
|
||||
name = value[1:strings.IndexByte(value, ':')]
|
||||
value = value[len(name)+2:]
|
||||
}
|
||||
if class == "basic" {
|
||||
// Basic types follow the following bit pattern:
|
||||
// ...xxxxx0
|
||||
// where xxxxx is allocated for the 18 possible basic types and all the
|
||||
// upper bits are used to indicate the named type.
|
||||
num, ok := basicTypes[value]
|
||||
if !ok {
|
||||
panic("invalid basic type: " + id)
|
||||
}
|
||||
if name != "" {
|
||||
// This type is named, set the upper bits to the name ID.
|
||||
num |= int64(getNamedTypeNum(namedTypes, name)) << 5
|
||||
}
|
||||
return big.NewInt(num << 1)
|
||||
} else {
|
||||
// Complex types use the following bit pattern:
|
||||
// ...nxxx1
|
||||
// where xxx indicates the complex type (any non-basic type). The upper
|
||||
// bits contain whatever the type contains. Types that wrap a single
|
||||
// other type (channel, interface, pointer, slice) just contain the bits
|
||||
// of the wrapped type. Other types (like struct) have a different
|
||||
// method of encoding the contents of the type.
|
||||
var num *big.Int
|
||||
var classNumber int64
|
||||
switch class {
|
||||
case "chan":
|
||||
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
|
||||
classNumber = 0
|
||||
case "interface":
|
||||
num = big.NewInt(int64(*fallbackIndex))
|
||||
*fallbackIndex++
|
||||
classNumber = 1
|
||||
case "pointer":
|
||||
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
|
||||
classNumber = 2
|
||||
case "slice":
|
||||
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
|
||||
classNumber = 3
|
||||
case "array":
|
||||
num = big.NewInt(int64(*fallbackIndex))
|
||||
*fallbackIndex++
|
||||
classNumber = 4
|
||||
case "func":
|
||||
num = big.NewInt(int64(*fallbackIndex))
|
||||
*fallbackIndex++
|
||||
classNumber = 5
|
||||
case "map":
|
||||
num = big.NewInt(int64(*fallbackIndex))
|
||||
*fallbackIndex++
|
||||
classNumber = 6
|
||||
case "struct":
|
||||
num = big.NewInt(int64(*fallbackIndex))
|
||||
*fallbackIndex++
|
||||
classNumber = 7
|
||||
default:
|
||||
panic("unknown type kind: " + id)
|
||||
}
|
||||
if name == "" {
|
||||
num.Lsh(num, 5).Or(num, big.NewInt((classNumber<<1)+1))
|
||||
} else {
|
||||
// TODO: store num in a sidetable
|
||||
num = big.NewInt(int64(getNamedTypeNum(namedTypes, name))<<1 | 1)
|
||||
num.Lsh(num, 4).Or(num, big.NewInt((classNumber<<1)+1))
|
||||
}
|
||||
return num
|
||||
}
|
||||
}
|
||||
|
||||
// getNamedTypeNum returns an appropriate (unique) number for the given named
|
||||
// type. If the name already has a number that number is returned, else a new
|
||||
// number is returned. The number is always non-zero.
|
||||
func getNamedTypeNum(namedTypes map[string]int, name string) int {
|
||||
if num, ok := namedTypes[name]; ok {
|
||||
return num
|
||||
} else {
|
||||
num = len(namedTypes) + 1
|
||||
namedTypes[name] = num
|
||||
return num
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package compiler
|
||||
|
||||
// This file implements the syscall.Syscall and syscall.Syscall6 instructions as
|
||||
// compiler builtins.
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// emitSyscall emits an inline system call instruction, depending on the target
|
||||
// OS/arch.
|
||||
func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
|
||||
num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value)
|
||||
var syscallResult llvm.Value
|
||||
switch {
|
||||
case c.GOARCH == "amd64":
|
||||
if c.GOOS == "darwin" {
|
||||
// Darwin adds this magic number to system call numbers:
|
||||
//
|
||||
// > Syscall classes for 64-bit system call entry.
|
||||
// > For 64-bit users, the 32-bit syscall number is partitioned
|
||||
// > with the high-order bits representing the class and low-order
|
||||
// > bits being the syscall number within that class.
|
||||
// > The high-order 32-bits of the 64-bit syscall number are unused.
|
||||
// > All system classes enter the kernel via the syscall instruction.
|
||||
//
|
||||
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
||||
num += 0x2000000
|
||||
}
|
||||
// Sources:
|
||||
// https://stackoverflow.com/a/2538212
|
||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
||||
args := []llvm.Value{llvm.ConstInt(c.uintptrType, num, false)}
|
||||
argTypes := []llvm.Type{c.uintptrType}
|
||||
// Constraints will look something like:
|
||||
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
||||
constraints := "={rax},0"
|
||||
for i, arg := range call.Args[1:] {
|
||||
constraints += "," + [...]string{
|
||||
"{rdi}",
|
||||
"{rsi}",
|
||||
"{rdx}",
|
||||
"{r10}",
|
||||
"{r8}",
|
||||
"{r9}",
|
||||
"{r11}",
|
||||
"{r12}",
|
||||
"{r13}",
|
||||
}[i]
|
||||
llvmValue, err := c.parseExpr(frame, arg)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
constraints += ",~{rcx},~{r11}"
|
||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
syscallResult = c.builder.CreateCall(target, args, "")
|
||||
case c.GOARCH == "arm" && c.GOOS == "linux":
|
||||
// Implement the EABI system call convention for Linux.
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
// Constraints will look something like:
|
||||
// ={r0},0,{r1},{r2},{r7},~{r3}
|
||||
constraints := "={r0}"
|
||||
for i, arg := range call.Args[1:] {
|
||||
constraints += "," + [...]string{
|
||||
"0", // tie to output
|
||||
"{r1}",
|
||||
"{r2}",
|
||||
"{r3}",
|
||||
"{r4}",
|
||||
"{r5}",
|
||||
"{r6}",
|
||||
}[i]
|
||||
llvmValue, err := c.parseExpr(frame, arg)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
args = append(args, llvm.ConstInt(c.uintptrType, num, false))
|
||||
argTypes = append(argTypes, c.uintptrType)
|
||||
constraints += ",{r7}" // syscall number
|
||||
for i := len(call.Args) - 1; i < 4; i++ {
|
||||
// r0-r3 get clobbered after the syscall returns
|
||||
constraints += ",~{r" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
syscallResult = c.builder.CreateCall(target, args, "")
|
||||
case c.GOARCH == "arm64" && c.GOOS == "linux":
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
argTypes := []llvm.Type{}
|
||||
// Constraints will look something like:
|
||||
// ={x0},0,{x1},{x2},{x8},~{x3},~{x4},~{x5},~{x6},~{x7},~{x16},~{x17}
|
||||
constraints := "={x0}"
|
||||
for i, arg := range call.Args[1:] {
|
||||
constraints += "," + [...]string{
|
||||
"0", // tie to output
|
||||
"{x1}",
|
||||
"{x2}",
|
||||
"{x3}",
|
||||
"{x4}",
|
||||
"{x5}",
|
||||
}[i]
|
||||
llvmValue, err := c.parseExpr(frame, arg)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
args = append(args, llvmValue)
|
||||
argTypes = append(argTypes, llvmValue.Type())
|
||||
}
|
||||
args = append(args, llvm.ConstInt(c.uintptrType, num, false))
|
||||
argTypes = append(argTypes, c.uintptrType)
|
||||
constraints += ",{x8}" // syscall number
|
||||
for i := len(call.Args) - 1; i < 8; i++ {
|
||||
// x0-x7 may get clobbered during the syscall following the aarch64
|
||||
// calling convention.
|
||||
constraints += ",~{x" + strconv.Itoa(i) + "}"
|
||||
}
|
||||
constraints += ",~{x16},~{x17}" // scratch registers
|
||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
syscallResult = c.builder.CreateCall(target, args, "")
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH)
|
||||
}
|
||||
switch c.GOOS {
|
||||
case "linux":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
// if syscallResult < 0 && syscallResult > -4096 {
|
||||
// err = -syscallResult
|
||||
// }
|
||||
// return syscallResult, 0, err
|
||||
zero := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
|
||||
inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096
|
||||
hasError := c.builder.CreateAnd(inrange1, inrange2, "")
|
||||
errResult := c.builder.CreateSelect(hasError, c.builder.CreateSub(zero, syscallResult, ""), zero, "syscallError")
|
||||
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
|
||||
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
case "darwin":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
// if syscallResult != 0 {
|
||||
// err = syscallResult
|
||||
// }
|
||||
// return syscallResult, 0, err
|
||||
zero := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
hasError := c.builder.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
|
||||
errResult := c.builder.CreateSelect(hasError, syscallResult, zero, "syscallError")
|
||||
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
|
||||
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH)
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -308,7 +308,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
ret = llvm.ConstInsertValue(ret, retLen, []uint32{2}) // cap
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, ret}
|
||||
case callee.Name() == "runtime.makeInterface":
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstPtrToInt(inst.Operand(0), fr.TargetData.IntPtrType())}
|
||||
uintptrType := callee.Type().Context().IntType(fr.TargetData.PointerSize() * 8)
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstPtrToInt(inst.Operand(0), uintptrType)}
|
||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||
// This are all print instructions, which necessarily have side
|
||||
// effects but no results.
|
||||
|
||||
+3
-2
@@ -63,14 +63,15 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
||||
return errors.New("expected all instructions in " + name + " to be *.init() calls")
|
||||
}
|
||||
pkgName := initName[:len(initName)-5]
|
||||
_, err := e.Function(call.CalledValue(), []Value{&LocalValue{e, undefPtr}, &LocalValue{e, undefPtr}}, pkgName)
|
||||
fn := call.CalledValue()
|
||||
call.EraseFromParentAsInstruction()
|
||||
_, err := e.Function(fn, []Value{&LocalValue{e, undefPtr}, &LocalValue{e, undefPtr}}, pkgName)
|
||||
if err == ErrUnreachable {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+21
-7
@@ -24,6 +24,13 @@ type sideEffectResult struct {
|
||||
// returns whether this function has side effects and if it does, which globals
|
||||
// it mentions anywhere in this function or any called functions.
|
||||
func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
||||
switch fn.Name() {
|
||||
case "runtime.alloc":
|
||||
// Cannot be scanned but can be interpreted.
|
||||
return &sideEffectResult{severity: sideEffectNone}
|
||||
case "runtime._panic":
|
||||
return &sideEffectResult{severity: sideEffectLimited}
|
||||
}
|
||||
if e.sideEffectFuncs == nil {
|
||||
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
|
||||
}
|
||||
@@ -73,25 +80,32 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
||||
result.updateSeverity(sideEffectAll)
|
||||
continue
|
||||
}
|
||||
name := child.Name()
|
||||
if child.IsDeclaration() {
|
||||
if name == "runtime.makeInterface" {
|
||||
switch child.Name() {
|
||||
case "runtime.makeInterface":
|
||||
// Can be interpreted so does not have side effects.
|
||||
continue
|
||||
}
|
||||
// External function call. Assume only limited side effects
|
||||
// (no affected globals, etc.).
|
||||
if result.hasLocalSideEffects(dirtyLocals, inst) {
|
||||
if e.hasLocalSideEffects(dirtyLocals, inst) {
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
}
|
||||
continue
|
||||
}
|
||||
childSideEffects := e.hasSideEffects(fn)
|
||||
childSideEffects := e.hasSideEffects(child)
|
||||
switch childSideEffects.severity {
|
||||
case sideEffectInProgress, sideEffectNone:
|
||||
// no side effects or recursive function - continue scanning
|
||||
case sideEffectLimited:
|
||||
// The return value may be problematic.
|
||||
if e.hasLocalSideEffects(dirtyLocals, inst) {
|
||||
result.updateSeverity(sideEffectLimited)
|
||||
}
|
||||
case sideEffectAll:
|
||||
result.updateSeverity(sideEffectAll)
|
||||
default:
|
||||
result.update(childSideEffects)
|
||||
panic("unreachable")
|
||||
}
|
||||
case llvm.Load, llvm.Store:
|
||||
if inst.IsVolatile() {
|
||||
@@ -118,7 +132,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
||||
// hasLocalSideEffects checks whether the given instruction flows into a branch
|
||||
// or return instruction, in which case the whole function must be marked as
|
||||
// having side effects and be called at runtime.
|
||||
func (r *sideEffectResult) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct{}, inst llvm.Value) bool {
|
||||
func (e *Eval) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct{}, inst llvm.Value) bool {
|
||||
if _, ok := dirtyLocals[inst]; ok {
|
||||
// It is already known that this local is dirty.
|
||||
return true
|
||||
@@ -156,7 +170,7 @@ func (r *sideEffectResult) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct
|
||||
// For a list:
|
||||
// https://godoc.org/github.com/llvm-mirror/llvm/bindings/go/llvm#Opcode
|
||||
dirtyLocals[user] = struct{}{}
|
||||
if r.hasLocalSideEffects(dirtyLocals, user) {
|
||||
if e.hasLocalSideEffects(dirtyLocals, user) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,522 +0,0 @@
|
||||
package ir
|
||||
|
||||
// This file provides functionality to interpret very basic Go SSA, for
|
||||
// compile-time initialization of globals.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
var ErrCGoWrapper = errors.New("tinygo internal: cgo wrapper") // a signal, not an error
|
||||
|
||||
// Ignore these calls (replace with a zero return value) when encountered during
|
||||
// interpretation.
|
||||
var ignoreInitCalls = map[string]struct{}{
|
||||
"syscall.runtime_envs": struct{}{},
|
||||
"syscall/js.predefValue": struct{}{},
|
||||
"(syscall/js.Value).Get": struct{}{},
|
||||
"(syscall/js.Value).New": struct{}{},
|
||||
"(syscall/js.Value).Int": struct{}{},
|
||||
"os.init$1": struct{}{},
|
||||
}
|
||||
|
||||
// Interpret instructions as far as possible, and drop those instructions from
|
||||
// the basic block.
|
||||
func (p *Program) Interpret(block *ssa.BasicBlock, dumpSSA bool) error {
|
||||
if dumpSSA {
|
||||
fmt.Printf("\ninterpret: %s\n", block.Parent().Pkg.Pkg.Path())
|
||||
}
|
||||
for {
|
||||
i, err := p.interpret(block.Instrs, nil, nil, nil, dumpSSA)
|
||||
if err == ErrCGoWrapper {
|
||||
// skip this instruction
|
||||
block.Instrs = block.Instrs[i+1:]
|
||||
continue
|
||||
}
|
||||
block.Instrs = block.Instrs[i:]
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Interpret instructions as far as possible, and return the index of the first
|
||||
// unknown instruction.
|
||||
func (p *Program) interpret(instrs []ssa.Instruction, paramKeys []*ssa.Parameter, paramValues []Value, results []Value, dumpSSA bool) (int, error) {
|
||||
locals := map[ssa.Value]Value{}
|
||||
for i, key := range paramKeys {
|
||||
locals[key] = paramValues[i]
|
||||
}
|
||||
for i, instr := range instrs {
|
||||
if _, ok := instr.(*ssa.DebugRef); ok {
|
||||
continue
|
||||
}
|
||||
if dumpSSA {
|
||||
if val, ok := instr.(ssa.Value); ok && val.Name() != "" {
|
||||
fmt.Printf("\t%s: %s = %s\n", instr.Parent().RelString(nil), val.Name(), val.String())
|
||||
} else {
|
||||
fmt.Printf("\t%s: %s\n", instr.Parent().RelString(nil), instr.String())
|
||||
}
|
||||
}
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Alloc:
|
||||
alloc, err := p.getZeroValue(instr.Type().Underlying().(*types.Pointer).Elem())
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
locals[instr] = &PointerValue{nil, &alloc}
|
||||
case *ssa.BinOp:
|
||||
if typ, ok := instr.Type().(*types.Basic); ok && typ.Kind() == types.String {
|
||||
// Concatenate two strings.
|
||||
// This happens in the time package, for example.
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
y, err := p.getValue(instr.Y, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
xstr := constant.StringVal(x.(*ConstValue).Expr.Value)
|
||||
ystr := constant.StringVal(y.(*ConstValue).Expr.Value)
|
||||
locals[instr] = &ConstValue{ssa.NewConst(constant.MakeString(xstr+ystr), types.Typ[types.String])}
|
||||
} else {
|
||||
return i, errors.New("init: unknown binop: " + instr.String())
|
||||
}
|
||||
case *ssa.Call:
|
||||
common := instr.Common()
|
||||
callee := common.StaticCallee()
|
||||
if callee == nil {
|
||||
return i, nil // don't understand dynamic dispatch
|
||||
}
|
||||
if _, ok := ignoreInitCalls[callee.String()]; ok {
|
||||
// These calls are not needed and can be ignored, for the time
|
||||
// being.
|
||||
results := make([]Value, callee.Signature.Results().Len())
|
||||
for i := range results {
|
||||
var err error
|
||||
results[i], err = p.getZeroValue(callee.Signature.Results().At(i).Type())
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
if len(results) == 1 {
|
||||
locals[instr] = results[0]
|
||||
} else if len(results) > 1 {
|
||||
locals[instr] = &StructValue{Fields: results}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if callee.String() == "os.NewFile" {
|
||||
// Emulate the creation of os.Stdin, os.Stdout and os.Stderr.
|
||||
resultPtrType := callee.Signature.Results().At(0).Type().(*types.Pointer)
|
||||
resultStructOuterType := resultPtrType.Elem().Underlying().(*types.Struct)
|
||||
if resultStructOuterType.NumFields() != 1 {
|
||||
panic("expected 1 field in os.File struct")
|
||||
}
|
||||
fileInnerPtrType := resultStructOuterType.Field(0).Type().(*types.Pointer)
|
||||
fileInnerType := fileInnerPtrType.Elem().(*types.Named)
|
||||
fileInnerStructType := fileInnerType.Underlying().(*types.Struct)
|
||||
fileInner, err := p.getZeroValue(fileInnerType) // os.file
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
for fieldIndex := 0; fieldIndex < fileInnerStructType.NumFields(); fieldIndex++ {
|
||||
field := fileInnerStructType.Field(fieldIndex)
|
||||
if field.Name() == "name" {
|
||||
// Set the 'name' field.
|
||||
name, err := p.getValue(common.Args[1], locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
fileInner.(*StructValue).Fields[fieldIndex] = name
|
||||
} else if field.Type().String() == "internal/poll.FD" {
|
||||
// Set the file descriptor field.
|
||||
field := field.Type().Underlying().(*types.Struct)
|
||||
for subfieldIndex := 0; subfieldIndex < field.NumFields(); subfieldIndex++ {
|
||||
subfield := field.Field(subfieldIndex)
|
||||
if subfield.Name() == "Sysfd" {
|
||||
sysfd, err := p.getValue(common.Args[0], locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
sysfd = &ConstValue{Expr: ssa.NewConst(sysfd.(*ConstValue).Expr.Value, subfield.Type())}
|
||||
fileInner.(*StructValue).Fields[fieldIndex].(*StructValue).Fields[subfieldIndex] = sysfd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fileInnerPtr := &PointerValue{fileInnerPtrType, &fileInner} // *os.file
|
||||
var fileOuter Value = &StructValue{Type: resultPtrType.Elem(), Fields: []Value{fileInnerPtr}} // os.File
|
||||
result := &PointerValue{resultPtrType.Elem(), &fileOuter} // *os.File
|
||||
locals[instr] = result
|
||||
continue
|
||||
}
|
||||
if canInterpret(callee) {
|
||||
params := make([]Value, len(common.Args))
|
||||
for i, arg := range common.Args {
|
||||
val, err := p.getValue(arg, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
params[i] = val
|
||||
}
|
||||
results := make([]Value, callee.Signature.Results().Len())
|
||||
subi, err := p.interpret(callee.Blocks[0].Instrs, callee.Params, params, results, dumpSSA)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if subi != len(callee.Blocks[0].Instrs) {
|
||||
return i, errors.New("init: could not interpret all instructions of subroutine")
|
||||
}
|
||||
if len(results) == 1 {
|
||||
locals[instr] = results[0]
|
||||
} else {
|
||||
panic("unimplemented: not exactly 1 result")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if callee.Object() == nil || callee.Object().Name() == "init" {
|
||||
return i, nil // arrived at the init#num functions
|
||||
}
|
||||
return i, errors.New("todo: init call: " + callee.String())
|
||||
case *ssa.ChangeType:
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
// The only case when we need to bitcast is when casting between named
|
||||
// struct types, as those are actually different in LLVM. Let's just
|
||||
// bitcast all struct types for ease of use.
|
||||
if _, ok := instr.Type().Underlying().(*types.Struct); ok {
|
||||
return i, errors.New("todo: init: " + instr.String())
|
||||
}
|
||||
locals[instr] = x
|
||||
case *ssa.Convert:
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
typeFrom := instr.X.Type().Underlying()
|
||||
switch typeTo := instr.Type().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if typeTo.Kind() == types.String {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
if _, ok := typeFrom.(*types.Pointer); ok && typeTo.Kind() == types.UnsafePointer {
|
||||
locals[instr] = &PointerBitCastValue{typeTo, x}
|
||||
} else if typeFrom, ok := typeFrom.(*types.Basic); ok {
|
||||
if typeFrom.Kind() == types.UnsafePointer && typeTo.Kind() == types.Uintptr {
|
||||
locals[instr] = &PointerToUintptrValue{x}
|
||||
} else if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsInteger != 0 {
|
||||
locals[instr] = &ConstValue{Expr: ssa.NewConst(x.(*ConstValue).Expr.Value, typeTo)}
|
||||
} else {
|
||||
return i, nil
|
||||
}
|
||||
} else {
|
||||
return i, nil
|
||||
}
|
||||
case *types.Pointer:
|
||||
if typeFrom, ok := typeFrom.(*types.Basic); ok && typeFrom.Kind() == types.UnsafePointer {
|
||||
locals[instr] = &PointerBitCastValue{typeTo, x}
|
||||
} else {
|
||||
panic("expected unsafe pointer conversion")
|
||||
}
|
||||
default:
|
||||
return i, nil
|
||||
}
|
||||
case *ssa.DebugRef:
|
||||
// ignore
|
||||
case *ssa.Extract:
|
||||
tuple, err := p.getValue(instr.Tuple, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
locals[instr] = tuple.(*StructValue).Fields[instr.Index]
|
||||
case *ssa.FieldAddr:
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
var structVal *StructValue
|
||||
switch x := x.(type) {
|
||||
case *GlobalValue:
|
||||
structVal = x.Global.initializer.(*StructValue)
|
||||
case *PointerValue:
|
||||
structVal = (*x.Elem).(*StructValue)
|
||||
default:
|
||||
panic("expected a pointer")
|
||||
}
|
||||
locals[instr] = &PointerValue{nil, &structVal.Fields[instr.Field]}
|
||||
case *ssa.IndexAddr:
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if cnst, ok := instr.Index.(*ssa.Const); ok {
|
||||
index, _ := constant.Int64Val(cnst.Value)
|
||||
switch xPtr := x.(type) {
|
||||
case *GlobalValue:
|
||||
x = xPtr.Global.initializer
|
||||
case *PointerValue:
|
||||
x = *xPtr.Elem
|
||||
default:
|
||||
panic("expected a pointer")
|
||||
}
|
||||
switch x := x.(type) {
|
||||
case *ArrayValue:
|
||||
locals[instr] = &PointerValue{nil, &x.Elems[index]}
|
||||
default:
|
||||
return i, errors.New("todo: init IndexAddr not on an array or struct")
|
||||
}
|
||||
} else {
|
||||
return i, errors.New("todo: init IndexAddr index: " + instr.Index.String())
|
||||
}
|
||||
case *ssa.MakeMap:
|
||||
locals[instr] = &MapValue{instr.Type().Underlying().(*types.Map), nil, nil}
|
||||
case *ssa.MapUpdate:
|
||||
// Assume no duplicate keys exist. This is most likely true for
|
||||
// autogenerated code, but may not be true when trying to interpret
|
||||
// user code.
|
||||
key, err := p.getValue(instr.Key, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
value, err := p.getValue(instr.Value, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
x := locals[instr.Map].(*MapValue)
|
||||
x.Keys = append(x.Keys, key)
|
||||
x.Values = append(x.Values, value)
|
||||
case *ssa.Return:
|
||||
for i, r := range instr.Results {
|
||||
val, err := p.getValue(r, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
results[i] = val
|
||||
}
|
||||
case *ssa.Slice:
|
||||
// Turn a just-allocated array into a slice.
|
||||
if instr.Low != nil || instr.High != nil || instr.Max != nil {
|
||||
return i, errors.New("init: slice expression with bounds")
|
||||
}
|
||||
source, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
switch source := source.(type) {
|
||||
case *PointerValue: // pointer to array
|
||||
array := (*source.Elem).(*ArrayValue)
|
||||
locals[instr] = &SliceValue{instr.Type().Underlying().(*types.Slice), array}
|
||||
default:
|
||||
return i, errors.New("init: unknown slice type")
|
||||
}
|
||||
case *ssa.Store:
|
||||
if addr, ok := instr.Addr.(*ssa.Global); ok {
|
||||
if strings.HasPrefix(instr.Addr.Name(), "__cgofn__cgo_") || strings.HasPrefix(instr.Addr.Name(), "_cgo_") {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
continue
|
||||
}
|
||||
value, err := p.getValue(instr.Val, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
p.GetGlobal(addr).initializer = value
|
||||
} else if addr, ok := locals[instr.Addr]; ok {
|
||||
value, err := p.getValue(instr.Val, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if addr, ok := addr.(*PointerValue); ok {
|
||||
*(addr.Elem) = value
|
||||
} else {
|
||||
panic("store to non-pointer")
|
||||
}
|
||||
} else {
|
||||
return i, errors.New("todo: init Store: " + instr.String())
|
||||
}
|
||||
case *ssa.UnOp:
|
||||
if instr.Op != token.MUL || instr.CommaOk {
|
||||
return i, errors.New("init: unknown unop: " + instr.String())
|
||||
}
|
||||
valPtr, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
switch valPtr := valPtr.(type) {
|
||||
case *GlobalValue:
|
||||
locals[instr] = valPtr.Global.initializer
|
||||
case *PointerValue:
|
||||
locals[instr] = *valPtr.Elem
|
||||
default:
|
||||
panic("expected a pointer")
|
||||
}
|
||||
default:
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
return len(instrs), nil
|
||||
}
|
||||
|
||||
// Check whether this function can be interpreted at compile time. For that, it
|
||||
// needs to only contain relatively simple instructions (for example, no control
|
||||
// flow).
|
||||
func canInterpret(callee *ssa.Function) bool {
|
||||
if len(callee.Blocks) != 1 || callee.Signature.Results().Len() != 1 {
|
||||
// No control flow supported so only one basic block.
|
||||
// Only exactly one return value supported right now so check that as
|
||||
// well.
|
||||
return false
|
||||
}
|
||||
for _, instr := range callee.Blocks[0].Instrs {
|
||||
switch instr.(type) {
|
||||
// Ignore all functions fully supported by Program.interpret()
|
||||
// above.
|
||||
case *ssa.Alloc:
|
||||
case *ssa.ChangeType:
|
||||
case *ssa.DebugRef:
|
||||
case *ssa.Extract:
|
||||
case *ssa.FieldAddr:
|
||||
case *ssa.IndexAddr:
|
||||
case *ssa.MakeMap:
|
||||
case *ssa.MapUpdate:
|
||||
case *ssa.Return:
|
||||
case *ssa.Slice:
|
||||
case *ssa.Store:
|
||||
case *ssa.UnOp:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Program) getValue(value ssa.Value, locals map[ssa.Value]Value) (Value, error) {
|
||||
switch value := value.(type) {
|
||||
case *ssa.Const:
|
||||
return &ConstValue{value}, nil
|
||||
case *ssa.Function:
|
||||
return &FunctionValue{value.Type(), value}, nil
|
||||
case *ssa.Global:
|
||||
if strings.HasPrefix(value.Name(), "__cgofn__cgo_") || strings.HasPrefix(value.Name(), "_cgo_") {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil, ErrCGoWrapper
|
||||
}
|
||||
g := p.GetGlobal(value)
|
||||
if g.initializer == nil {
|
||||
value, err := p.getZeroValue(value.Type().Underlying().(*types.Pointer).Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.initializer = value
|
||||
}
|
||||
return &GlobalValue{g}, nil
|
||||
default:
|
||||
if local, ok := locals[value]; ok {
|
||||
return local, nil
|
||||
} else {
|
||||
return nil, errors.New("todo: init: unknown value: " + value.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) getZeroValue(t types.Type) (Value, error) {
|
||||
switch typ := t.Underlying().(type) {
|
||||
case *types.Array:
|
||||
elems := make([]Value, typ.Len())
|
||||
for i := range elems {
|
||||
elem, err := p.getZeroValue(typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elems[i] = elem
|
||||
}
|
||||
return &ArrayValue{typ.Elem(), elems}, nil
|
||||
case *types.Basic:
|
||||
return &ZeroBasicValue{typ}, nil
|
||||
case *types.Signature:
|
||||
return &FunctionValue{typ, nil}, nil
|
||||
case *types.Map:
|
||||
return &MapValue{typ, nil, nil}, nil
|
||||
case *types.Pointer:
|
||||
return &PointerValue{typ, nil}, nil
|
||||
case *types.Struct:
|
||||
elems := make([]Value, typ.NumFields())
|
||||
for i := range elems {
|
||||
elem, err := p.getZeroValue(typ.Field(i).Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elems[i] = elem
|
||||
}
|
||||
return &StructValue{t, elems}, nil
|
||||
case *types.Slice:
|
||||
return &SliceValue{typ, nil}, nil
|
||||
default:
|
||||
return nil, errors.New("todo: init: unknown global type: " + typ.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Boxed value for interpreter.
|
||||
type Value interface {
|
||||
}
|
||||
|
||||
type ConstValue struct {
|
||||
Expr *ssa.Const
|
||||
}
|
||||
|
||||
type ZeroBasicValue struct {
|
||||
Type *types.Basic
|
||||
}
|
||||
|
||||
type PointerValue struct {
|
||||
Type types.Type
|
||||
Elem *Value
|
||||
}
|
||||
|
||||
type FunctionValue struct {
|
||||
Type types.Type
|
||||
Elem *ssa.Function
|
||||
}
|
||||
|
||||
type PointerBitCastValue struct {
|
||||
Type types.Type
|
||||
Elem Value
|
||||
}
|
||||
|
||||
type PointerToUintptrValue struct {
|
||||
Elem Value
|
||||
}
|
||||
|
||||
type GlobalValue struct {
|
||||
Global *Global
|
||||
}
|
||||
|
||||
type ArrayValue struct {
|
||||
ElemType types.Type
|
||||
Elems []Value
|
||||
}
|
||||
|
||||
type StructValue struct {
|
||||
Type types.Type // types.Struct or types.Named
|
||||
Fields []Value
|
||||
}
|
||||
|
||||
type SliceValue struct {
|
||||
Type *types.Slice
|
||||
Array *ArrayValue
|
||||
}
|
||||
|
||||
type MapValue struct {
|
||||
Type *types.Map
|
||||
Keys []Value
|
||||
Values []Value
|
||||
}
|
||||
@@ -43,11 +43,10 @@ type Function struct {
|
||||
// Global variable, possibly constant.
|
||||
type Global struct {
|
||||
*ssa.Global
|
||||
program *Program
|
||||
LLVMGlobal llvm.Value
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
initializer Value
|
||||
program *Program
|
||||
LLVMGlobal llvm.Value
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
}
|
||||
|
||||
// Type with a name and possibly methods.
|
||||
@@ -188,9 +187,6 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||
func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||
memberNames := make([]string, 0)
|
||||
for name := range pkg.Members {
|
||||
if isCGoInternal(name) {
|
||||
continue
|
||||
}
|
||||
memberNames = append(memberNames, name)
|
||||
}
|
||||
sort.Strings(memberNames)
|
||||
@@ -199,9 +195,6 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
if isCGoInternal(member.Name()) {
|
||||
continue
|
||||
}
|
||||
p.addFunction(member)
|
||||
case *ssa.Type:
|
||||
t := &NamedType{Type: member}
|
||||
@@ -269,10 +262,16 @@ func (f *Function) parsePragmas() {
|
||||
}
|
||||
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
||||
for _, comment := range decl.Doc.List {
|
||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
||||
text := comment.Text
|
||||
if strings.HasPrefix(text, "//export ") {
|
||||
// Rewrite '//export' to '//go:export' for compatibility with
|
||||
// gc.
|
||||
text = "//go:" + text[2:]
|
||||
}
|
||||
if !strings.HasPrefix(text, "//go:") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(comment.Text)
|
||||
parts := strings.Fields(text)
|
||||
switch parts[0] {
|
||||
case "//go:export":
|
||||
if len(parts) != 2 {
|
||||
@@ -322,7 +321,7 @@ func (f *Function) IsNoBounds() bool {
|
||||
|
||||
// Return true iff this function is externally visible.
|
||||
func (f *Function) IsExported() bool {
|
||||
return f.exported
|
||||
return f.exported || f.CName() != ""
|
||||
}
|
||||
|
||||
// Return true for functions annotated with //go:interrupt. The function name is
|
||||
@@ -330,7 +329,7 @@ func (f *Function) IsExported() bool {
|
||||
//
|
||||
// On some platforms (like AVR), interrupts need a special compiler flag.
|
||||
func (f *Function) IsInterrupt() bool {
|
||||
return f.exported
|
||||
return f.interrupt
|
||||
}
|
||||
|
||||
// Return the link name for this function.
|
||||
@@ -389,15 +388,25 @@ 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
|
||||
return g.extern || g.CName() != ""
|
||||
}
|
||||
|
||||
func (g *Global) Initializer() Value {
|
||||
return g.initializer
|
||||
// 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,
|
||||
@@ -423,18 +432,6 @@ func (p *Program) IsVolatile(t types.Type) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if this is a CGo-internal function that can be ignored.
|
||||
func isCGoInternal(name string) bool {
|
||||
if strings.HasPrefix(name, "_Cgo_") || strings.HasPrefix(name, "_cgo") {
|
||||
// _Cgo_ptr, _Cgo_use, _cgoCheckResult, _cgo_runtime_cgocall
|
||||
return true // CGo-internal functions
|
||||
}
|
||||
if strings.HasPrefix(name, "__cgofn__cgo_") {
|
||||
return true // CGo function pointer in global scope
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get all methods of a type.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
ms := prog.MethodSets.MethodSet(typ)
|
||||
|
||||
+4
-3
@@ -68,11 +68,12 @@ func (p *Program) SimpleDCE() {
|
||||
// functions.
|
||||
main := p.mainPkg.Members["main"].(*ssa.Function)
|
||||
runtimePkg := p.Program.ImportedPackage("runtime")
|
||||
mathPkg := p.Program.ImportedPackage("math")
|
||||
p.GetFunction(main).flag = true
|
||||
worklist := []*ssa.Function{main}
|
||||
for _, f := range p.Functions {
|
||||
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg {
|
||||
if f.flag || isCGoInternal(f.Name()) {
|
||||
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || (f.Pkg == mathPkg && f.Pkg != nil) {
|
||||
if f.flag {
|
||||
continue
|
||||
}
|
||||
f.flag = true
|
||||
@@ -102,7 +103,7 @@ func (p *Program) SimpleDCE() {
|
||||
}
|
||||
}
|
||||
for _, operand := range instr.Operands(nil) {
|
||||
if operand == nil || *operand == nil || isCGoInternal((*operand).Name()) {
|
||||
if operand == nil || *operand == nil {
|
||||
continue
|
||||
}
|
||||
switch operand := (*operand).(type) {
|
||||
|
||||
+158
-106
@@ -9,36 +9,41 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// fileInfo holds all Cgo-related information of a given *ast.File.
|
||||
type fileInfo struct {
|
||||
*ast.File
|
||||
filename string
|
||||
functions []*functionInfo
|
||||
typedefs []*typedefInfo
|
||||
functions map[string]*functionInfo
|
||||
globals map[string]*globalInfo
|
||||
typedefs map[string]*typedefInfo
|
||||
importCPos token.Pos
|
||||
}
|
||||
|
||||
// functionInfo stores some information about a Cgo function found by libclang
|
||||
// and declared in the AST.
|
||||
type functionInfo struct {
|
||||
name string
|
||||
args []paramInfo
|
||||
result string
|
||||
args []paramInfo
|
||||
results *ast.FieldList
|
||||
}
|
||||
|
||||
// paramInfo is a parameter of a Cgo function (see functionInfo).
|
||||
type paramInfo struct {
|
||||
name string
|
||||
typeName string
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// typedefInfo contains information about a single typedef in C.
|
||||
type typedefInfo struct {
|
||||
newName string // newly defined type name
|
||||
oldName string // old type name, may be something like "unsigned long long"
|
||||
size int // size in bytes
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// globalInfo contains information about a declared global variable in C.
|
||||
type globalInfo struct {
|
||||
typeExpr ast.Expr
|
||||
}
|
||||
|
||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||
@@ -75,8 +80,11 @@ typedef unsigned long long _Cgo_ulonglong;
|
||||
// comment with libclang, and modifies the AST to use this information.
|
||||
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) error {
|
||||
info := &fileInfo{
|
||||
File: f,
|
||||
filename: filename,
|
||||
File: f,
|
||||
filename: filename,
|
||||
functions: map[string]*functionInfo{},
|
||||
globals: map[string]*globalInfo{},
|
||||
typedefs: map[string]*typedefInfo{},
|
||||
}
|
||||
|
||||
// Find `import "C"` statements in the file.
|
||||
@@ -122,6 +130,12 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
|
||||
// Declare functions found by libclang.
|
||||
info.addFuncDecls()
|
||||
|
||||
// Declare stub function pointer values found by libclang.
|
||||
info.addFuncPtrDecls()
|
||||
|
||||
// Declare globals found by libclang.
|
||||
info.addVarDecls()
|
||||
|
||||
// Forward C types to Go types (like C.uint32_t -> uint32).
|
||||
info.addTypeAliases()
|
||||
|
||||
@@ -129,7 +143,7 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
|
||||
info.addTypedefs()
|
||||
|
||||
// Patch the AST to use the declared types and functions.
|
||||
ast.Inspect(f, info.walker)
|
||||
f = astutil.Apply(f, info.walker, nil).(*ast.File)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -139,16 +153,22 @@ func (p *Package) processCgo(filename string, f *ast.File, cflags []string) erro
|
||||
func (info *fileInfo) addFuncDecls() {
|
||||
// TODO: replace all uses of importCPos with the real locations from
|
||||
// libclang.
|
||||
for _, fn := range info.functions {
|
||||
names := make([]string, 0, len(info.functions))
|
||||
for name := range info.functions {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
fn := info.functions[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Fun,
|
||||
Name: mapCgoType(fn.name),
|
||||
Name: "C." + name,
|
||||
}
|
||||
args := make([]*ast.Field, len(fn.args))
|
||||
decl := &ast.FuncDecl{
|
||||
Name: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: mapCgoType(fn.name),
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
@@ -158,16 +178,7 @@ func (info *fileInfo) addFuncDecls() {
|
||||
List: args,
|
||||
Closing: info.importCPos,
|
||||
},
|
||||
Results: &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Type: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: mapCgoType(fn.result),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Results: fn.results,
|
||||
},
|
||||
}
|
||||
obj.Decl = decl
|
||||
@@ -179,21 +190,107 @@ func (info *fileInfo) addFuncDecls() {
|
||||
Name: arg.name,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: mapCgoType(arg.name),
|
||||
Name: arg.name,
|
||||
Decl: decl,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: mapCgoType(arg.typeName),
|
||||
},
|
||||
Type: arg.typeExpr,
|
||||
}
|
||||
}
|
||||
info.Decls = append(info.Decls, decl)
|
||||
}
|
||||
}
|
||||
|
||||
// addFuncPtrDecls creates stub declarations of function pointer values. These
|
||||
// values will later be replaced with the real values in the compiler.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// var (
|
||||
// C.add unsafe.Pointer
|
||||
// C.mul unsafe.Pointer
|
||||
// // ...
|
||||
// )
|
||||
func (info *fileInfo) addFuncPtrDecls() {
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: info.importCPos,
|
||||
Tok: token.VAR,
|
||||
Lparen: info.importCPos,
|
||||
Rparen: info.importCPos,
|
||||
}
|
||||
names := make([]string, 0, len(info.functions))
|
||||
for name := range info.functions {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "C." + name + "$funcaddr",
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{&ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "C." + name + "$funcaddr",
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "unsafe",
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "Pointer",
|
||||
},
|
||||
},
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
}
|
||||
info.Decls = append(info.Decls, gen)
|
||||
}
|
||||
|
||||
// addVarDecls declares external C globals in the Go source.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
// var (
|
||||
// C.globalInt int
|
||||
// C.globalBool bool
|
||||
// // ...
|
||||
// )
|
||||
func (info *fileInfo) addVarDecls() {
|
||||
gen := &ast.GenDecl{
|
||||
TokPos: info.importCPos,
|
||||
Tok: token.VAR,
|
||||
Lparen: info.importCPos,
|
||||
Rparen: info.importCPos,
|
||||
}
|
||||
names := make([]string, 0, len(info.globals))
|
||||
for name := range info.globals {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
global := info.globals[name]
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: "C." + name,
|
||||
}
|
||||
valueSpec := &ast.ValueSpec{
|
||||
Names: []*ast.Ident{&ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "C." + name,
|
||||
Obj: obj,
|
||||
}},
|
||||
Type: global.typeExpr,
|
||||
}
|
||||
obj.Decl = valueSpec
|
||||
gen.Specs = append(gen.Specs, valueSpec)
|
||||
}
|
||||
info.Decls = append(info.Decls, gen)
|
||||
}
|
||||
|
||||
// addTypeAliases aliases some built-in Go types with their equivalent C types.
|
||||
// It adds code like the following to the AST:
|
||||
//
|
||||
@@ -243,55 +340,32 @@ func (info *fileInfo) addTypedefs() {
|
||||
TokPos: info.importCPos,
|
||||
Tok: token.TYPE,
|
||||
}
|
||||
for _, typedef := range info.typedefs {
|
||||
newType := mapCgoType(typedef.newName)
|
||||
oldType := mapCgoType(typedef.oldName)
|
||||
switch oldType {
|
||||
// TODO: plain char (may be signed or unsigned)
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typedef.size {
|
||||
case 1:
|
||||
oldType = "int8"
|
||||
case 2:
|
||||
oldType = "int16"
|
||||
case 4:
|
||||
oldType = "int32"
|
||||
case 8:
|
||||
oldType = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typedef.size {
|
||||
case 1:
|
||||
oldType = "uint8"
|
||||
case 2:
|
||||
oldType = "uint16"
|
||||
case 4:
|
||||
oldType = "uint32"
|
||||
case 8:
|
||||
oldType = "uint64"
|
||||
}
|
||||
names := make([]string, 0, len(info.typedefs))
|
||||
for name := range info.typedefs {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
typedef := info.typedefs[name]
|
||||
typeName := "C." + name
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
typeName = "C." + name[len("_Cgo_"):]
|
||||
}
|
||||
if strings.HasPrefix(newType, "C._Cgo_") {
|
||||
newType = "C." + newType[len("C._Cgo_"):]
|
||||
}
|
||||
if _, ok := cgoAliases[newType]; ok {
|
||||
if _, ok := cgoAliases[typeName]; ok {
|
||||
// This is a type that also exists in Go (defined in stdint.h).
|
||||
continue
|
||||
}
|
||||
obj := &ast.Object{
|
||||
Kind: ast.Typ,
|
||||
Name: newType,
|
||||
Name: typeName,
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: newType,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: oldType,
|
||||
},
|
||||
Type: typedef.typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
@@ -299,12 +373,12 @@ func (info *fileInfo) addTypedefs() {
|
||||
info.Decls = append(info.Decls, gen)
|
||||
}
|
||||
|
||||
// walker replaces all "C".<something> call expressions to literal
|
||||
// "C.<something>" expressions. This is impossible to write in Go (a dot cannot
|
||||
// be used in the middle of a name) so is used as a new namespace for C call
|
||||
// expressions.
|
||||
func (info *fileInfo) walker(node ast.Node) bool {
|
||||
switch node := node.(type) {
|
||||
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
||||
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
||||
// used in the middle of a name) so in practice all C identifiers live in a
|
||||
// separate namespace (no _Cgo_ hacks like in gc).
|
||||
func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
|
||||
switch node := cursor.Node().(type) {
|
||||
case *ast.CallExpr:
|
||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
@@ -314,49 +388,27 @@ func (info *fileInfo) walker(node ast.Node) bool {
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if x.Name == "C" {
|
||||
if _, ok := info.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
||||
node.Fun = &ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: mapCgoType(fun.Sel.Name),
|
||||
Name: "C." + fun.Sel.Name,
|
||||
}
|
||||
}
|
||||
case *ast.ValueSpec:
|
||||
typ, ok := node.Type.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
x, ok := typ.X.(*ast.Ident)
|
||||
case *ast.SelectorExpr:
|
||||
x, ok := node.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if x.Name == "C" {
|
||||
node.Type = &ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: mapCgoType(typ.Sel.Name),
|
||||
name := "C." + node.Sel.Name
|
||||
if _, ok := info.functions[node.Sel.Name]; ok {
|
||||
name += "$funcaddr"
|
||||
}
|
||||
cursor.Replace(&ast.Ident{
|
||||
NamePos: x.NamePos,
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// mapCgoType converts a C type name into a Go type name with a "C." prefix.
|
||||
func mapCgoType(t string) string {
|
||||
switch t {
|
||||
case "signed char":
|
||||
return "C.schar"
|
||||
case "long long":
|
||||
return "C.longlong"
|
||||
case "unsigned char":
|
||||
return "C.schar"
|
||||
case "unsigned short":
|
||||
return "C.ushort"
|
||||
case "unsigned int":
|
||||
return "C.uint"
|
||||
case "unsigned long":
|
||||
return "C.ulong"
|
||||
case "unsigned long long":
|
||||
return "C.ulonglong"
|
||||
default:
|
||||
return "C." + t
|
||||
}
|
||||
}
|
||||
|
||||
+141
-15
@@ -5,6 +5,10 @@ package loader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -88,30 +92,74 @@ func tinygo_clang_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.in
|
||||
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
|
||||
return C.CXChildVisit_Continue // not supported
|
||||
}
|
||||
numArgs := C.clang_Cursor_getNumArguments(c)
|
||||
fn := &functionInfo{name: name}
|
||||
info.functions = append(info.functions, fn)
|
||||
for i := C.int(0); i < numArgs; i++ {
|
||||
numArgs := int(C.clang_Cursor_getNumArguments(c))
|
||||
fn := &functionInfo{}
|
||||
info.functions[name] = fn
|
||||
for i := 0; i < numArgs; i++ {
|
||||
arg := C.clang_Cursor_getArgument(c, C.uint(i))
|
||||
argName := getString(C.clang_getCursorSpelling(arg))
|
||||
argType := C.clang_getArgType(cursorType, C.uint(i))
|
||||
argTypeName := getString(C.clang_getTypeSpelling(argType))
|
||||
fn.args = append(fn.args, paramInfo{argName, argTypeName})
|
||||
if argName == "" {
|
||||
argName = "$" + strconv.Itoa(i)
|
||||
}
|
||||
fn.args = append(fn.args, paramInfo{
|
||||
name: argName,
|
||||
typeExpr: info.makeASTType(argType),
|
||||
})
|
||||
}
|
||||
resultType := C.clang_getCursorResultType(c)
|
||||
resultTypeName := getString(C.clang_getTypeSpelling(resultType))
|
||||
fn.result = resultTypeName
|
||||
if resultType.kind != C.CXType_Void {
|
||||
fn.results = &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Type: info.makeASTType(resultType),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case C.CXCursor_TypedefDecl:
|
||||
typedefType := C.clang_getCursorType(c)
|
||||
name := getString(C.clang_getTypedefName(typedefType))
|
||||
underlyingType := C.clang_getTypedefDeclUnderlyingType(c)
|
||||
underlyingTypeName := getString(C.clang_getTypeSpelling(underlyingType))
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
info.typedefs = append(info.typedefs, &typedefInfo{
|
||||
newName: name,
|
||||
oldName: underlyingTypeName,
|
||||
size: int(typeSize),
|
||||
})
|
||||
expr := info.makeASTType(underlyingType)
|
||||
if strings.HasPrefix(name, "_Cgo_") {
|
||||
expr := expr.(*ast.Ident)
|
||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||
switch expr.Name {
|
||||
// TODO: plain char (may be signed or unsigned)
|
||||
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "int8"
|
||||
case 2:
|
||||
expr.Name = "int16"
|
||||
case 4:
|
||||
expr.Name = "int32"
|
||||
case 8:
|
||||
expr.Name = "int64"
|
||||
}
|
||||
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
|
||||
switch typeSize {
|
||||
case 1:
|
||||
expr.Name = "uint8"
|
||||
case 2:
|
||||
expr.Name = "uint16"
|
||||
case 4:
|
||||
expr.Name = "uint32"
|
||||
case 8:
|
||||
expr.Name = "uint64"
|
||||
}
|
||||
}
|
||||
}
|
||||
info.typedefs[name] = &typedefInfo{
|
||||
typeExpr: expr,
|
||||
}
|
||||
case C.CXCursor_VarDecl:
|
||||
name := getString(C.clang_getCursorSpelling(c))
|
||||
cursorType := C.clang_getCursorType(c)
|
||||
info.globals[name] = &globalInfo{
|
||||
typeExpr: info.makeASTType(cursorType),
|
||||
}
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
@@ -122,3 +170,81 @@ func getString(clangString C.CXString) (s string) {
|
||||
C.clang_disposeString(clangString)
|
||||
return
|
||||
}
|
||||
|
||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||
// it converts a libclang type to a type in the Go AST.
|
||||
func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
||||
var typeName string
|
||||
switch typ.kind {
|
||||
case C.CXType_SChar:
|
||||
typeName = "C.schar"
|
||||
case C.CXType_UChar:
|
||||
typeName = "C.uchar"
|
||||
case C.CXType_Short:
|
||||
typeName = "C.short"
|
||||
case C.CXType_UShort:
|
||||
typeName = "C.ushort"
|
||||
case C.CXType_Int:
|
||||
typeName = "C.int"
|
||||
case C.CXType_UInt:
|
||||
typeName = "C.uint"
|
||||
case C.CXType_Long:
|
||||
typeName = "C.long"
|
||||
case C.CXType_ULong:
|
||||
typeName = "C.ulong"
|
||||
case C.CXType_LongLong:
|
||||
typeName = "C.longlong"
|
||||
case C.CXType_ULongLong:
|
||||
typeName = "C.ulonglong"
|
||||
case C.CXType_Bool:
|
||||
typeName = "bool"
|
||||
case C.CXType_Float, C.CXType_Double, C.CXType_LongDouble:
|
||||
switch C.clang_Type_getSizeOf(typ) {
|
||||
case 4:
|
||||
typeName = "float32"
|
||||
case 8:
|
||||
typeName = "float64"
|
||||
default:
|
||||
// Don't do anything, rely on the fallback code to show a somewhat
|
||||
// sensible error message like "undeclared name: C.long double".
|
||||
}
|
||||
case C.CXType_Complex:
|
||||
switch C.clang_Type_getSizeOf(typ) {
|
||||
case 8:
|
||||
typeName = "complex64"
|
||||
case 16:
|
||||
typeName = "complex128"
|
||||
}
|
||||
case C.CXType_Pointer:
|
||||
return &ast.StarExpr{
|
||||
Star: info.importCPos,
|
||||
X: info.makeASTType(C.clang_getPointeeType(typ)),
|
||||
}
|
||||
case C.CXType_FunctionProto:
|
||||
// Be compatible with gc, which uses the *[0]byte type for function
|
||||
// pointer types.
|
||||
// Return type [0]byte because this is a function type, not a pointer to
|
||||
// this function type.
|
||||
return &ast.ArrayType{
|
||||
Lbrack: info.importCPos,
|
||||
Len: &ast.BasicLit{
|
||||
ValuePos: info.importCPos,
|
||||
Kind: token.INT,
|
||||
Value: "0",
|
||||
},
|
||||
Elt: &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "byte",
|
||||
},
|
||||
}
|
||||
}
|
||||
if typeName == "" {
|
||||
// Fallback, probably incorrect but at least the error points to an odd
|
||||
// type name.
|
||||
typeName = "C." + getString(C.clang_getTypeSpelling(typ))
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: typeName,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
package loader
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I/usr/lib/llvm-7/include
|
||||
#cgo LDFLAGS: -L/usr/lib/llvm-7/lib -lclang
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-7/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-7/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm/lib -lclang -lffi
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -21,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
var commands = map[string]string{
|
||||
"ar": "ar",
|
||||
"ar": "llvm-ar",
|
||||
"clang": "clang-7",
|
||||
"ld.lld": "ld.lld-7",
|
||||
"wasm-ld": "wasm-ld-7",
|
||||
@@ -46,7 +47,6 @@ type BuildConfig struct {
|
||||
dumpSSA bool
|
||||
debug bool
|
||||
printSizes string
|
||||
initInterp bool
|
||||
cFlags []string
|
||||
ldFlags []string
|
||||
wasmAbi string
|
||||
@@ -63,19 +63,18 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
spec.LDFlags = append(spec.LDFlags, config.ldFlags...)
|
||||
|
||||
compilerConfig := compiler.Config{
|
||||
Triple: spec.Triple,
|
||||
CPU: spec.CPU,
|
||||
GOOS: spec.GOOS,
|
||||
GOARCH: spec.GOARCH,
|
||||
GC: config.gc,
|
||||
CFlags: spec.CFlags,
|
||||
LDFlags: spec.LDFlags,
|
||||
Debug: config.debug,
|
||||
DumpSSA: config.dumpSSA,
|
||||
RootDir: sourceDir(),
|
||||
GOPATH: getGopath(),
|
||||
BuildTags: spec.BuildTags,
|
||||
InitInterp: config.initInterp,
|
||||
Triple: spec.Triple,
|
||||
CPU: spec.CPU,
|
||||
GOOS: spec.GOOS,
|
||||
GOARCH: spec.GOARCH,
|
||||
GC: config.gc,
|
||||
CFlags: spec.CFlags,
|
||||
LDFlags: spec.LDFlags,
|
||||
Debug: config.debug,
|
||||
DumpSSA: config.dumpSSA,
|
||||
RootDir: sourceDir(),
|
||||
GOPATH: getGopath(),
|
||||
BuildTags: spec.BuildTags,
|
||||
}
|
||||
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
||||
if err != nil {
|
||||
@@ -95,17 +94,17 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
return errors.New("verification error after IR construction")
|
||||
}
|
||||
|
||||
if config.initInterp {
|
||||
err = interp.Run(c.Module(), c.TargetData(), config.dumpSSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("verification error after interpreting runtime.initAll")
|
||||
}
|
||||
err = interp.Run(c.Module(), c.TargetData(), config.dumpSSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("verification error after interpreting runtime.initAll")
|
||||
}
|
||||
|
||||
c.ApplyFunctionSections() // -ffunction-sections
|
||||
if spec.GOOS != "darwin" {
|
||||
c.ApplyFunctionSections() // -ffunction-sections
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("verification error after applying function sections")
|
||||
}
|
||||
@@ -319,14 +318,29 @@ func Flash(pkgName, target, port string, config *BuildConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Compile(pkgName, ".hex", spec, config, func(tmppath string) error {
|
||||
// determine the type of file to compile
|
||||
var fileExt string
|
||||
|
||||
switch {
|
||||
case strings.Contains(spec.Flasher, "{hex}"):
|
||||
fileExt = ".hex"
|
||||
case strings.Contains(spec.Flasher, "{elf}"):
|
||||
fileExt = ".elf"
|
||||
case strings.Contains(spec.Flasher, "{bin}"):
|
||||
fileExt = ".bin"
|
||||
default:
|
||||
return errors.New("invalid target file - did you forget the {hex} token in the 'flash' section?")
|
||||
}
|
||||
|
||||
return Compile(pkgName, fileExt, spec, config, func(tmppath string) error {
|
||||
if spec.Flasher == "" {
|
||||
return errors.New("no flash command specified - did you miss a -target flag?")
|
||||
}
|
||||
|
||||
// Create the command.
|
||||
flashCmd := spec.Flasher
|
||||
flashCmd = strings.Replace(flashCmd, "{hex}", tmppath, -1)
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1)
|
||||
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||
|
||||
// Execute the command.
|
||||
@@ -455,6 +469,8 @@ func Run(pkgName, target string, config *BuildConfig) error {
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||
fmt.Fprintln(os.Stderr, "version:", version)
|
||||
fmt.Fprintf(os.Stderr, "usage: %s command [-printir] [-target=<target>] -o <output> <input>\n", os.Args[0])
|
||||
fmt.Fprintln(os.Stderr, "\ncommands:")
|
||||
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
|
||||
@@ -498,7 +514,6 @@ func main() {
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
initInterp := flag.Bool("initinterp", true, "enable/disable partial evaluator of generated IR")
|
||||
port := flag.String("port", "/dev/ttyACM0", "flash port")
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
@@ -519,7 +534,6 @@ func main() {
|
||||
dumpSSA: *dumpSSA,
|
||||
debug: !*nodebug,
|
||||
printSizes: *printSize,
|
||||
initInterp: *initInterp,
|
||||
wasmAbi: *wasmAbi,
|
||||
}
|
||||
|
||||
@@ -601,6 +615,8 @@ func main() {
|
||||
}
|
||||
case "help":
|
||||
usage()
|
||||
case "version":
|
||||
fmt.Printf("tinygo version %s %s/%s\n", version, runtime.GOOS, runtime.GOARCH)
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Unknown command:", command)
|
||||
usage()
|
||||
|
||||
+32
-3
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
@@ -23,6 +24,9 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
dirMatches, err := filepath.Glob(TESTDATA + "/*/main.go")
|
||||
if err != nil {
|
||||
t.Fatal("could not read test packages:", err)
|
||||
}
|
||||
if len(matches) == 0 || len(dirMatches) == 0 {
|
||||
t.Fatal("no test files found")
|
||||
}
|
||||
@@ -39,19 +43,45 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
t.Log("running tests on the host...")
|
||||
t.Log("running tests on host...")
|
||||
for _, path := range matches {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "", t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("running tests on the qemu target...")
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
t.Log("running tests for emulated cortex-m3...")
|
||||
for _, path := range matches {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "qemu", t)
|
||||
})
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
t.Log("running tests for linux/arm...")
|
||||
for _, path := range matches {
|
||||
if path == "testdata/cgo/" {
|
||||
continue // TODO: improve CGo
|
||||
}
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "arm--linux-gnueabi", t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("running tests for linux/arm64...")
|
||||
for _, path := range matches {
|
||||
if path == "testdata/cgo/" {
|
||||
continue // TODO: improve CGo
|
||||
}
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "aarch64--linux-gnueabi", t)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runTest(path, tmpdir string, target string, t *testing.T) {
|
||||
@@ -76,7 +106,6 @@ func runTest(path, tmpdir string, target string, t *testing.T) {
|
||||
dumpSSA: false,
|
||||
debug: false,
|
||||
printSizes: "",
|
||||
initInterp: true,
|
||||
}
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
err = Build("./"+path, binary, target, config)
|
||||
|
||||
@@ -118,3 +118,21 @@ func SetPriority(irq uint32, priority uint32) {
|
||||
priority = priority << (regpos * 8) // bits to set
|
||||
NVIC.IPR[regnum] = RegValue((uint32(NVIC.IPR[regnum]) &^ mask) | priority)
|
||||
}
|
||||
|
||||
// DisableInterrupts disables all interrupts, and returns the old state.
|
||||
//
|
||||
// TODO: it doesn't actually return the old state, meaning that it cannot be
|
||||
// nested.
|
||||
func DisableInterrupts() uintptr {
|
||||
Asm("cpsid if")
|
||||
return 0
|
||||
}
|
||||
|
||||
// EnableInterrupts enables all interrupts again. The value passed in must be
|
||||
// the mask returned by DisableInterrupts.
|
||||
//
|
||||
// TODO: it doesn't actually use the old state, meaning that it cannot be
|
||||
// nested.
|
||||
func EnableInterrupts(mask uintptr) {
|
||||
Asm("cpsie if")
|
||||
}
|
||||
|
||||
@@ -13,11 +13,23 @@ function init() {
|
||||
document.querySelector('#b').oninput = updateResult;
|
||||
|
||||
const go = new Go();
|
||||
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function(obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
updateResult();
|
||||
})
|
||||
if ('instantiateStreaming' in WebAssembly) {
|
||||
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function(obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
updateResult();
|
||||
})
|
||||
} else {
|
||||
fetch(WASM_URL).then(resp =>
|
||||
resp.arrayBuffer()
|
||||
).then(bytes =>
|
||||
WebAssembly.instantiate(bytes, go.importObject).then(function(obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
updateResult();
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// +build sam,atsamd21,circuitplay_express
|
||||
|
||||
package machine
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PB09
|
||||
D1 = PB08
|
||||
D2 = PB02
|
||||
D3 = PB03
|
||||
D4 = PA28
|
||||
D5 = PA14
|
||||
D6 = PA05
|
||||
D7 = PA15
|
||||
D8 = PB23
|
||||
D9 = PA06
|
||||
D10 = PA07
|
||||
D11 = 0xff // does not seem to exist
|
||||
D12 = PA02
|
||||
D13 = PA17 // PWM available
|
||||
)
|
||||
|
||||
// Analog Pins
|
||||
const (
|
||||
A0 = PA02 // PWM available, also ADC/AIN[0]
|
||||
A1 = PA05 // ADC/AIN[5]
|
||||
A2 = PA06 // PWM available, also ADC/AIN[6]
|
||||
A3 = PA07 // PWM available, also ADC/AIN[7]
|
||||
A4 = PB03 // PORTB
|
||||
A5 = PB02 // PORTB
|
||||
A6 = PB09 // PORTB
|
||||
A7 = PB08 // PORTB
|
||||
A8 = PA11 // ADC/AIN[19]
|
||||
A9 = PA09 // ADC/AIN[17]
|
||||
A10 = PA04
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
NEOPIXELS = D8
|
||||
|
||||
BUTTONA = D4
|
||||
BUTTONB = D5
|
||||
SLIDER = D7 // built-in slide switch
|
||||
|
||||
BUTTON = BUTTONA
|
||||
BUTTON1 = BUTTONB
|
||||
|
||||
LIGHTSENSOR = A8
|
||||
TEMPSENSOR = A9
|
||||
PROXIMITY = A10
|
||||
)
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART0 pins
|
||||
const (
|
||||
UART_TX_PIN = PB08 // PORTB
|
||||
UART_RX_PIN = PB09 // PORTB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA00 // SDA: SERCOM3/PAD[0]
|
||||
SCL_PIN = PA01 // SCL: SERCOM3/PAD[1]
|
||||
)
|
||||
@@ -1,40 +1,46 @@
|
||||
// +build sam,atsamd21g18a,itsybitsy_m0
|
||||
// +build sam,atsamd21,itsybitsy_m0
|
||||
|
||||
package machine
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = 11 // UART0 RX
|
||||
D1 = 10 // UART0 TX
|
||||
D2 = 14
|
||||
D3 = 9 // PWM available
|
||||
D4 = 8 // PWM available
|
||||
D5 = 15 // PWM available
|
||||
D6 = 20 // PWM available
|
||||
D7 = 21 // PWM available
|
||||
D8 = 6 // PWM available
|
||||
D9 = 7 // PWM available
|
||||
D10 = 18 // can be used for PWM or UART1 TX
|
||||
D11 = 16 // can be used for PWM or UART1 RX
|
||||
D12 = 19 // PWM available
|
||||
D13 = 17 // PWM available
|
||||
D0 = PA11 // UART0 RX
|
||||
D1 = PA10 // UART0 TX
|
||||
D2 = PA14
|
||||
D3 = PA09 // PWM available
|
||||
D4 = PA08 // PWM available
|
||||
D5 = PA15 // PWM available
|
||||
D6 = PA20 // PWM available
|
||||
D7 = PA21 // PWM available
|
||||
D8 = PA06 // PWM available
|
||||
D9 = PA07 // PWM available
|
||||
D10 = PA18 // can be used for PWM or UART1 TX
|
||||
D11 = PA16 // can be used for PWM or UART1 RX
|
||||
D12 = PA19 // PWM available
|
||||
D13 = PA17 // PWM available
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = 2 // ADC/AIN[0]
|
||||
// A1 = 8 // ADC/AIN[2] TODO: requires PORTB
|
||||
// A2 = 9 // ADC/AIN[3] TODO: requires PORTB
|
||||
A3 = 4 // ADC/AIN[4]
|
||||
A4 = 5 // ADC/AIN[5]
|
||||
//A5 = 2 // ADC/AIN[10] TODO: requires PORTB
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PB08 // ADC/AIN[2]
|
||||
A2 = PB09 // ADC/AIN[3]
|
||||
A3 = PA04 // ADC/AIN[4]
|
||||
A4 = PA05 // ADC/AIN[5]
|
||||
A5 = PB02 // ADC/AIN[10]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
)
|
||||
|
||||
// UART0 pins
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
@@ -42,6 +48,6 @@ const (
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = 22 // SDA: SERCOM3/PAD[0]
|
||||
SCL_PIN = 23 // SCL: SERCOM3/PAD[1]
|
||||
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||
SCL_PIN = PA23 // SCL: SERCOM3/PAD[1]
|
||||
)
|
||||
|
||||
@@ -43,6 +43,27 @@ const (
|
||||
SPI0_MISO_PIN = 22 // P14 on the board
|
||||
)
|
||||
|
||||
// GPIO/Analog pins
|
||||
const (
|
||||
P0 = 3
|
||||
P1 = 2
|
||||
P2 = 1
|
||||
P3 = 4
|
||||
P4 = 5
|
||||
P5 = 17
|
||||
P6 = 12
|
||||
P7 = 11
|
||||
P8 = 18
|
||||
P9 = 10
|
||||
P10 = 6
|
||||
P11 = 26
|
||||
P12 = 20
|
||||
P13 = 23
|
||||
P14 = 22
|
||||
P15 = 21
|
||||
P16 = 16
|
||||
)
|
||||
|
||||
// LED matrix pins
|
||||
const (
|
||||
LED_COL_1 = 4
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,908 +0,0 @@
|
||||
// +build sam,atsamd21g18a
|
||||
|
||||
// Peripheral abstraction layer for the atsamd21.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 48000000
|
||||
|
||||
type GPIOMode uint8
|
||||
|
||||
const (
|
||||
GPIO_ANALOG = 1
|
||||
GPIO_SERCOM = 2
|
||||
GPIO_SERCOM_ALT = 3
|
||||
GPIO_TIMER = 4
|
||||
GPIO_TIMER_ALT = 5
|
||||
GPIO_COM = 6
|
||||
GPIO_AC_CLK = 7
|
||||
GPIO_DIGITAL = 8
|
||||
GPIO_INPUT = 9
|
||||
GPIO_INPUT_PULLUP = 10
|
||||
GPIO_OUTPUT = 11
|
||||
GPIO_PWM = GPIO_TIMER
|
||||
GPIO_PWM_ALT = GPIO_TIMER_ALT
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p GPIO) Configure(config GPIOConfig) {
|
||||
switch config.Mode {
|
||||
case GPIO_OUTPUT:
|
||||
sam.PORT.DIRSET0 = (1 << p.Pin)
|
||||
// output is also set to input enable so pin can read back its own value
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
|
||||
case GPIO_INPUT:
|
||||
sam.PORT.DIRCLR0 = (1 << p.Pin)
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
|
||||
case GPIO_SERCOM:
|
||||
if p.Pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
p.setPMux(val | (GPIO_SERCOM << sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
p.setPMux(val | (GPIO_SERCOM << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p GPIO) Get() bool {
|
||||
return (sam.PORT.IN0>>p.Pin)&1 > 0
|
||||
}
|
||||
|
||||
// Set the pin to high or low.
|
||||
// Warning: only use this on an output pin!
|
||||
func (p GPIO) Set(high bool) {
|
||||
if high {
|
||||
sam.PORT.OUTSET0 = (1 << p.Pin)
|
||||
} else {
|
||||
sam.PORT.OUTCLR0 = (1 << p.Pin)
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (p GPIO) getPMux() sam.RegValue8 {
|
||||
return getPMux(p.Pin)
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (p GPIO) setPMux(val sam.RegValue8) {
|
||||
setPMux(p.Pin, val)
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (p GPIO) getPinCfg() sam.RegValue8 {
|
||||
return getPinCfg(p.Pin)
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (p GPIO) setPinCfg(val sam.RegValue8) {
|
||||
setPinCfg(p.Pin, val)
|
||||
}
|
||||
|
||||
// UART on the SAMD21.
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
}
|
||||
|
||||
var (
|
||||
// The first hardware serial port on the SAMD21. Uses the SERCOM0 interface.
|
||||
UART0 = UART{Bus: sam.SERCOM0_USART, Buffer: NewRingBuffer()}
|
||||
|
||||
// The second hardware serial port on the SAMD21. Uses the SERCOM1 interface.
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART, Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
const (
|
||||
sampleRate16X = 16
|
||||
lsbFirst = 1
|
||||
sercomRXPad0 = 0
|
||||
sercomRXPad1 = 1
|
||||
sercomRXPad2 = 2
|
||||
sercomRXPad3 = 3
|
||||
sercomTXPad0 = 0 // Only for UART
|
||||
sercomTXPad2 = 1 // Only for UART
|
||||
sercomTXPad023 = 2 // Only for UART with TX on PAD0, RTS on PAD2 and CTS on PAD3
|
||||
)
|
||||
|
||||
// Configure the UART.
|
||||
func (uart UART) Configure(config UARTConfig) {
|
||||
// Default baud rate to 115200.
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
|
||||
// determine pins
|
||||
if config.TX == 0 {
|
||||
// use default pins
|
||||
config.TX = UART_TX_PIN
|
||||
config.RX = UART_RX_PIN
|
||||
}
|
||||
|
||||
// determine pads
|
||||
var txpad, rxpad int
|
||||
switch config.TX {
|
||||
case UART_TX_PIN:
|
||||
txpad = sercomTXPad2
|
||||
case D10:
|
||||
txpad = sercomTXPad2
|
||||
case D11:
|
||||
txpad = sercomTXPad0
|
||||
default:
|
||||
panic("Invalid TX pin for UART")
|
||||
}
|
||||
|
||||
switch config.RX {
|
||||
case UART_RX_PIN:
|
||||
rxpad = sercomRXPad3
|
||||
case D10:
|
||||
rxpad = sercomRXPad2
|
||||
case D11:
|
||||
rxpad = sercomRXPad0
|
||||
case D12:
|
||||
rxpad = sercomRXPad3
|
||||
case D13:
|
||||
rxpad = sercomRXPad1
|
||||
default:
|
||||
panic("Invalid RX pin for UART")
|
||||
}
|
||||
|
||||
// configure pins
|
||||
GPIO{config.TX}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
GPIO{config.RX}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
|
||||
// reset SERCOM0
|
||||
uart.Bus.CTRLA |= sam.SERCOM_USART_CTRLA_SWRST
|
||||
for (uart.Bus.CTRLA&sam.SERCOM_USART_CTRLA_SWRST) > 0 ||
|
||||
(uart.Bus.SYNCBUSY&sam.SERCOM_USART_SYNCBUSY_SWRST) > 0 {
|
||||
}
|
||||
|
||||
// set UART mode/sample rate
|
||||
// SERCOM_USART_CTRLA_MODE(mode) |
|
||||
// SERCOM_USART_CTRLA_SAMPR(sampleRate);
|
||||
uart.Bus.CTRLA = (sam.SERCOM_USART_CTRLA_MODE_USART_INT_CLK << sam.SERCOM_USART_CTRLA_MODE_Pos) |
|
||||
(1 << sam.SERCOM_USART_CTRLA_SAMPR_Pos) // sample rate of 16x
|
||||
|
||||
// Set baud rate
|
||||
uart.SetBaudRate(config.BaudRate)
|
||||
|
||||
// setup UART frame
|
||||
// SERCOM_USART_CTRLA_FORM( (parityMode == SERCOM_NO_PARITY ? 0 : 1) ) |
|
||||
// dataOrder << SERCOM_USART_CTRLA_DORD_Pos;
|
||||
uart.Bus.CTRLA |= (0 << sam.SERCOM_USART_CTRLA_FORM_Pos) | // no parity
|
||||
(lsbFirst << sam.SERCOM_USART_CTRLA_DORD_Pos) // data order
|
||||
|
||||
// set UART stop bits/parity
|
||||
// SERCOM_USART_CTRLB_CHSIZE(charSize) |
|
||||
// nbStopBits << SERCOM_USART_CTRLB_SBMODE_Pos |
|
||||
// (parityMode == SERCOM_NO_PARITY ? 0 : parityMode) << SERCOM_USART_CTRLB_PMODE_Pos; //If no parity use default value
|
||||
uart.Bus.CTRLB |= (0 << sam.SERCOM_USART_CTRLB_CHSIZE_Pos) | // 8 bits is 0
|
||||
(0 << sam.SERCOM_USART_CTRLB_SBMODE_Pos) | // 1 stop bit is zero
|
||||
(0 << sam.SERCOM_USART_CTRLB_PMODE_Pos) // no parity
|
||||
|
||||
// set UART pads. This is not same as pins...
|
||||
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
||||
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
||||
uart.Bus.CTRLA |= sam.RegValue((txpad << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
|
||||
(rxpad << sam.SERCOM_USART_CTRLA_RXPO_Pos))
|
||||
|
||||
// Enable Transceiver and Receiver
|
||||
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
|
||||
uart.Bus.CTRLB |= (sam.SERCOM_USART_CTRLB_TXEN | sam.SERCOM_USART_CTRLB_RXEN)
|
||||
|
||||
// Enable USART1 port.
|
||||
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
|
||||
uart.Bus.CTRLA |= sam.SERCOM_USART_CTRLA_ENABLE
|
||||
for (uart.Bus.SYNCBUSY & sam.SERCOM_USART_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// setup interrupt on receive
|
||||
uart.Bus.INTENSET = sam.SERCOM_USART_INTENSET_RXC
|
||||
|
||||
// Enable RX IRQ.
|
||||
if config.TX == UART_TX_PIN {
|
||||
// UART0
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0)
|
||||
} else {
|
||||
// UART1
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM1)
|
||||
}
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
func (uart UART) SetBaudRate(br uint32) {
|
||||
// Asynchronous fractional mode (Table 24-2 in datasheet)
|
||||
// BAUD = fref / (sampleRateValue * fbaud)
|
||||
// (multiply by 8, to calculate fractional piece)
|
||||
// uint32_t baudTimes8 = (SystemCoreClock * 8) / (16 * baudrate);
|
||||
baud := (CPU_FREQUENCY * 8) / (sampleRate16X * br)
|
||||
|
||||
// sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
|
||||
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
||||
uart.Bus.BAUD = sam.RegValue16(((baud % 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_FP_Pos) |
|
||||
((baud / 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_BAUD_Pos))
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
// wait until ready to receive
|
||||
for (uart.Bus.INTFLAG & sam.SERCOM_USART_INTFLAG_DRE) == 0 {
|
||||
}
|
||||
uart.Bus.DATA = sam.RegValue16(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:export SERCOM0_IRQHandler
|
||||
func handleUART0() {
|
||||
// should reset IRQ
|
||||
UART0.Receive(byte((UART0.Bus.DATA & 0xFF)))
|
||||
UART0.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
|
||||
}
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
// should reset IRQ
|
||||
UART1.Receive(byte((UART1.Bus.DATA & 0xFF)))
|
||||
UART1.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
|
||||
}
|
||||
|
||||
// I2C on the SAMD21.
|
||||
type I2C struct {
|
||||
Bus *sam.SERCOM_I2CM_Type
|
||||
}
|
||||
|
||||
// Since the I2C interfaces on the SAMD21 use the SERCOMx peripherals,
|
||||
// you can have multiple ones. we currently only implement one.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM3_I2CM}
|
||||
)
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
SCL uint8
|
||||
SDA uint8
|
||||
}
|
||||
|
||||
const (
|
||||
// Default rise time in nanoseconds, based on 4.7K ohm pull up resistors
|
||||
riseTimeNanoseconds = 125
|
||||
|
||||
// wire bus states
|
||||
wireUnknownState = 0
|
||||
wireIdleState = 1
|
||||
wireOwnerState = 2
|
||||
wireBusyState = 3
|
||||
|
||||
// wire commands
|
||||
wireCmdNoAction = 0
|
||||
wireCmdRepeatStart = 1
|
||||
wireCmdRead = 2
|
||||
wireCmdStop = 3
|
||||
)
|
||||
|
||||
const i2cTimeout = 1000
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
}
|
||||
|
||||
// reset SERCOM3
|
||||
i2c.Bus.CTRLA |= sam.SERCOM_I2CM_CTRLA_SWRST
|
||||
for (i2c.Bus.CTRLA&sam.SERCOM_I2CM_CTRLA_SWRST) > 0 ||
|
||||
(i2c.Bus.SYNCBUSY&sam.SERCOM_I2CM_SYNCBUSY_SWRST) > 0 {
|
||||
}
|
||||
|
||||
// Set i2c master mode
|
||||
//SERCOM_I2CM_CTRLA_MODE( I2C_MASTER_OPERATION )
|
||||
i2c.Bus.CTRLA = (sam.SERCOM_I2CM_CTRLA_MODE_I2C_MASTER << sam.SERCOM_I2CM_CTRLA_MODE_Pos) // |
|
||||
|
||||
i2c.SetBaudRate(config.Frequency)
|
||||
|
||||
// Enable I2CM port.
|
||||
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
|
||||
i2c.Bus.CTRLA |= sam.SERCOM_I2CM_CTRLA_ENABLE
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// set bus idle mode
|
||||
i2c.Bus.STATUS |= (wireIdleState << sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
GPIO{SDA_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
GPIO{SCL_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
func (i2c I2C) SetBaudRate(br uint32) {
|
||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||
baud := CPU_FREQUENCY/(2*br) - 5 - (((CPU_FREQUENCY / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
i2c.Bus.BAUD = sam.RegValue(baud)
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
var err error
|
||||
if len(w) != 0 {
|
||||
// send start/address for write
|
||||
i2c.sendAddress(addr, true)
|
||||
|
||||
// wait until transmission complete
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on ready to write data")
|
||||
}
|
||||
}
|
||||
|
||||
// ACK received (0: ACK, 1: NACK)
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C write error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
// write data
|
||||
for _, b := range w {
|
||||
err = i2c.WriteByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = i2c.signalStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(r) != 0 {
|
||||
// send start/address for read
|
||||
i2c.sendAddress(addr, false)
|
||||
|
||||
// wait transmission complete
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_SB) == 0 {
|
||||
// If the slave NACKS the address, the MB bit will be set.
|
||||
// In that case, send a stop condition and return error.
|
||||
if (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) > 0 {
|
||||
i2c.Bus.CTRLB |= (wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop condition
|
||||
return errors.New("I2C read error: expected ACK not NACK")
|
||||
}
|
||||
}
|
||||
|
||||
// ACK received (0: ACK, 1: NACK)
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C read error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
// read first byte
|
||||
r[0] = i2c.readByte()
|
||||
for i := 1; i < len(r); i++ {
|
||||
// Send an ACK
|
||||
i2c.Bus.CTRLB &^= sam.SERCOM_I2CM_CTRLB_ACKACT
|
||||
|
||||
i2c.signalRead()
|
||||
|
||||
// Read data and send the ACK
|
||||
r[i] = i2c.readByte()
|
||||
}
|
||||
|
||||
// Send NACK to end transmission
|
||||
i2c.Bus.CTRLB |= sam.SERCOM_I2CM_CTRLB_ACKACT
|
||||
|
||||
err = i2c.signalStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) WriteByte(data byte) error {
|
||||
// Send data byte
|
||||
i2c.Bus.DATA = sam.RegValue8(data)
|
||||
|
||||
// wait until transmission successful
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) == 0 {
|
||||
// check for bus error
|
||||
if (sam.SERCOM3_I2CM.STATUS & sam.SERCOM_I2CM_STATUS_BUSERR) > 0 {
|
||||
return errors.New("I2C bus error")
|
||||
}
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on write data")
|
||||
}
|
||||
}
|
||||
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C write error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendAddress sends the address and start signal
|
||||
func (i2c I2C) sendAddress(address uint16, write bool) error {
|
||||
data := (address << 1)
|
||||
if !write {
|
||||
data |= 1 // set read flag
|
||||
}
|
||||
|
||||
// wait until bus ready
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.STATUS&(wireIdleState<<sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)) == 0 &&
|
||||
(i2c.Bus.STATUS&(wireOwnerState<<sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on bus ready")
|
||||
}
|
||||
}
|
||||
i2c.Bus.ADDR = sam.RegValue(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalStop() error {
|
||||
i2c.Bus.CTRLB |= (wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on signal stop")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalRead() error {
|
||||
i2c.Bus.CTRLB |= (wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on signal read")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) readByte() byte {
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_SB) == 0 {
|
||||
}
|
||||
return byte(i2c.Bus.DATA)
|
||||
}
|
||||
|
||||
// PWM
|
||||
const period = 0xFFFF
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_TCC0_ | sam.PM_APBCMASK_TCC1_ | sam.PM_APBCMASK_TCC2_
|
||||
|
||||
// Use GCLK0 for TCC0/TCC1
|
||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 {
|
||||
}
|
||||
|
||||
// Use GCLK0 for TCC2/TC3
|
||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
|
||||
// disable timer
|
||||
timer.CTRLA &^= sam.TCC_CTRLA_ENABLE
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// Use "Normal PWM" (single-slope PWM)
|
||||
timer.WAVE |= sam.TCC_WAVE_WAVEGEN_NPWM
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_WAVE) > 0 {
|
||||
}
|
||||
|
||||
// Set the period (the number to count to (TOP) before resetting timer)
|
||||
//TCC0->PER.reg = period;
|
||||
timer.PER = period
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_PER) > 0 {
|
||||
}
|
||||
|
||||
// Set pin as output
|
||||
sam.PORT.DIRSET0 = (1 << pwm.Pin)
|
||||
// Set pin to low
|
||||
sam.PORT.OUTCLR0 = (1 << pwm.Pin)
|
||||
|
||||
// Enable the port multiplexer for pin
|
||||
pwm.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
|
||||
// Connect TCCX timer to pin.
|
||||
// we normally use the F channel aka ALT
|
||||
pwmConfig := GPIO_PWM_ALT
|
||||
|
||||
// in the case of PA6 or PA7 we have to use E channel
|
||||
if pwm.Pin == 6 || pwm.Pin == 7 {
|
||||
pwmConfig = GPIO_PWM
|
||||
}
|
||||
|
||||
if pwm.Pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
pwm.setPMux(val | sam.RegValue8(pwmConfig<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
pwm.setPMux(val | sam.RegValue8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
|
||||
// disable output
|
||||
timer.CTRLA &^= sam.TCC_CTRLA_ENABLE
|
||||
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// Set PWM signal to output duty cycle
|
||||
pwm.setChannel(sam.RegValue(value))
|
||||
|
||||
// Wait for synchronization on all channels
|
||||
for (timer.SYNCBUSY & (sam.TCC_SYNCBUSY_CC0 |
|
||||
sam.TCC_SYNCBUSY_CC1 |
|
||||
sam.TCC_SYNCBUSY_CC2 |
|
||||
sam.TCC_SYNCBUSY_CC3)) > 0 {
|
||||
}
|
||||
|
||||
// enable
|
||||
timer.CTRLA |= sam.TCC_CTRLA_ENABLE
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (pwm PWM) getPMux() sam.RegValue8 {
|
||||
return getPMux(pwm.Pin)
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (pwm PWM) setPMux(val sam.RegValue8) {
|
||||
setPMux(pwm.Pin, val)
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (pwm PWM) getPinCfg() sam.RegValue8 {
|
||||
return getPinCfg(pwm.Pin)
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (pwm PWM) setPinCfg(val sam.RegValue8) {
|
||||
setPinCfg(pwm.Pin, val)
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func getPMux(p uint8) sam.RegValue8 {
|
||||
pin := p >> 1
|
||||
switch pin {
|
||||
case 0:
|
||||
return sam.PORT.PMUX0_0
|
||||
case 1:
|
||||
return sam.PORT.PMUX0_1
|
||||
case 2:
|
||||
return sam.PORT.PMUX0_2
|
||||
case 3:
|
||||
return sam.PORT.PMUX0_3
|
||||
case 4:
|
||||
return sam.PORT.PMUX0_4
|
||||
case 5:
|
||||
return sam.PORT.PMUX0_5
|
||||
case 6:
|
||||
return sam.PORT.PMUX0_6
|
||||
case 7:
|
||||
return sam.PORT.PMUX0_7
|
||||
case 8:
|
||||
return sam.PORT.PMUX0_8
|
||||
case 9:
|
||||
return sam.PORT.PMUX0_9
|
||||
case 10:
|
||||
return sam.PORT.PMUX0_10
|
||||
case 11:
|
||||
return sam.PORT.PMUX0_11
|
||||
case 12:
|
||||
return sam.PORT.PMUX0_12
|
||||
case 13:
|
||||
return sam.PORT.PMUX0_13
|
||||
case 14:
|
||||
return sam.PORT.PMUX0_14
|
||||
case 15:
|
||||
return sam.PORT.PMUX0_15
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func setPMux(p uint8, val sam.RegValue8) {
|
||||
pin := p >> 1
|
||||
switch pin {
|
||||
case 0:
|
||||
sam.PORT.PMUX0_0 = val
|
||||
case 1:
|
||||
sam.PORT.PMUX0_1 = val
|
||||
case 2:
|
||||
sam.PORT.PMUX0_2 = val
|
||||
case 3:
|
||||
sam.PORT.PMUX0_3 = val
|
||||
case 4:
|
||||
sam.PORT.PMUX0_4 = val
|
||||
case 5:
|
||||
sam.PORT.PMUX0_5 = val
|
||||
case 6:
|
||||
sam.PORT.PMUX0_6 = val
|
||||
case 7:
|
||||
sam.PORT.PMUX0_7 = val
|
||||
case 8:
|
||||
sam.PORT.PMUX0_8 = val
|
||||
case 9:
|
||||
sam.PORT.PMUX0_9 = val
|
||||
case 10:
|
||||
sam.PORT.PMUX0_10 = val
|
||||
case 11:
|
||||
sam.PORT.PMUX0_11 = val
|
||||
case 12:
|
||||
sam.PORT.PMUX0_12 = val
|
||||
case 13:
|
||||
sam.PORT.PMUX0_13 = val
|
||||
case 14:
|
||||
sam.PORT.PMUX0_14 = val
|
||||
case 15:
|
||||
sam.PORT.PMUX0_15 = val
|
||||
}
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func getPinCfg(p uint8) sam.RegValue8 {
|
||||
switch p {
|
||||
case 0:
|
||||
return sam.PORT.PINCFG0_0
|
||||
case 1:
|
||||
return sam.PORT.PINCFG0_1
|
||||
case 2:
|
||||
return sam.PORT.PINCFG0_2
|
||||
case 3:
|
||||
return sam.PORT.PINCFG0_3
|
||||
case 4:
|
||||
return sam.PORT.PINCFG0_4
|
||||
case 5:
|
||||
return sam.PORT.PINCFG0_5
|
||||
case 6:
|
||||
return sam.PORT.PINCFG0_6
|
||||
case 7:
|
||||
return sam.PORT.PINCFG0_7
|
||||
case 8:
|
||||
return sam.PORT.PINCFG0_8
|
||||
case 9:
|
||||
return sam.PORT.PINCFG0_9
|
||||
case 10:
|
||||
return sam.PORT.PINCFG0_10
|
||||
case 11:
|
||||
return sam.PORT.PINCFG0_11
|
||||
case 12:
|
||||
return sam.PORT.PINCFG0_12
|
||||
case 13:
|
||||
return sam.PORT.PINCFG0_13
|
||||
case 14:
|
||||
return sam.PORT.PINCFG0_14
|
||||
case 15:
|
||||
return sam.PORT.PINCFG0_15
|
||||
case 16:
|
||||
return sam.PORT.PINCFG0_16
|
||||
case 17:
|
||||
return sam.PORT.PINCFG0_17
|
||||
case 18:
|
||||
return sam.PORT.PINCFG0_18
|
||||
case 19:
|
||||
return sam.PORT.PINCFG0_19
|
||||
case 20:
|
||||
return sam.PORT.PINCFG0_20
|
||||
case 21:
|
||||
return sam.PORT.PINCFG0_21
|
||||
case 22:
|
||||
return sam.PORT.PINCFG0_22
|
||||
case 23:
|
||||
return sam.PORT.PINCFG0_23
|
||||
case 24:
|
||||
return sam.PORT.PINCFG0_24
|
||||
case 25:
|
||||
return sam.PORT.PINCFG0_25
|
||||
case 26:
|
||||
return sam.PORT.PINCFG0_26
|
||||
case 27:
|
||||
return sam.PORT.PINCFG0_27
|
||||
case 28:
|
||||
return sam.PORT.PINCFG0_28
|
||||
case 29:
|
||||
return sam.PORT.PINCFG0_29
|
||||
case 30:
|
||||
return sam.PORT.PINCFG0_30
|
||||
case 31:
|
||||
return sam.PORT.PINCFG0_31
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func setPinCfg(p uint8, val sam.RegValue8) {
|
||||
switch p {
|
||||
case 0:
|
||||
sam.PORT.PINCFG0_0 = val
|
||||
case 1:
|
||||
sam.PORT.PINCFG0_1 = val
|
||||
case 2:
|
||||
sam.PORT.PINCFG0_2 = val
|
||||
case 3:
|
||||
sam.PORT.PINCFG0_3 = val
|
||||
case 4:
|
||||
sam.PORT.PINCFG0_4 = val
|
||||
case 5:
|
||||
sam.PORT.PINCFG0_5 = val
|
||||
case 6:
|
||||
sam.PORT.PINCFG0_6 = val
|
||||
case 7:
|
||||
sam.PORT.PINCFG0_7 = val
|
||||
case 8:
|
||||
sam.PORT.PINCFG0_8 = val
|
||||
case 9:
|
||||
sam.PORT.PINCFG0_9 = val
|
||||
case 10:
|
||||
sam.PORT.PINCFG0_10 = val
|
||||
case 11:
|
||||
sam.PORT.PINCFG0_11 = val
|
||||
case 12:
|
||||
sam.PORT.PINCFG0_12 = val
|
||||
case 13:
|
||||
sam.PORT.PINCFG0_13 = val
|
||||
case 14:
|
||||
sam.PORT.PINCFG0_14 = val
|
||||
case 15:
|
||||
sam.PORT.PINCFG0_15 = val
|
||||
case 16:
|
||||
sam.PORT.PINCFG0_16 = val
|
||||
case 17:
|
||||
sam.PORT.PINCFG0_17 = val
|
||||
case 18:
|
||||
sam.PORT.PINCFG0_18 = val
|
||||
case 19:
|
||||
sam.PORT.PINCFG0_19 = val
|
||||
case 20:
|
||||
sam.PORT.PINCFG0_20 = val
|
||||
case 21:
|
||||
sam.PORT.PINCFG0_21 = val
|
||||
case 22:
|
||||
sam.PORT.PINCFG0_22 = val
|
||||
case 23:
|
||||
sam.PORT.PINCFG0_23 = val
|
||||
case 24:
|
||||
sam.PORT.PINCFG0_24 = val
|
||||
case 25:
|
||||
sam.PORT.PINCFG0_25 = val
|
||||
case 26:
|
||||
sam.PORT.PINCFG0_26 = val
|
||||
case 27:
|
||||
sam.PORT.PINCFG0_27 = val
|
||||
case 28:
|
||||
sam.PORT.PINCFG0_28 = val
|
||||
case 29:
|
||||
sam.PORT.PINCFG0_29 = val
|
||||
case 30:
|
||||
sam.PORT.PINCFG0_30 = val
|
||||
case 31:
|
||||
sam.PORT.PINCFG0_31 = val
|
||||
}
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case 6:
|
||||
return sam.TCC1
|
||||
case 7:
|
||||
return sam.TCC1
|
||||
case 8:
|
||||
return sam.TCC1
|
||||
case 9:
|
||||
return sam.TCC1
|
||||
case 14:
|
||||
return sam.TCC0
|
||||
case 15:
|
||||
return sam.TCC0
|
||||
case 16:
|
||||
return sam.TCC0
|
||||
case 17:
|
||||
return sam.TCC0
|
||||
case 18:
|
||||
return sam.TCC0
|
||||
case 19:
|
||||
return sam.TCC0
|
||||
case 20:
|
||||
return sam.TCC0
|
||||
case 21:
|
||||
return sam.TCC0
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
// setChannel sets the value for the correct channel for PWM on this pin
|
||||
func (pwm PWM) setChannel(val sam.RegValue) {
|
||||
switch pwm.Pin {
|
||||
case 6:
|
||||
pwm.getTimer().CC0 = val
|
||||
case 7:
|
||||
pwm.getTimer().CC1 = val
|
||||
case 8:
|
||||
pwm.getTimer().CC0 = val
|
||||
case 9:
|
||||
pwm.getTimer().CC1 = val
|
||||
case 14:
|
||||
pwm.getTimer().CC0 = val
|
||||
case 15:
|
||||
pwm.getTimer().CC1 = val
|
||||
case 16:
|
||||
pwm.getTimer().CC2 = val
|
||||
case 17:
|
||||
pwm.getTimer().CC3 = val
|
||||
case 18:
|
||||
pwm.getTimer().CC2 = val
|
||||
case 19:
|
||||
pwm.getTimer().CC3 = val
|
||||
case 20:
|
||||
pwm.getTimer().CC2 = val
|
||||
case 21:
|
||||
pwm.getTimer().CC3 = val
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"device/nrf"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
return nrf.GPIO, p.Pin
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 64000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
return nrf.P0, p.Pin
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 64000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
if p.Pin >= 32 {
|
||||
|
||||
@@ -0,0 +1,599 @@
|
||||
// +build sam
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"device/sam"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const deviceDescriptorSize = 18
|
||||
|
||||
// DeviceDescriptor implements the USB standard device descriptor.
|
||||
//
|
||||
// Table 9-8. Standard Device Descriptor
|
||||
// bLength, bDescriptorType, bcdUSB, bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0,
|
||||
// idVendor, idProduct, bcdDevice, iManufacturer, iProduct, iSerialNumber, bNumConfigurations */
|
||||
//
|
||||
type DeviceDescriptor struct {
|
||||
bLength uint8 // 18
|
||||
bDescriptorType uint8 // 1 USB_DEVICE_DESCRIPTOR_TYPE
|
||||
bcdUSB uint16 // 0x200
|
||||
bDeviceClass uint8
|
||||
bDeviceSubClass uint8
|
||||
bDeviceProtocol uint8
|
||||
bMaxPacketSize0 uint8 // Packet 0
|
||||
idVendor uint16
|
||||
idProduct uint16
|
||||
bcdDevice uint16 // 0x100
|
||||
iManufacturer uint8
|
||||
iProduct uint8
|
||||
iSerialNumber uint8
|
||||
bNumConfigurations uint8
|
||||
}
|
||||
|
||||
// NewDeviceDescriptor returns a USB DeviceDescriptor.
|
||||
func NewDeviceDescriptor(class, subClass, proto, packetSize0 uint8, vid, pid, version uint16, im, ip, is, configs uint8) DeviceDescriptor {
|
||||
return DeviceDescriptor{deviceDescriptorSize, 1, 0x200, class, subClass, proto, packetSize0, vid, pid, version, im, ip, is, configs}
|
||||
}
|
||||
|
||||
// Bytes returns DeviceDescriptor data
|
||||
func (d DeviceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, deviceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bcdUSB)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.bMaxPacketSize0)
|
||||
binary.Write(buf, binary.LittleEndian, d.idVendor)
|
||||
binary.Write(buf, binary.LittleEndian, d.idProduct)
|
||||
binary.Write(buf, binary.LittleEndian, d.bcdDevice)
|
||||
binary.Write(buf, binary.LittleEndian, d.iManufacturer)
|
||||
binary.Write(buf, binary.LittleEndian, d.iProduct)
|
||||
binary.Write(buf, binary.LittleEndian, d.iSerialNumber)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumConfigurations)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const configDescriptorSize = 9
|
||||
|
||||
// ConfigDescriptor implements the standard USB configuration descriptor.
|
||||
//
|
||||
// Table 9-10. Standard Configuration Descriptor
|
||||
// bLength, bDescriptorType, wTotalLength, bNumInterfaces, bConfigurationValue, iConfiguration
|
||||
// bmAttributes, bMaxPower
|
||||
//
|
||||
type ConfigDescriptor struct {
|
||||
bLength uint8 // 9
|
||||
bDescriptorType uint8 // 2
|
||||
wTotalLength uint16 // total length
|
||||
bNumInterfaces uint8
|
||||
bConfigurationValue uint8
|
||||
iConfiguration uint8
|
||||
bmAttributes uint8
|
||||
bMaxPower uint8
|
||||
}
|
||||
|
||||
// NewConfigDescriptor returns a new USB ConfigDescriptor.
|
||||
func NewConfigDescriptor(totalLength uint16, interfaces uint8) ConfigDescriptor {
|
||||
return ConfigDescriptor{configDescriptorSize, 2, totalLength, interfaces, 1, 0, usb_CONFIG_BUS_POWERED | usb_CONFIG_REMOTE_WAKEUP, 50}
|
||||
}
|
||||
|
||||
// Bytes returns ConfigDescriptor data.
|
||||
func (d ConfigDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, configDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.wTotalLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumInterfaces)
|
||||
binary.Write(buf, binary.LittleEndian, d.bConfigurationValue)
|
||||
binary.Write(buf, binary.LittleEndian, d.iConfiguration)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmAttributes)
|
||||
binary.Write(buf, binary.LittleEndian, d.bMaxPower)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const interfaceDescriptorSize = 9
|
||||
|
||||
// InterfaceDescriptor implements the standard USB interface descriptor.
|
||||
//
|
||||
// Table 9-12. Standard Interface Descriptor
|
||||
// bLength, bDescriptorType, bInterfaceNumber, bAlternateSetting, bNumEndpoints, bInterfaceClass,
|
||||
// bInterfaceSubClass, bInterfaceProtocol, iInterface
|
||||
//
|
||||
type InterfaceDescriptor struct {
|
||||
bLength uint8 // 9
|
||||
bDescriptorType uint8 // 4
|
||||
bInterfaceNumber uint8
|
||||
bAlternateSetting uint8
|
||||
bNumEndpoints uint8
|
||||
bInterfaceClass uint8
|
||||
bInterfaceSubClass uint8
|
||||
bInterfaceProtocol uint8
|
||||
iInterface uint8
|
||||
}
|
||||
|
||||
// NewInterfaceDescriptor returns a new USB InterfaceDescriptor.
|
||||
func NewInterfaceDescriptor(n, numEndpoints, class, subClass, protocol uint8) InterfaceDescriptor {
|
||||
return InterfaceDescriptor{interfaceDescriptorSize, 4, n, 0, numEndpoints, class, subClass, protocol, 0}
|
||||
}
|
||||
|
||||
// Bytes returns InterfaceDescriptor data.
|
||||
func (d InterfaceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, interfaceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceNumber)
|
||||
binary.Write(buf, binary.LittleEndian, d.bAlternateSetting)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumEndpoints)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.iInterface)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const endpointDescriptorSize = 7
|
||||
|
||||
// EndpointDescriptor implements the standard USB endpoint descriptor.
|
||||
//
|
||||
// Table 9-13. Standard Endpoint Descriptor
|
||||
// bLength, bDescriptorType, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval
|
||||
//
|
||||
type EndpointDescriptor struct {
|
||||
bLength uint8 // 7
|
||||
bDescriptorType uint8 // 5
|
||||
bEndpointAddress uint8
|
||||
bmAttributes uint8
|
||||
wMaxPacketSize uint16
|
||||
bInterval uint8
|
||||
}
|
||||
|
||||
// NewEndpointDescriptor returns a new USB EndpointDescriptor.
|
||||
func NewEndpointDescriptor(addr, attr uint8, packetSize uint16, interval uint8) EndpointDescriptor {
|
||||
return EndpointDescriptor{endpointDescriptorSize, 5, addr, attr, packetSize, interval}
|
||||
}
|
||||
|
||||
// Bytes returns EndpointDescriptor data.
|
||||
func (d EndpointDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, endpointDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bEndpointAddress)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmAttributes)
|
||||
binary.Write(buf, binary.LittleEndian, d.wMaxPacketSize)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterval)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const iadDescriptorSize = 8
|
||||
|
||||
// IADDescriptor is an Interface Association Descriptor, which is used
|
||||
// to bind 2 interfaces together in CDC composite device.
|
||||
//
|
||||
// Standard Interface Association Descriptor:
|
||||
// bLength, bDescriptorType, bFirstInterface, bInterfaceCount, bFunctionClass, bFunctionSubClass,
|
||||
// bFunctionProtocol, iFunction
|
||||
//
|
||||
type IADDescriptor struct {
|
||||
bLength uint8 // 8
|
||||
bDescriptorType uint8 // 11
|
||||
bFirstInterface uint8
|
||||
bInterfaceCount uint8
|
||||
bFunctionClass uint8
|
||||
bFunctionSubClass uint8
|
||||
bFunctionProtocol uint8
|
||||
iFunction uint8
|
||||
}
|
||||
|
||||
// NewIADDescriptor returns a new USB IADDescriptor.
|
||||
func NewIADDescriptor(firstInterface, count, class, subClass, protocol uint8) IADDescriptor {
|
||||
return IADDescriptor{iadDescriptorSize, 11, firstInterface, count, class, subClass, protocol, 0}
|
||||
}
|
||||
|
||||
// Bytes returns IADDescriptor data.
|
||||
func (d IADDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, iadDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFirstInterface)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceCount)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.iFunction)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const cdcCSInterfaceDescriptorSize = 5
|
||||
|
||||
// CDCCSInterfaceDescriptor is a CDC CS interface descriptor.
|
||||
type CDCCSInterfaceDescriptor struct {
|
||||
len uint8 // 5
|
||||
dtype uint8 // 0x24
|
||||
subtype uint8
|
||||
d0 uint8
|
||||
d1 uint8
|
||||
}
|
||||
|
||||
// NewCDCCSInterfaceDescriptor returns a new USB CDCCSInterfaceDescriptor.
|
||||
func NewCDCCSInterfaceDescriptor(subtype, d0, d1 uint8) CDCCSInterfaceDescriptor {
|
||||
return CDCCSInterfaceDescriptor{cdcCSInterfaceDescriptorSize, 0x24, subtype, d0, d1}
|
||||
}
|
||||
|
||||
// Bytes returns CDCCSInterfaceDescriptor data.
|
||||
func (d CDCCSInterfaceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cdcCSInterfaceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.len)
|
||||
binary.Write(buf, binary.LittleEndian, d.dtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.subtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.d0)
|
||||
binary.Write(buf, binary.LittleEndian, d.d1)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const cmFunctionalDescriptorSize = 5
|
||||
|
||||
// CMFunctionalDescriptor is the functional descriptor general format.
|
||||
type CMFunctionalDescriptor struct {
|
||||
bFunctionLength uint8
|
||||
bDescriptorType uint8 // 0x24
|
||||
bDescriptorSubtype uint8 // 1
|
||||
bmCapabilities uint8
|
||||
bDataInterface uint8
|
||||
}
|
||||
|
||||
// NewCMFunctionalDescriptor returns a new USB CMFunctionalDescriptor.
|
||||
func NewCMFunctionalDescriptor(subtype, d0, d1 uint8) CMFunctionalDescriptor {
|
||||
return CMFunctionalDescriptor{5, 0x24, subtype, d0, d1}
|
||||
}
|
||||
|
||||
// Bytes returns the CMFunctionalDescriptor data.
|
||||
func (d CMFunctionalDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cmFunctionalDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorSubtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmCapabilities)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDataInterface)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const acmFunctionalDescriptorSize = 4
|
||||
|
||||
// ACMFunctionalDescriptor is a Abstract Control Model (ACM) USB descriptor.
|
||||
type ACMFunctionalDescriptor struct {
|
||||
len uint8
|
||||
dtype uint8 // 0x24
|
||||
subtype uint8 // 1
|
||||
bmCapabilities uint8
|
||||
}
|
||||
|
||||
// NewACMFunctionalDescriptor returns a new USB ACMFunctionalDescriptor.
|
||||
func NewACMFunctionalDescriptor(subtype, d0 uint8) ACMFunctionalDescriptor {
|
||||
return ACMFunctionalDescriptor{4, 0x24, subtype, d0}
|
||||
}
|
||||
|
||||
// Bytes returns the ACMFunctionalDescriptor data.
|
||||
func (d ACMFunctionalDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, acmFunctionalDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.len)
|
||||
binary.Write(buf, binary.LittleEndian, d.dtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.subtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmCapabilities)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// CDCDescriptor is the Communication Device Class (CDC) descriptor.
|
||||
type CDCDescriptor struct {
|
||||
// IAD
|
||||
iad IADDescriptor // Only needed on compound device
|
||||
|
||||
// Control
|
||||
cif InterfaceDescriptor
|
||||
header CDCCSInterfaceDescriptor
|
||||
|
||||
// CDC control
|
||||
controlManagement ACMFunctionalDescriptor // ACM
|
||||
functionalDescriptor CDCCSInterfaceDescriptor // CDC_UNION
|
||||
callManagement CMFunctionalDescriptor // Call Management
|
||||
cifin EndpointDescriptor
|
||||
|
||||
// CDC Data
|
||||
dif InterfaceDescriptor
|
||||
in EndpointDescriptor
|
||||
out EndpointDescriptor
|
||||
}
|
||||
|
||||
func NewCDCDescriptor(i IADDescriptor, c InterfaceDescriptor,
|
||||
h CDCCSInterfaceDescriptor,
|
||||
cm ACMFunctionalDescriptor,
|
||||
fd CDCCSInterfaceDescriptor,
|
||||
callm CMFunctionalDescriptor,
|
||||
ci EndpointDescriptor,
|
||||
di InterfaceDescriptor,
|
||||
inp EndpointDescriptor,
|
||||
outp EndpointDescriptor) CDCDescriptor {
|
||||
return CDCDescriptor{iad: i,
|
||||
cif: c,
|
||||
header: h,
|
||||
controlManagement: cm,
|
||||
functionalDescriptor: fd,
|
||||
callManagement: callm,
|
||||
cifin: ci,
|
||||
dif: di,
|
||||
in: inp,
|
||||
out: outp}
|
||||
}
|
||||
|
||||
const cdcSize = iadDescriptorSize +
|
||||
interfaceDescriptorSize +
|
||||
cdcCSInterfaceDescriptorSize +
|
||||
acmFunctionalDescriptorSize +
|
||||
cdcCSInterfaceDescriptorSize +
|
||||
cmFunctionalDescriptorSize +
|
||||
endpointDescriptorSize +
|
||||
interfaceDescriptorSize +
|
||||
endpointDescriptorSize +
|
||||
endpointDescriptorSize
|
||||
|
||||
// Bytes returns CDCDescriptor data.
|
||||
func (d CDCDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cdcSize))
|
||||
buf.Write(d.iad.Bytes())
|
||||
buf.Write(d.cif.Bytes())
|
||||
buf.Write(d.header.Bytes())
|
||||
buf.Write(d.controlManagement.Bytes())
|
||||
buf.Write(d.functionalDescriptor.Bytes())
|
||||
buf.Write(d.callManagement.Bytes())
|
||||
buf.Write(d.cifin.Bytes())
|
||||
buf.Write(d.dif.Bytes())
|
||||
buf.Write(d.in.Bytes())
|
||||
buf.Write(d.out.Bytes())
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// MSCDescriptor is not used yet.
|
||||
type MSCDescriptor struct {
|
||||
msc InterfaceDescriptor
|
||||
in EndpointDescriptor
|
||||
out EndpointDescriptor
|
||||
}
|
||||
|
||||
type cdcLineInfo struct {
|
||||
dwDTERate uint32
|
||||
bCharFormat uint8
|
||||
bParityType uint8
|
||||
bDataBits uint8
|
||||
lineState uint8
|
||||
}
|
||||
|
||||
var (
|
||||
// TODO: allow setting these
|
||||
usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English
|
||||
usb_STRING_PRODUCT = "Arduino Zero"
|
||||
usb_STRING_MANUFACTURER = "Arduino"
|
||||
|
||||
usb_VID uint16 = 0x2341
|
||||
usb_PID uint16 = 0x004d
|
||||
)
|
||||
|
||||
const (
|
||||
usb_IMANUFACTURER = 1
|
||||
usb_IPRODUCT = 2
|
||||
usb_ISERIAL = 3
|
||||
|
||||
usb_ENDPOINT_TYPE_CONTROL = 0x00
|
||||
usb_ENDPOINT_TYPE_ISOCHRONOUS = 0x01
|
||||
usb_ENDPOINT_TYPE_BULK = 0x02
|
||||
usb_ENDPOINT_TYPE_INTERRUPT = 0x03
|
||||
|
||||
usb_DEVICE_DESCRIPTOR_TYPE = 1
|
||||
usb_CONFIGURATION_DESCRIPTOR_TYPE = 2
|
||||
usb_STRING_DESCRIPTOR_TYPE = 3
|
||||
usb_INTERFACE_DESCRIPTOR_TYPE = 4
|
||||
usb_ENDPOINT_DESCRIPTOR_TYPE = 5
|
||||
usb_DEVICE_QUALIFIER = 6
|
||||
usb_OTHER_SPEED_CONFIGURATION = 7
|
||||
|
||||
usbEndpointOut = 0x00
|
||||
usbEndpointIn = 0x80
|
||||
|
||||
usbEndpointPacketSize = 64 // 64 for Full Speed, EPT size max is 1024
|
||||
usb_EPT_NUM = 7
|
||||
|
||||
// standard requests
|
||||
usb_GET_STATUS = 0
|
||||
usb_CLEAR_FEATURE = 1
|
||||
usb_SET_FEATURE = 3
|
||||
usb_SET_ADDRESS = 5
|
||||
usb_GET_DESCRIPTOR = 6
|
||||
usb_SET_DESCRIPTOR = 7
|
||||
usb_GET_CONFIGURATION = 8
|
||||
usb_SET_CONFIGURATION = 9
|
||||
usb_GET_INTERFACE = 10
|
||||
usb_SET_INTERFACE = 11
|
||||
|
||||
usb_DEVICE_CLASS_COMMUNICATIONS = 0x02
|
||||
usb_DEVICE_CLASS_HUMAN_INTERFACE = 0x03
|
||||
usb_DEVICE_CLASS_STORAGE = 0x08
|
||||
usb_DEVICE_CLASS_VENDOR_SPECIFIC = 0xFF
|
||||
|
||||
usb_CONFIG_POWERED_MASK = 0x40
|
||||
usb_CONFIG_BUS_POWERED = 0x80
|
||||
usb_CONFIG_SELF_POWERED = 0xC0
|
||||
usb_CONFIG_REMOTE_WAKEUP = 0x20
|
||||
|
||||
// CDC
|
||||
usb_CDC_ACM_INTERFACE = 0 // CDC ACM
|
||||
usb_CDC_DATA_INTERFACE = 1 // CDC Data
|
||||
usb_CDC_FIRST_ENDPOINT = 1
|
||||
usb_CDC_ENDPOINT_ACM = 1
|
||||
usb_CDC_ENDPOINT_OUT = 2
|
||||
usb_CDC_ENDPOINT_IN = 3
|
||||
|
||||
// bmRequestType
|
||||
usb_REQUEST_HOSTTODEVICE = 0x00
|
||||
usb_REQUEST_DEVICETOHOST = 0x80
|
||||
usb_REQUEST_DIRECTION = 0x80
|
||||
|
||||
usb_REQUEST_STANDARD = 0x00
|
||||
usb_REQUEST_CLASS = 0x20
|
||||
usb_REQUEST_VENDOR = 0x40
|
||||
usb_REQUEST_TYPE = 0x60
|
||||
|
||||
usb_REQUEST_DEVICE = 0x00
|
||||
usb_REQUEST_INTERFACE = 0x01
|
||||
usb_REQUEST_ENDPOINT = 0x02
|
||||
usb_REQUEST_OTHER = 0x03
|
||||
usb_REQUEST_RECIPIENT = 0x1F
|
||||
|
||||
usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE)
|
||||
|
||||
// CDC Class requests
|
||||
usb_CDC_SET_LINE_CODING = 0x20
|
||||
usb_CDC_GET_LINE_CODING = 0x21
|
||||
usb_CDC_SET_CONTROL_LINE_STATE = 0x22
|
||||
usb_CDC_SEND_BREAK = 0x23
|
||||
|
||||
usb_CDC_V1_10 = 0x0110
|
||||
usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02
|
||||
|
||||
usb_CDC_CALL_MANAGEMENT = 0x01
|
||||
usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02
|
||||
usb_CDC_HEADER = 0x00
|
||||
usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02
|
||||
usb_CDC_UNION = 0x06
|
||||
usb_CDC_CS_INTERFACE = 0x24
|
||||
usb_CDC_CS_ENDPOINT = 0x25
|
||||
usb_CDC_DATA_INTERFACE_CLASS = 0x0A
|
||||
)
|
||||
|
||||
// usbDeviceDescBank is the USB device endpoint descriptor.
|
||||
// typedef struct {
|
||||
// __IO USB_DEVICE_ADDR_Type ADDR; /**< \brief Offset: 0x000 (R/W 32) DEVICE_DESC_BANK Endpoint Bank, Adress of Data Buffer */
|
||||
// __IO USB_DEVICE_PCKSIZE_Type PCKSIZE; /**< \brief Offset: 0x004 (R/W 32) DEVICE_DESC_BANK Endpoint Bank, Packet Size */
|
||||
// __IO USB_DEVICE_EXTREG_Type EXTREG; /**< \brief Offset: 0x008 (R/W 16) DEVICE_DESC_BANK Endpoint Bank, Extended */
|
||||
// __IO USB_DEVICE_STATUS_BK_Type STATUS_BK; /**< \brief Offset: 0x00A (R/W 8) DEVICE_DESC_BANK Enpoint Bank, Status of Bank */
|
||||
// RoReg8 Reserved1[0x5];
|
||||
// } UsbDeviceDescBank;
|
||||
type usbDeviceDescBank struct {
|
||||
ADDR sam.RegValue
|
||||
PCKSIZE sam.RegValue
|
||||
EXTREG sam.RegValue16
|
||||
STATUS_BK sam.RegValue8
|
||||
_reserved [5]sam.RegValue8
|
||||
}
|
||||
|
||||
type usbDeviceDescriptor struct {
|
||||
DeviceDescBank [2]usbDeviceDescBank
|
||||
}
|
||||
|
||||
// typedef struct {
|
||||
// union {
|
||||
// uint8_t bmRequestType;
|
||||
// struct {
|
||||
// uint8_t direction : 5;
|
||||
// uint8_t type : 2;
|
||||
// uint8_t transferDirection : 1;
|
||||
// };
|
||||
// };
|
||||
// uint8_t bRequest;
|
||||
// uint8_t wValueL;
|
||||
// uint8_t wValueH;
|
||||
// uint16_t wIndex;
|
||||
// uint16_t wLength;
|
||||
// } USBSetup;
|
||||
type usbSetup struct {
|
||||
bmRequestType uint8
|
||||
bRequest uint8
|
||||
wValueL uint8
|
||||
wValueH uint8
|
||||
wIndex uint16
|
||||
wLength uint16
|
||||
}
|
||||
|
||||
func newUSBSetup(data []byte) usbSetup {
|
||||
buf := bytes.NewBuffer(data)
|
||||
u := usbSetup{}
|
||||
binary.Read(buf, binary.LittleEndian, &(u.bmRequestType))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.bRequest))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wValueL))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wValueH))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wIndex))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wLength))
|
||||
return u
|
||||
}
|
||||
|
||||
// USBCDC is the serial interface that works over the USB port.
|
||||
// To implement the USBCDC interface for a board, you must declare a concrete type as follows:
|
||||
//
|
||||
// type USBCDC struct {
|
||||
// Buffer *RingBuffer
|
||||
// }
|
||||
//
|
||||
// You can also add additional members to this struct depending on your implementation,
|
||||
// but the *RingBuffer is required.
|
||||
// When you are declaring the USBCDC for your board, make sure that you also declare the
|
||||
// RingBuffer using the NewRingBuffer() function:
|
||||
//
|
||||
// USBCDC{Buffer: NewRingBuffer()}
|
||||
//
|
||||
|
||||
// Read from the RX buffer.
|
||||
func (usbcdc USBCDC) Read(data []byte) (n int, err error) {
|
||||
// check if RX buffer is empty
|
||||
size := usbcdc.Buffered()
|
||||
if size == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Make sure we do not read more from buffer than the data slice can hold.
|
||||
if len(data) < size {
|
||||
size = len(data)
|
||||
}
|
||||
|
||||
// only read number of bytes used from buffer
|
||||
for i := 0; i < size; i++ {
|
||||
v, _ := usbcdc.ReadByte()
|
||||
data[i] = v
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// Write data to the USBCDC.
|
||||
func (usbcdc USBCDC) Write(data []byte) (n int, err error) {
|
||||
for _, v := range data {
|
||||
usbcdc.WriteByte(v)
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the RX buffer.
|
||||
// If there is no data in the buffer, returns an error.
|
||||
func (usbcdc USBCDC) ReadByte() (byte, error) {
|
||||
// check if RX buffer is empty
|
||||
buf, ok := usbcdc.Buffer.Get()
|
||||
if !ok {
|
||||
return 0, errors.New("Buffer empty")
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
func (usbcdc USBCDC) Buffered() int {
|
||||
return int(usbcdc.Buffer.Used())
|
||||
}
|
||||
|
||||
// Receive handles adding data to the UART's data buffer.
|
||||
// Usually called by the IRQ handler for a machine.
|
||||
func (usbcdc USBCDC) Receive(data byte) {
|
||||
usbcdc.Buffer.Put(data)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Portable analogs of some common system call errors.
|
||||
var (
|
||||
ErrUnsupported = errors.New("operation not supported")
|
||||
)
|
||||
|
||||
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
||||
// standard output, and standard error file descriptors.
|
||||
var (
|
||||
Stdin = &File{0, "/dev/stdin"}
|
||||
Stdout = &File{1, "/dev/stdout"}
|
||||
Stderr = &File{2, "/dev/stderr"}
|
||||
)
|
||||
|
||||
// File represents an open file descriptor.
|
||||
type File struct {
|
||||
fd uintptr
|
||||
name string
|
||||
}
|
||||
|
||||
// NewFile returns a new File with the given file descriptor and name.
|
||||
func NewFile(fd uintptr, name string) *File {
|
||||
return &File{fd, name}
|
||||
}
|
||||
|
||||
// Fd returns the integer Unix file descriptor referencing the open file. The
|
||||
// file descriptor is valid only until f.Close is called.
|
||||
func (f *File) Fd() uintptr {
|
||||
return f.fd
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// +build darwin linux
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Read reads up to len(b) bytes from the File. It returns the number of bytes
|
||||
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
return syscall.Read(int(f.fd), b)
|
||||
}
|
||||
|
||||
// Write writes len(b) bytes to the File. It returns the number of bytes written
|
||||
// and an error, if any. Write returns a non-nil error when n != len(b).
|
||||
func (f *File) Write(b []byte) (n int, err error) {
|
||||
return syscall.Write(int(f.fd), b)
|
||||
}
|
||||
|
||||
// Close closes the File, rendering it unusable for I/O.
|
||||
func (f *File) Close() error {
|
||||
return syscall.Close(int(f.fd))
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// +build wasm
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
// Read is unsupported on this system.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
return 0, ErrUnsupported
|
||||
}
|
||||
|
||||
// Write writes len(b) bytes to the output. It returns the number of bytes
|
||||
// written or an error if this file is not stdout or stderr.
|
||||
func (f *File) Write(b []byte) (n int, err error) {
|
||||
switch f.fd {
|
||||
case Stdout.fd, Stderr.fd:
|
||||
for _, c := range b {
|
||||
putchar(c)
|
||||
}
|
||||
return len(b), nil
|
||||
default:
|
||||
return 0, ErrUnsupported
|
||||
}
|
||||
}
|
||||
|
||||
// Close is unsupported on this system.
|
||||
func (f *File) Close() error {
|
||||
return ErrUnsupported
|
||||
}
|
||||
|
||||
//go:linkname putchar runtime.putchar
|
||||
func putchar(c byte)
|
||||
+137
-11
@@ -1,9 +1,29 @@
|
||||
package reflect
|
||||
|
||||
// A Kind is the number that the compiler uses for this type.
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// The compiler uses a compact encoding to store type information. Unlike the
|
||||
// main Go compiler, most of the types are stored directly in the type code.
|
||||
//
|
||||
// Not used directly. These types are all replaced with the number the compiler
|
||||
// uses internally for the type.
|
||||
// Type code bit allocation:
|
||||
// xxxxx0: basic types, where xxxxx is the basic type number (never 0).
|
||||
// The higher bits indicate the named type, if any.
|
||||
// nxxx1: complex types, where n indicates whether this is a named type (named
|
||||
// if set) and xxx contains the type kind number:
|
||||
// 0 (0001): Chan
|
||||
// 1 (0011): Interface
|
||||
// 2 (0101): Ptr
|
||||
// 3 (0111): Slice
|
||||
// 4 (1001): Array
|
||||
// 5 (1011): Func
|
||||
// 6 (1101): Map
|
||||
// 7 (1111): Struct
|
||||
// The higher bits are either the contents of the type depending on the
|
||||
// type (if n is clear) or indicate the number of the named type (if n
|
||||
// is set).
|
||||
|
||||
type Kind uintptr
|
||||
|
||||
// Copied from reflect/type.go
|
||||
@@ -26,18 +46,82 @@ const (
|
||||
Float64
|
||||
Complex64
|
||||
Complex128
|
||||
Array
|
||||
String
|
||||
UnsafePointer
|
||||
Chan
|
||||
Func
|
||||
Interface
|
||||
Map
|
||||
Ptr
|
||||
Slice
|
||||
String
|
||||
Array
|
||||
Func
|
||||
Map
|
||||
Struct
|
||||
UnsafePointer
|
||||
)
|
||||
|
||||
func (k Kind) String() string {
|
||||
switch k {
|
||||
case Bool:
|
||||
return "bool"
|
||||
case Int:
|
||||
return "int"
|
||||
case Int8:
|
||||
return "int8"
|
||||
case Int16:
|
||||
return "int16"
|
||||
case Int32:
|
||||
return "int32"
|
||||
case Int64:
|
||||
return "int64"
|
||||
case Uint:
|
||||
return "uint"
|
||||
case Uint8:
|
||||
return "uint8"
|
||||
case Uint16:
|
||||
return "uint16"
|
||||
case Uint32:
|
||||
return "uint32"
|
||||
case Uint64:
|
||||
return "uint64"
|
||||
case Uintptr:
|
||||
return "uintptr"
|
||||
case Float32:
|
||||
return "float32"
|
||||
case Float64:
|
||||
return "float64"
|
||||
case Complex64:
|
||||
return "complex64"
|
||||
case Complex128:
|
||||
return "complex128"
|
||||
case String:
|
||||
return "string"
|
||||
case UnsafePointer:
|
||||
return "unsafe.Pointer"
|
||||
case Chan:
|
||||
return "chan"
|
||||
case Interface:
|
||||
return "interface"
|
||||
case Ptr:
|
||||
return "ptr"
|
||||
case Slice:
|
||||
return "slice"
|
||||
case Array:
|
||||
return "array"
|
||||
case Func:
|
||||
return "func"
|
||||
case Map:
|
||||
return "map"
|
||||
case Struct:
|
||||
return "struct"
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
// basicType returns a new Type for this kind if Kind is a basic type.
|
||||
func (k Kind) basicType() Type {
|
||||
return Type(k << 1)
|
||||
}
|
||||
|
||||
// The typecode as used in an interface{}.
|
||||
type Type uintptr
|
||||
|
||||
@@ -50,11 +134,24 @@ func (t Type) String() string {
|
||||
}
|
||||
|
||||
func (t Type) Kind() Kind {
|
||||
return Invalid // TODO
|
||||
if t % 2 == 0 {
|
||||
// basic type
|
||||
return Kind((t >> 1) % 32)
|
||||
} else {
|
||||
return Kind(t >> 1) % 8 + 19
|
||||
}
|
||||
}
|
||||
|
||||
func (t Type) Elem() Type {
|
||||
panic("unimplemented: (reflect.Type).Elem()")
|
||||
switch t.Kind() {
|
||||
case Chan, Ptr, Slice:
|
||||
if (t >> 4) % 2 != 0 {
|
||||
panic("unimplemented: (reflect.Type).Elem() for named types")
|
||||
}
|
||||
return t >> 5
|
||||
default: // not implemented: Array, Map
|
||||
panic("unimplemented: (reflect.Type).Elem()")
|
||||
}
|
||||
}
|
||||
|
||||
func (t Type) Field(i int) StructField {
|
||||
@@ -74,7 +171,36 @@ func (t Type) NumField() int {
|
||||
}
|
||||
|
||||
func (t Type) Size() uintptr {
|
||||
panic("unimplemented: (reflect.Type).Size()")
|
||||
switch t.Kind() {
|
||||
case Bool, Int8, Uint8:
|
||||
return 1
|
||||
case Int16, Uint16:
|
||||
return 2
|
||||
case Int32, Uint32:
|
||||
return 4
|
||||
case Int64, Uint64:
|
||||
return 8
|
||||
case Int, Uint:
|
||||
return unsafe.Sizeof(int(0))
|
||||
case Uintptr:
|
||||
return unsafe.Sizeof(uintptr(0))
|
||||
case Float32:
|
||||
return 4
|
||||
case Float64:
|
||||
return 8
|
||||
case Complex64:
|
||||
return 8
|
||||
case Complex128:
|
||||
return 16
|
||||
case String:
|
||||
return unsafe.Sizeof(StringHeader{})
|
||||
case UnsafePointer, Chan, Map, Ptr:
|
||||
return unsafe.Sizeof(uintptr(0))
|
||||
case Slice:
|
||||
return unsafe.Sizeof(SliceHeader{})
|
||||
default:
|
||||
panic("unimplemented: size of type")
|
||||
}
|
||||
}
|
||||
|
||||
type StructField struct {
|
||||
|
||||
+396
-31
@@ -1,42 +1,94 @@
|
||||
package reflect
|
||||
|
||||
import (
|
||||
_ "unsafe" // for go:linkname
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// This is the same thing as an interface{}.
|
||||
type Value struct {
|
||||
typecode Type
|
||||
value *uint8
|
||||
value unsafe.Pointer
|
||||
indirect bool
|
||||
}
|
||||
|
||||
func Indirect(v Value) Value {
|
||||
return v
|
||||
if v.Kind() != Ptr {
|
||||
return v
|
||||
}
|
||||
return v.Elem()
|
||||
}
|
||||
|
||||
func ValueOf(i interface{}) Value
|
||||
|
||||
//go:linkname _ValueOf reflect.ValueOf
|
||||
func _ValueOf(i Value) Value {
|
||||
return i
|
||||
func ValueOf(i interface{}) Value {
|
||||
v := (*interfaceHeader)(unsafe.Pointer(&i))
|
||||
return Value{
|
||||
typecode: v.typecode,
|
||||
value: v.value,
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Interface() interface{}
|
||||
func (v Value) Interface() interface{} {
|
||||
i := interfaceHeader{
|
||||
typecode: v.typecode,
|
||||
value: v.value,
|
||||
}
|
||||
if v.indirect && v.Type().Size() <= unsafe.Sizeof(uintptr(0)) {
|
||||
// Value was indirect but must be put back directly in the interface
|
||||
// value.
|
||||
var value uintptr
|
||||
for j := v.Type().Size(); j != 0; j-- {
|
||||
value = (value << 8) | uintptr(*(*uint8)(unsafe.Pointer(uintptr(v.value) + j - 1)))
|
||||
}
|
||||
i.value = unsafe.Pointer(value)
|
||||
}
|
||||
return *(*interface{})(unsafe.Pointer(&i))
|
||||
}
|
||||
|
||||
func (v Value) Type() Type {
|
||||
return v.typecode
|
||||
}
|
||||
|
||||
func (v Value) Kind() Kind {
|
||||
return Invalid // TODO
|
||||
return v.Type().Kind()
|
||||
}
|
||||
|
||||
func (v Value) IsNil() bool {
|
||||
panic("unimplemented: (reflect.Value).IsNil()")
|
||||
switch v.Kind() {
|
||||
case Chan, Map, Ptr:
|
||||
return v.value == nil
|
||||
case Func:
|
||||
if v.value == nil {
|
||||
return true
|
||||
}
|
||||
fn := (*funcHeader)(v.value)
|
||||
return fn.Code == nil
|
||||
case Slice:
|
||||
if v.value == nil {
|
||||
return true
|
||||
}
|
||||
slice := (*SliceHeader)(v.value)
|
||||
return slice.Data == 0
|
||||
case Interface:
|
||||
if v.value == nil {
|
||||
return true
|
||||
}
|
||||
itf := (*interfaceHeader)(v.value)
|
||||
return itf.value == nil
|
||||
default:
|
||||
panic(&ValueError{"IsNil"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Pointer() uintptr {
|
||||
panic("unimplemented: (reflect.Value).Pointer()")
|
||||
switch v.Kind() {
|
||||
case Chan, Map, Ptr, UnsafePointer:
|
||||
return uintptr(v.value)
|
||||
case Slice:
|
||||
slice := (*SliceHeader)(v.value)
|
||||
return slice.Data
|
||||
case Func:
|
||||
panic("unimplemented: (reflect.Value).Pointer()")
|
||||
default: // not implemented: Func
|
||||
panic(&ValueError{"Pointer"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) IsValid() bool {
|
||||
@@ -44,7 +96,8 @@ func (v Value) IsValid() bool {
|
||||
}
|
||||
|
||||
func (v Value) CanInterface() bool {
|
||||
panic("unimplemented: (reflect.Value).CanInterface()")
|
||||
// No Value types of private data can be constructed at the moment.
|
||||
return true
|
||||
}
|
||||
|
||||
func (v Value) CanAddr() bool {
|
||||
@@ -56,31 +109,160 @@ func (v Value) Addr() Value {
|
||||
}
|
||||
|
||||
func (v Value) CanSet() bool {
|
||||
panic("unimplemented: (reflect.Value).CanSet()")
|
||||
return v.indirect
|
||||
}
|
||||
|
||||
func (v Value) Bool() bool {
|
||||
panic("unimplemented: (reflect.Value).Bool()")
|
||||
switch v.Kind() {
|
||||
case Bool:
|
||||
if v.indirect {
|
||||
return *((*bool)(v.value))
|
||||
} else {
|
||||
return uintptr(v.value) != 0
|
||||
}
|
||||
default:
|
||||
panic(&ValueError{"Bool"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Int() int64 {
|
||||
panic("unimplemented: (reflect.Value).Int()")
|
||||
switch v.Kind() {
|
||||
case Int:
|
||||
if v.indirect || unsafe.Sizeof(int(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
return int64(*(*int)(v.value))
|
||||
} else {
|
||||
return int64(int(uintptr(v.value)))
|
||||
}
|
||||
case Int8:
|
||||
if v.indirect {
|
||||
return int64(*(*int8)(v.value))
|
||||
} else {
|
||||
return int64(int8(uintptr(v.value)))
|
||||
}
|
||||
case Int16:
|
||||
if v.indirect {
|
||||
return int64(*(*int16)(v.value))
|
||||
} else {
|
||||
return int64(int16(uintptr(v.value)))
|
||||
}
|
||||
case Int32:
|
||||
if v.indirect || unsafe.Sizeof(int32(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
return int64(*(*int32)(v.value))
|
||||
} else {
|
||||
return int64(int32(uintptr(v.value)))
|
||||
}
|
||||
case Int64:
|
||||
if v.indirect || unsafe.Sizeof(int64(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
return int64(*(*int64)(v.value))
|
||||
} else {
|
||||
return int64(int64(uintptr(v.value)))
|
||||
}
|
||||
default:
|
||||
panic(&ValueError{"Int"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Uint() uint64 {
|
||||
panic("unimplemented: (reflect.Value).Uint()")
|
||||
switch v.Kind() {
|
||||
case Uintptr:
|
||||
if v.indirect {
|
||||
return uint64(*(*uintptr)(v.value))
|
||||
} else {
|
||||
return uint64(uintptr(v.value))
|
||||
}
|
||||
case Uint8:
|
||||
if v.indirect {
|
||||
return uint64(*(*uint8)(v.value))
|
||||
} else {
|
||||
return uint64(uintptr(v.value))
|
||||
}
|
||||
case Uint16:
|
||||
if v.indirect {
|
||||
return uint64(*(*uint16)(v.value))
|
||||
} else {
|
||||
return uint64(uintptr(v.value))
|
||||
}
|
||||
case Uint:
|
||||
if v.indirect || unsafe.Sizeof(uint(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
return uint64(*(*uint)(v.value))
|
||||
} else {
|
||||
return uint64(uintptr(v.value))
|
||||
}
|
||||
case Uint32:
|
||||
if v.indirect || unsafe.Sizeof(uint32(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
return uint64(*(*uint32)(v.value))
|
||||
} else {
|
||||
return uint64(uintptr(v.value))
|
||||
}
|
||||
case Uint64:
|
||||
if v.indirect || unsafe.Sizeof(uint64(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
return uint64(*(*uint64)(v.value))
|
||||
} else {
|
||||
return uint64(uintptr(v.value))
|
||||
}
|
||||
default:
|
||||
panic(&ValueError{"Uint"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Float() float64 {
|
||||
panic("unimplemented: (reflect.Value).Float()")
|
||||
switch v.Kind() {
|
||||
case Float32:
|
||||
if v.indirect || unsafe.Sizeof(float32(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
// The float is stored as an external value on systems with 16-bit
|
||||
// pointers.
|
||||
return float64(*(*float32)(v.value))
|
||||
} else {
|
||||
// The float is directly stored in the interface value on systems
|
||||
// with 32-bit and 64-bit pointers.
|
||||
return float64(*(*float32)(unsafe.Pointer(&v.value)))
|
||||
}
|
||||
case Float64:
|
||||
if v.indirect || unsafe.Sizeof(float64(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
// For systems with 16-bit and 32-bit pointers.
|
||||
return *(*float64)(v.value)
|
||||
} else {
|
||||
// The float is directly stored in the interface value on systems
|
||||
// with 64-bit pointers.
|
||||
return *(*float64)(unsafe.Pointer(&v.value))
|
||||
}
|
||||
default:
|
||||
panic(&ValueError{"Float"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Complex() complex128 {
|
||||
panic("unimplemented: (reflect.Value).Complex()")
|
||||
switch v.Kind() {
|
||||
case Complex64:
|
||||
if v.indirect || unsafe.Sizeof(complex64(0)) > unsafe.Sizeof(uintptr(0)) {
|
||||
// The complex number is stored as an external value on systems with
|
||||
// 16-bit and 32-bit pointers.
|
||||
return complex128(*(*complex64)(v.value))
|
||||
} else {
|
||||
// The complex number is directly stored in the interface value on
|
||||
// systems with 64-bit pointers.
|
||||
return complex128(*(*complex64)(unsafe.Pointer(&v.value)))
|
||||
}
|
||||
case Complex128:
|
||||
// This is a 128-bit value, which is always stored as an external value.
|
||||
// It may be stored in the pointer directly on very uncommon
|
||||
// architectures with 128-bit pointers, however.
|
||||
return *(*complex128)(v.value)
|
||||
default:
|
||||
panic(&ValueError{"Complex"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) String() string {
|
||||
panic("unimplemented: (reflect.Value).String()")
|
||||
switch v.Kind() {
|
||||
case String:
|
||||
// A string value is always bigger than a pointer as it is made of a
|
||||
// pointer and a length.
|
||||
return *(*string)(v.value)
|
||||
default:
|
||||
// Special case because of the special treatment of .String() in Go.
|
||||
return "<T>"
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Bytes() []byte {
|
||||
@@ -92,7 +274,25 @@ func (v Value) Slice(i, j int) Value {
|
||||
}
|
||||
|
||||
func (v Value) Len() int {
|
||||
panic("unimplemented: (reflect.Value).Len()")
|
||||
t := v.Type()
|
||||
switch t.Kind() {
|
||||
case Slice:
|
||||
return int((*SliceHeader)(v.value).Len)
|
||||
case String:
|
||||
return int((*StringHeader)(v.value).Len)
|
||||
default: // Array, Chan, Map
|
||||
panic("unimplemented: (reflect.Value).Len()")
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Cap() int {
|
||||
t := v.Type()
|
||||
switch t.Kind() {
|
||||
case Slice:
|
||||
return int((*SliceHeader)(v.value).Cap)
|
||||
default: // Array, Chan
|
||||
panic("unimplemented: (reflect.Value).Cap()")
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) NumField() int {
|
||||
@@ -100,7 +300,23 @@ func (v Value) NumField() int {
|
||||
}
|
||||
|
||||
func (v Value) Elem() Value {
|
||||
panic("unimplemented: (reflect.Value).Elem()")
|
||||
switch v.Kind() {
|
||||
case Ptr:
|
||||
ptr := v.value
|
||||
if v.indirect {
|
||||
ptr = *(*unsafe.Pointer)(ptr)
|
||||
}
|
||||
if ptr == nil {
|
||||
return Value{}
|
||||
}
|
||||
return Value{
|
||||
typecode: v.Type().Elem(),
|
||||
value: ptr,
|
||||
indirect: true,
|
||||
}
|
||||
default: // not implemented: Interface
|
||||
panic(&ValueError{"Elem"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) Field(i int) Value {
|
||||
@@ -108,7 +324,37 @@ func (v Value) Field(i int) Value {
|
||||
}
|
||||
|
||||
func (v Value) Index(i int) Value {
|
||||
panic("unimplemented: (reflect.Value).Index()")
|
||||
switch v.Kind() {
|
||||
case Slice:
|
||||
// Extract an element from the slice.
|
||||
slice := *(*SliceHeader)(v.value)
|
||||
if uint(i) >= uint(slice.Len) {
|
||||
panic("reflect: slice index out of range")
|
||||
}
|
||||
elem := Value{
|
||||
typecode: v.Type().Elem(),
|
||||
indirect: true,
|
||||
}
|
||||
addr := uintptr(slice.Data) + elem.Type().Size() * uintptr(i) // pointer to new value
|
||||
elem.value = unsafe.Pointer(addr)
|
||||
return elem
|
||||
case String:
|
||||
// Extract a character from a string.
|
||||
// A string is never stored directly in the interface, but always as a
|
||||
// pointer to the string value.
|
||||
s := *(*StringHeader)(v.value)
|
||||
if uint(i) >= uint(s.Len) {
|
||||
panic("reflect: string index out of range")
|
||||
}
|
||||
return Value{
|
||||
typecode: Uint8.basicType(),
|
||||
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(s.Data + uintptr(i))))),
|
||||
}
|
||||
case Array:
|
||||
panic("unimplemented: (reflect.Value).Index()")
|
||||
default:
|
||||
panic(&ValueError{"Index"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) MapKeys() []Value {
|
||||
@@ -120,33 +366,152 @@ func (v Value) MapIndex(key Value) Value {
|
||||
}
|
||||
|
||||
func (v Value) Set(x Value) {
|
||||
panic("unimplemented: (reflect.Value).Set()")
|
||||
if !v.indirect {
|
||||
panic("reflect: value is not addressable")
|
||||
}
|
||||
if v.Type() != x.Type() {
|
||||
if v.Kind() == Interface {
|
||||
panic("reflect: unimplemented: assigning to interface of different type")
|
||||
} else {
|
||||
panic("reflect: cannot assign")
|
||||
}
|
||||
}
|
||||
size := v.Type().Size()
|
||||
xptr := x.value
|
||||
if size <= unsafe.Sizeof(uintptr(0)) && !x.indirect {
|
||||
value := x.value
|
||||
xptr = unsafe.Pointer(&value)
|
||||
}
|
||||
memcpy(v.value, xptr, size)
|
||||
}
|
||||
|
||||
func (v Value) SetBool(x bool) {
|
||||
panic("unimplemented: (reflect.Value).SetBool()")
|
||||
if !v.indirect {
|
||||
panic("reflect: value is not addressable")
|
||||
}
|
||||
switch v.Kind() {
|
||||
case Bool:
|
||||
*(*bool)(v.value) = x
|
||||
default:
|
||||
panic(&ValueError{"SetBool"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) SetInt(x int64) {
|
||||
panic("unimplemented: (reflect.Value).SetInt()")
|
||||
if !v.indirect {
|
||||
panic("reflect: value is not addressable")
|
||||
}
|
||||
switch v.Kind() {
|
||||
case Int:
|
||||
*(*int)(v.value) = int(x)
|
||||
case Int8:
|
||||
*(*int8)(v.value) = int8(x)
|
||||
case Int16:
|
||||
*(*int16)(v.value) = int16(x)
|
||||
case Int32:
|
||||
*(*int32)(v.value) = int32(x)
|
||||
case Int64:
|
||||
*(*int64)(v.value) = x
|
||||
default:
|
||||
panic(&ValueError{"SetInt"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) SetUint(x uint64) {
|
||||
panic("unimplemented: (reflect.Value).SetUint()")
|
||||
if !v.indirect {
|
||||
panic("reflect: value is not addressable")
|
||||
}
|
||||
switch v.Kind() {
|
||||
case Uint:
|
||||
*(*uint)(v.value) = uint(x)
|
||||
case Uint8:
|
||||
*(*uint8)(v.value) = uint8(x)
|
||||
case Uint16:
|
||||
*(*uint16)(v.value) = uint16(x)
|
||||
case Uint32:
|
||||
*(*uint32)(v.value) = uint32(x)
|
||||
case Uint64:
|
||||
*(*uint64)(v.value) = x
|
||||
case Uintptr:
|
||||
*(*uintptr)(v.value) = uintptr(x)
|
||||
default:
|
||||
panic(&ValueError{"SetUint"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) SetFloat(x float64) {
|
||||
panic("unimplemented: (reflect.Value).SetFloat()")
|
||||
if !v.indirect {
|
||||
panic("reflect: value is not addressable")
|
||||
}
|
||||
switch v.Kind() {
|
||||
case Float32:
|
||||
*(*float32)(v.value) = float32(x)
|
||||
case Float64:
|
||||
*(*float64)(v.value) = x
|
||||
default:
|
||||
panic(&ValueError{"SetFloat"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) SetComplex(x complex128) {
|
||||
panic("unimplemented: (reflect.Value).SetComplex()")
|
||||
if !v.indirect {
|
||||
panic("reflect: value is not addressable")
|
||||
}
|
||||
switch v.Kind() {
|
||||
case Complex64:
|
||||
*(*complex64)(v.value) = complex64(x)
|
||||
case Complex128:
|
||||
*(*complex128)(v.value) = x
|
||||
default:
|
||||
panic(&ValueError{"SetComplex"})
|
||||
}
|
||||
}
|
||||
|
||||
func (v Value) SetString(x string) {
|
||||
panic("unimplemented: (reflect.Value).SetString()")
|
||||
if !v.indirect {
|
||||
panic("reflect: value is not addressable")
|
||||
}
|
||||
switch v.Kind() {
|
||||
case String:
|
||||
*(*string)(v.value) = x
|
||||
default:
|
||||
panic(&ValueError{"SetString"})
|
||||
}
|
||||
}
|
||||
|
||||
func MakeSlice(typ Type, len, cap int) Value {
|
||||
panic("unimplemented: reflect.MakeSlice()")
|
||||
}
|
||||
|
||||
type funcHeader struct {
|
||||
Context unsafe.Pointer
|
||||
Code unsafe.Pointer
|
||||
}
|
||||
|
||||
// This is the same thing as an interface{}.
|
||||
type interfaceHeader struct {
|
||||
typecode Type
|
||||
value unsafe.Pointer
|
||||
}
|
||||
|
||||
type SliceHeader struct {
|
||||
Data uintptr
|
||||
Len uintptr
|
||||
Cap uintptr
|
||||
}
|
||||
|
||||
type StringHeader struct {
|
||||
Data uintptr
|
||||
Len uintptr
|
||||
}
|
||||
|
||||
type ValueError struct {
|
||||
Method string
|
||||
}
|
||||
|
||||
func (e *ValueError) Error() string {
|
||||
return "reflect: call of reflect.Value." + e.Method + " on invalid type"
|
||||
}
|
||||
|
||||
//go:linkname memcpy runtime.memcpy
|
||||
func memcpy(dst, src unsafe.Pointer, size uintptr)
|
||||
|
||||
@@ -5,7 +5,9 @@ const GOARCH = "amd64"
|
||||
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||
const TargetBits = 64
|
||||
|
||||
// Align on word boundary.
|
||||
// Align a pointer.
|
||||
// Note that some amd64 instructions (like movaps) expect 16-byte aligned
|
||||
// memory, thus the result must be 16-byte aligned.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 7) &^ 7
|
||||
return (ptr + 15) &^ 15
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ package runtime
|
||||
// Interfaces are represented as a pair of {typecode, value}, where value can be
|
||||
// anything (including non-pointers).
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type _interface struct {
|
||||
typecode uintptr
|
||||
value *uint8
|
||||
value unsafe.Pointer
|
||||
}
|
||||
|
||||
// Return true iff both interfaces are equal.
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
package runtime
|
||||
|
||||
// This file redirects math stubs to their fallback implementation.
|
||||
// TODO: use optimized versions if possible.
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
//go:linkname math_Asin math.Asin
|
||||
func math_Asin(x float64) float64 { return math_asin(x) }
|
||||
|
||||
//go:linkname math_asin math.asin
|
||||
func math_asin(x float64) float64
|
||||
|
||||
//go:linkname math_Asinh math.Asinh
|
||||
func math_Asinh(x float64) float64 { return math_asinh(x) }
|
||||
|
||||
//go:linkname math_asinh math.asinh
|
||||
func math_asinh(x float64) float64
|
||||
|
||||
//go:linkname math_Acos math.Acos
|
||||
func math_Acos(x float64) float64 { return math_acos(x) }
|
||||
|
||||
//go:linkname math_acos math.acos
|
||||
func math_acos(x float64) float64
|
||||
|
||||
//go:linkname math_Acosh math.Acosh
|
||||
func math_Acosh(x float64) float64 { return math_acosh(x) }
|
||||
|
||||
//go:linkname math_acosh math.acosh
|
||||
func math_acosh(x float64) float64
|
||||
|
||||
//go:linkname math_Atan math.Atan
|
||||
func math_Atan(x float64) float64 { return math_atan(x) }
|
||||
|
||||
//go:linkname math_atan math.atan
|
||||
func math_atan(x float64) float64
|
||||
|
||||
//go:linkname math_Atanh math.Atanh
|
||||
func math_Atanh(x float64) float64 { return math_atanh(x) }
|
||||
|
||||
//go:linkname math_atanh math.atanh
|
||||
func math_atanh(x float64) float64
|
||||
|
||||
//go:linkname math_Atan2 math.Atan2
|
||||
func math_Atan2(y, x float64) float64 { return math_atan2(y, x) }
|
||||
|
||||
//go:linkname math_atan2 math.atan2
|
||||
func math_atan2(y, x float64) float64
|
||||
|
||||
//go:linkname math_Cbrt math.Cbrt
|
||||
func math_Cbrt(x float64) float64 { return math_cbrt(x) }
|
||||
|
||||
//go:linkname math_cbrt math.cbrt
|
||||
func math_cbrt(x float64) float64
|
||||
|
||||
//go:linkname math_Ceil math.Ceil
|
||||
func math_Ceil(x float64) float64 { return math_ceil(x) }
|
||||
|
||||
//go:linkname math_ceil math.ceil
|
||||
func math_ceil(x float64) float64
|
||||
|
||||
//go:linkname math_Cos math.Cos
|
||||
func math_Cos(x float64) float64 { return math_cos(x) }
|
||||
|
||||
//go:linkname math_cos math.cos
|
||||
func math_cos(x float64) float64
|
||||
|
||||
//go:linkname math_Cosh math.Cosh
|
||||
func math_Cosh(x float64) float64 { return math_cosh(x) }
|
||||
|
||||
//go:linkname math_cosh math.cosh
|
||||
func math_cosh(x float64) float64
|
||||
|
||||
//go:linkname math_Erf math.Erf
|
||||
func math_Erf(x float64) float64 { return math_erf(x) }
|
||||
|
||||
//go:linkname math_erf math.erf
|
||||
func math_erf(x float64) float64
|
||||
|
||||
//go:linkname math_Erfc math.Erfc
|
||||
func math_Erfc(x float64) float64 { return math_erfc(x) }
|
||||
|
||||
//go:linkname math_erfc math.erfc
|
||||
func math_erfc(x float64) float64
|
||||
|
||||
//go:linkname math_Exp math.Exp
|
||||
func math_Exp(x float64) float64 { return math_exp(x) }
|
||||
|
||||
//go:linkname math_exp math.exp
|
||||
func math_exp(x float64) float64
|
||||
|
||||
//go:linkname math_Expm1 math.Expm1
|
||||
func math_Expm1(x float64) float64 { return math_expm1(x) }
|
||||
|
||||
//go:linkname math_expm1 math.expm1
|
||||
func math_expm1(x float64) float64
|
||||
|
||||
//go:linkname math_Exp2 math.Exp2
|
||||
func math_Exp2(x float64) float64 { return math_exp2(x) }
|
||||
|
||||
//go:linkname math_exp2 math.exp2
|
||||
func math_exp2(x float64) float64
|
||||
|
||||
//go:linkname math_Floor math.Floor
|
||||
func math_Floor(x float64) float64 { return math_floor(x) }
|
||||
|
||||
//go:linkname math_floor math.floor
|
||||
func math_floor(x float64) float64
|
||||
|
||||
//go:linkname math_Frexp math.Frexp
|
||||
func math_Frexp(x float64) (float64, int) { return math_frexp(x) }
|
||||
|
||||
//go:linkname math_frexp math.frexp
|
||||
func math_frexp(x float64) (float64, int)
|
||||
|
||||
//go:linkname math_Hypot math.Hypot
|
||||
func math_Hypot(p, q float64) float64 { return math_hypot(p, q) }
|
||||
|
||||
//go:linkname math_hypot math.hypot
|
||||
func math_hypot(p, q float64) float64
|
||||
|
||||
//go:linkname math_Ldexp math.Ldexp
|
||||
func math_Ldexp(frac float64, exp int) float64 { return math_ldexp(frac, exp) }
|
||||
|
||||
//go:linkname math_ldexp math.ldexp
|
||||
func math_ldexp(frac float64, exp int) float64
|
||||
|
||||
//go:linkname math_Log math.Log
|
||||
func math_Log(x float64) float64 { return math_log(x) }
|
||||
|
||||
//go:linkname math_log math.log
|
||||
func math_log(x float64) float64
|
||||
|
||||
//go:linkname math_Log1p math.Log1p
|
||||
func math_Log1p(x float64) float64 { return math_log1p(x) }
|
||||
|
||||
//go:linkname math_log1p math.log1p
|
||||
func math_log1p(x float64) float64
|
||||
|
||||
//go:linkname math_Log10 math.Log10
|
||||
func math_Log10(x float64) float64 { return math_log10(x) }
|
||||
|
||||
//go:linkname math_log10 math.log10
|
||||
func math_log10(x float64) float64
|
||||
|
||||
//go:linkname math_Log2 math.Log2
|
||||
func math_Log2(x float64) float64 { return math_log2(x) }
|
||||
|
||||
//go:linkname math_log2 math.log2
|
||||
func math_log2(x float64) float64
|
||||
|
||||
//go:linkname math_Max math.Max
|
||||
func math_Max(x, y float64) float64 { return math_max(x, y) }
|
||||
|
||||
//go:linkname math_max math.max
|
||||
func math_max(x, y float64) float64
|
||||
|
||||
//go:linkname math_Min math.Min
|
||||
func math_Min(x, y float64) float64 { return math_min(x, y) }
|
||||
|
||||
//go:linkname math_min math.min
|
||||
func math_min(x, y float64) float64
|
||||
|
||||
//go:linkname math_Mod math.Mod
|
||||
func math_Mod(x, y float64) float64 { return math_mod(x, y) }
|
||||
|
||||
//go:linkname math_mod math.mod
|
||||
func math_mod(x, y float64) float64
|
||||
|
||||
//go:linkname math_Modf math.Modf
|
||||
func math_Modf(x float64) (float64, float64) { return math_modf(x) }
|
||||
|
||||
//go:linkname math_modf math.modf
|
||||
func math_modf(x float64) (float64, float64)
|
||||
|
||||
//go:linkname math_Pow math.Pow
|
||||
func math_Pow(x, y float64) float64 { return math_pow(x, y) }
|
||||
|
||||
//go:linkname math_pow math.pow
|
||||
func math_pow(x, y float64) float64
|
||||
|
||||
//go:linkname math_Remainder math.Remainder
|
||||
func math_Remainder(x, y float64) float64 { return math_remainder(x, y) }
|
||||
|
||||
//go:linkname math_remainder math.remainder
|
||||
func math_remainder(x, y float64) float64
|
||||
|
||||
//go:linkname math_Sin math.Sin
|
||||
func math_Sin(x float64) float64 { return math_sin(x) }
|
||||
|
||||
//go:linkname math_sin math.sin
|
||||
func math_sin(x float64) float64
|
||||
|
||||
//go:linkname math_Sinh math.Sinh
|
||||
func math_Sinh(x float64) float64 { return math_sinh(x) }
|
||||
|
||||
//go:linkname math_sinh math.sinh
|
||||
func math_sinh(x float64) float64
|
||||
|
||||
//go:linkname math_Sqrt math.Sqrt
|
||||
func math_Sqrt(x float64) float64 { return math_sqrt(x) }
|
||||
|
||||
//go:linkname math_sqrt math.sqrt
|
||||
func math_sqrt(x float64) float64
|
||||
|
||||
//go:linkname math_Tan math.Tan
|
||||
func math_Tan(x float64) float64 { return math_tan(x) }
|
||||
|
||||
//go:linkname math_tan math.tan
|
||||
func math_tan(x float64) float64
|
||||
|
||||
//go:linkname math_Tanh math.Tanh
|
||||
func math_Tanh(x float64) float64 { return math_tanh(x) }
|
||||
|
||||
//go:linkname math_tanh math.tanh
|
||||
func math_tanh(x float64) float64
|
||||
|
||||
//go:linkname math_Trunc math.Trunc
|
||||
func math_Trunc(x float64) float64 { return math_trunc(x) }
|
||||
|
||||
//go:linkname math_trunc math.trunc
|
||||
func math_trunc(x float64) float64
|
||||
@@ -0,0 +1,5 @@
|
||||
// +build darwin
|
||||
|
||||
package runtime
|
||||
|
||||
const GOOS = "darwin"
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd21g18a
|
||||
// +build sam,atsamd21
|
||||
|
||||
package runtime
|
||||
|
||||
@@ -24,8 +24,9 @@ func init() {
|
||||
initRTC()
|
||||
initUARTClock()
|
||||
initI2CClock()
|
||||
initUSBClock()
|
||||
|
||||
// connect to UART
|
||||
// connect to USB CDC interface
|
||||
machine.UART0.Configure(machine.UARTConfig{})
|
||||
}
|
||||
|
||||
@@ -205,11 +206,7 @@ func initRTC() {
|
||||
|
||||
// set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1
|
||||
sam.RTC_MODE0.CTRL = sam.RegValue16((sam.RTC_MODE0_CTRL_MODE_COUNT32 << sam.RTC_MODE0_CTRL_MODE_Pos) |
|
||||
(sam.RTC_MODE0_CTRL_PRESCALER_DIV1 << sam.RTC_MODE0_CTRL_PRESCALER_Pos) |
|
||||
sam.RTC_MODE0_CTRL_MATCHCLR)
|
||||
waitForSync()
|
||||
|
||||
sam.RTC_MODE0.COMP0 = 0xffffffff
|
||||
(sam.RTC_MODE0_CTRL_PRESCALER_DIV1 << sam.RTC_MODE0_CTRL_PRESCALER_Pos))
|
||||
waitForSync()
|
||||
|
||||
// re-enable RTC
|
||||
@@ -256,8 +253,8 @@ func ticks() timeUnit {
|
||||
sam.RTC_MODE0.READREQ = sam.RTC_MODE0_READREQ_RREQ
|
||||
waitForSync()
|
||||
|
||||
rtcCounter := uint64(sam.RTC_MODE0.COUNT) * 30 // each counter tick == 30.5us
|
||||
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
||||
rtcCounter := (uint64(sam.RTC_MODE0.COUNT) * 305) / 10 // each counter tick == 30.5us
|
||||
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
||||
timerLastCounter = rtcCounter
|
||||
timestamp += timeUnit(offset) // TODO: not precise
|
||||
return timestamp
|
||||
@@ -277,7 +274,7 @@ func timerSleep(ticks uint32) {
|
||||
|
||||
// set compare value
|
||||
cnt := sam.RTC_MODE0.COUNT
|
||||
sam.RTC_MODE0.COMP0 = sam.RegValue(uint32(cnt) + (ticks / 30)) // each counter tick == 30.5us
|
||||
sam.RTC_MODE0.COMP0 = sam.RegValue(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
|
||||
waitForSync()
|
||||
|
||||
// enable IRQ for CMP0 compare
|
||||
@@ -335,3 +332,14 @@ func initI2CClock() {
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
waitForSync()
|
||||
}
|
||||
|
||||
func initUSBClock() {
|
||||
// Turn on clock for USB
|
||||
sam.PM.APBBMASK |= sam.PM_APBBMASK_USB_
|
||||
|
||||
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference)
|
||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_USB << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
waitForSync()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
// +build darwin linux
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package runtime
|
||||
|
||||
// This file implements syscall.Syscall and the like.
|
||||
|
||||
//go:linkname syscall_Syscall syscall.Syscall
|
||||
func syscall_Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err uintptr) {
|
||||
panic("syscall")
|
||||
}
|
||||
|
||||
//go:linkname syscall_Syscall6 syscall.Syscall6
|
||||
func syscall_Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err uintptr) {
|
||||
panic("syscall6")
|
||||
}
|
||||
@@ -189,6 +189,9 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
||||
return nil, errors.New("expected a full LLVM target or a custom target in -target flag")
|
||||
}
|
||||
goos := tripleSplit[2]
|
||||
if strings.HasPrefix(goos, "darwin") {
|
||||
goos = "darwin"
|
||||
}
|
||||
goarch := map[string]string{ // map from LLVM arch to Go arch
|
||||
"i386": "386",
|
||||
"x86_64": "amd64",
|
||||
@@ -211,22 +214,28 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: commands["clang"],
|
||||
Linker: "cc",
|
||||
LDFlags: []string{"-no-pie"}, // WARNING: clang < 5.0 requires -nopie
|
||||
Objcopy: "objcopy",
|
||||
GDB: "gdb",
|
||||
GDBCmds: []string{"run"},
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
if goarch == "arm" {
|
||||
if goarch == "arm" && goos == "linux" {
|
||||
spec.Linker = "arm-linux-gnueabi-gcc"
|
||||
spec.Objcopy = "arm-linux-gnueabi-objcopy"
|
||||
spec.GDB = "arm-linux-gnueabi-gdb"
|
||||
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabi"}
|
||||
}
|
||||
if goarch == "arm64" {
|
||||
if goarch == "arm64" && goos == "linux" {
|
||||
spec.Linker = "aarch64-linux-gnu-gcc"
|
||||
spec.Objcopy = "aarch64-linux-gnu-objcopy"
|
||||
spec.GDB = "aarch64-linux-gnu-gdb"
|
||||
spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"}
|
||||
}
|
||||
if goarch == "386" {
|
||||
spec.CFlags = []string{"-m32"}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv6m-none-eabi",
|
||||
"build-tags": ["atsamd21g18", "sam"],
|
||||
"build-tags": ["atsamd21g18", "atsamd21", "sam"],
|
||||
"cflags": [
|
||||
"--target=armv6m-none-eabi",
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"ldflags": [
|
||||
"-T", "targets/atsamd21g18.ld"
|
||||
"-T", "targets/atsamd21.ld"
|
||||
],
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd21g18a.s"
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["sam", "atsamd21g18a", "circuitplay_express"],
|
||||
"flash": "uf2conv.py {bin}"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18"],
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["sam", "atsamd21g18a", "itsybitsy_m0"],
|
||||
"flash": "bossac -d -i -e -w -v -R --offset=0x2000 {hex}"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"build-tags": ["nrf52", "nrf"],
|
||||
"cflags": [
|
||||
"--target=armv7em-none-eabi",
|
||||
"-mfloat-abi=soft",
|
||||
"-Qunused-arguments",
|
||||
"-DNRF52832_XXAA",
|
||||
"-Ilib/CMSIS/CMSIS/Include"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"build-tags": ["nrf52840", "nrf"],
|
||||
"cflags": [
|
||||
"--target=armv7em-none-eabi",
|
||||
"-mfloat-abi=soft",
|
||||
"-Qunused-arguments",
|
||||
"-DNRF52840_XXAA",
|
||||
"-Ilib/CMSIS/CMSIS/Include"
|
||||
|
||||
Vendored
+17
@@ -47,6 +47,20 @@ func main() {
|
||||
println(s2 == Struct2{"foo", 0.0, 7})
|
||||
println(s2 == Struct2{"foo", 1.0, 5})
|
||||
println(s2 == Struct2{"foo", 1.0, 7})
|
||||
|
||||
println("complex numbers")
|
||||
println(c64 == 3+2i)
|
||||
println(c64 == 4+2i)
|
||||
println(c64 == 3+3i)
|
||||
println(c64 != 3+2i)
|
||||
println(c64 != 4+2i)
|
||||
println(c64 != 3+3i)
|
||||
println(c128 == 3+2i)
|
||||
println(c128 == 4+2i)
|
||||
println(c128 == 3+3i)
|
||||
println(c128 != 3+2i)
|
||||
println(c128 != 4+2i)
|
||||
println(c128 != 3+3i)
|
||||
}
|
||||
|
||||
var x = true
|
||||
@@ -58,6 +72,9 @@ var s2 = Struct2{"foo", 0.0, 5}
|
||||
|
||||
var a1 = [2]int{1, 2}
|
||||
|
||||
var c64 = 3 + 2i
|
||||
var c128 = 4 + 3i
|
||||
|
||||
type Int int
|
||||
|
||||
type Struct1 struct {
|
||||
|
||||
Vendored
+13
@@ -41,3 +41,16 @@ true
|
||||
false
|
||||
true
|
||||
false
|
||||
complex numbers
|
||||
true
|
||||
false
|
||||
false
|
||||
false
|
||||
true
|
||||
true
|
||||
false
|
||||
false
|
||||
false
|
||||
true
|
||||
true
|
||||
true
|
||||
|
||||
Vendored
+17
@@ -1,5 +1,14 @@
|
||||
#include "main.h"
|
||||
|
||||
int global = 3;
|
||||
_Bool globalBool = 1;
|
||||
_Bool globalBool2 = 10; // test narrowing
|
||||
float globalFloat = 3.1;
|
||||
double globalDouble = 3.2;
|
||||
_Complex float globalComplexFloat = 4.1+3.3i;
|
||||
_Complex double globalComplexDouble = 4.2+3.4i;
|
||||
_Complex double globalComplexLongDouble = 4.3+3.5i;
|
||||
|
||||
int fortytwo() {
|
||||
return 42;
|
||||
}
|
||||
@@ -7,3 +16,11 @@ int fortytwo() {
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int doCallback(int a, int b, binop_t callback) {
|
||||
return callback(a, b);
|
||||
}
|
||||
|
||||
void store(int value, int *ptr) {
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
Vendored
+25
@@ -3,6 +3,7 @@ package main
|
||||
/*
|
||||
int fortytwo(void);
|
||||
#include "main.h"
|
||||
int mul(int, int);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -16,4 +17,28 @@ func main() {
|
||||
println("myint size:", int(unsafe.Sizeof(x)))
|
||||
var y C.longlong = -(1 << 40)
|
||||
println("longlong:", y)
|
||||
println("global:", C.global)
|
||||
var ptr C.intPointer
|
||||
var n C.int = 15
|
||||
ptr = C.intPointer(&n)
|
||||
println("15:", *ptr)
|
||||
C.store(25, &n)
|
||||
println("25:", *ptr)
|
||||
cb := C.binop_t(C.add)
|
||||
println("callback 1:", C.doCallback(20, 30, cb))
|
||||
cb = C.binop_t(C.mul)
|
||||
println("callback 2:", C.doCallback(20, 30, cb))
|
||||
|
||||
// more globals
|
||||
println("bool:", C.globalBool, C.globalBool2 == true)
|
||||
println("float:", C.globalFloat)
|
||||
println("double:", C.globalDouble)
|
||||
println("complex float:", C.globalComplexFloat)
|
||||
println("complex double:", C.globalComplexDouble)
|
||||
println("complex long double:", C.globalComplexLongDouble)
|
||||
}
|
||||
|
||||
//export mul
|
||||
func mul(a, b C.int) C.int {
|
||||
return a * b
|
||||
}
|
||||
|
||||
Vendored
+18
@@ -1,2 +1,20 @@
|
||||
typedef short myint;
|
||||
int add(int a, int b);
|
||||
typedef int (*binop_t) (int, int);
|
||||
int doCallback(int a, int b, binop_t cb);
|
||||
typedef int * intPointer;
|
||||
void store(int value, int *ptr);
|
||||
|
||||
// test globals
|
||||
extern int global;
|
||||
extern _Bool globalBool;
|
||||
extern _Bool globalBool2;
|
||||
extern float globalFloat;
|
||||
extern double globalDouble;
|
||||
extern _Complex float globalComplexFloat;
|
||||
extern _Complex double globalComplexDouble;
|
||||
extern _Complex double globalComplexLongDouble;
|
||||
|
||||
// test duplicate definitions
|
||||
int add(int a, int b);
|
||||
extern int global;
|
||||
|
||||
Vendored
+11
@@ -3,3 +3,14 @@ add: 8
|
||||
myint: 3 5
|
||||
myint size: 2
|
||||
longlong: -1099511627776
|
||||
global: 3
|
||||
15: 15
|
||||
25: 25
|
||||
callback 1: 50
|
||||
callback 2: 600
|
||||
bool: true true
|
||||
float: +3.100000e+000
|
||||
double: +3.200000e+000
|
||||
complex float: (+4.100000e+000+3.300000e+000i)
|
||||
complex double: (+4.200000e+000+3.400000e+000i)
|
||||
complex long double: (+4.300000e+000+3.500000e+000i)
|
||||
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import "math"
|
||||
|
||||
func main() {
|
||||
for _, n := range []float64{0.3, 1.5, 2.6, -1.1, -3.1, -3.8} {
|
||||
println("n:", n)
|
||||
println(" asin: ", math.Asin(n))
|
||||
println(" asinh: ", math.Asinh(n))
|
||||
println(" acos: ", math.Acos(n))
|
||||
println(" acosh: ", math.Acosh(n))
|
||||
println(" atan: ", math.Atan(n))
|
||||
println(" atanh: ", math.Atanh(n))
|
||||
println(" atan2: ", math.Atan2(n, 0.2))
|
||||
println(" cbrt: ", math.Cbrt(n))
|
||||
println(" ceil: ", math.Ceil(n))
|
||||
println(" cos: ", math.Cos(n))
|
||||
println(" cosh: ", math.Cosh(n))
|
||||
println(" erf: ", math.Erf(n))
|
||||
println(" erfc: ", math.Erfc(n))
|
||||
println(" exp: ", math.Exp(n))
|
||||
println(" expm1: ", math.Expm1(n))
|
||||
println(" exp2: ", math.Exp2(n))
|
||||
println(" floor: ", math.Floor(n))
|
||||
f, e := math.Frexp(n)
|
||||
println(" frexp: ", f, e)
|
||||
println(" hypot: ", math.Hypot(n, n*2))
|
||||
println(" ldexp: ", math.Ldexp(n, 2))
|
||||
println(" log: ", math.Log(n))
|
||||
println(" log1p: ", math.Log1p(n))
|
||||
println(" log10: ", math.Log10(n))
|
||||
println(" log2: ", math.Log2(n))
|
||||
println(" max: ", math.Max(n, n+1))
|
||||
println(" min: ", math.Min(n, n+1))
|
||||
println(" mod: ", math.Mod(n, n+1))
|
||||
i, f := math.Modf(n)
|
||||
println(" modf: ", i, f)
|
||||
println(" pow: ", math.Pow(n, n))
|
||||
println(" remainder:", math.Remainder(n, n+0.2))
|
||||
println(" sin: ", math.Sin(n))
|
||||
println(" sinh: ", math.Sinh(n))
|
||||
println(" tan: ", math.Tan(n))
|
||||
println(" tanh: ", math.Tanh(n))
|
||||
println(" trunc: ", math.Trunc(n))
|
||||
}
|
||||
}
|
||||
Vendored
+216
@@ -0,0 +1,216 @@
|
||||
n: +3.000000e-001
|
||||
asin: +3.046927e-001
|
||||
asinh: +2.956730e-001
|
||||
acos: +1.266104e+000
|
||||
acosh: NaN
|
||||
atan: +2.914568e-001
|
||||
atanh: +3.095196e-001
|
||||
atan2: +9.827937e-001
|
||||
cbrt: +6.694330e-001
|
||||
ceil: +1.000000e+000
|
||||
cos: +9.553365e-001
|
||||
cosh: +1.045339e+000
|
||||
erf: +3.286268e-001
|
||||
erfc: +6.713732e-001
|
||||
exp: +1.349859e+000
|
||||
expm1: +3.498588e-001
|
||||
exp2: +1.231144e+000
|
||||
floor: +0.000000e+000
|
||||
frexp: +6.000000e-001 -1
|
||||
hypot: +6.708204e-001
|
||||
ldexp: +1.200000e+000
|
||||
log: -1.203973e+000
|
||||
log1p: +2.623643e-001
|
||||
log10: -5.228787e-001
|
||||
log2: -1.736966e+000
|
||||
max: +1.300000e+000
|
||||
min: +3.000000e-001
|
||||
mod: +3.000000e-001
|
||||
modf: +0.000000e+000 +3.000000e-001
|
||||
pow: +6.968453e-001
|
||||
remainder: -2.000000e-001
|
||||
sin: +2.955202e-001
|
||||
sinh: +3.045203e-001
|
||||
tan: +3.093362e-001
|
||||
tanh: +2.913126e-001
|
||||
trunc: +0.000000e+000
|
||||
n: +1.500000e+000
|
||||
asin: NaN
|
||||
asinh: +1.194763e+000
|
||||
acos: NaN
|
||||
acosh: +9.624237e-001
|
||||
atan: +9.827937e-001
|
||||
atanh: NaN
|
||||
atan2: +1.438245e+000
|
||||
cbrt: +1.144714e+000
|
||||
ceil: +2.000000e+000
|
||||
cos: +7.073720e-002
|
||||
cosh: +2.352410e+000
|
||||
erf: +9.661051e-001
|
||||
erfc: +3.389485e-002
|
||||
exp: +4.481689e+000
|
||||
expm1: +3.481689e+000
|
||||
exp2: +2.828427e+000
|
||||
floor: +1.000000e+000
|
||||
frexp: +7.500000e-001 1
|
||||
hypot: +3.354102e+000
|
||||
ldexp: +6.000000e+000
|
||||
log: +4.054651e-001
|
||||
log1p: +9.162907e-001
|
||||
log10: +1.760913e-001
|
||||
log2: +5.849625e-001
|
||||
max: +2.500000e+000
|
||||
min: +1.500000e+000
|
||||
mod: +1.500000e+000
|
||||
modf: +1.000000e+000 +5.000000e-001
|
||||
pow: +1.837117e+000
|
||||
remainder: -2.000000e-001
|
||||
sin: +9.974950e-001
|
||||
sinh: +2.129279e+000
|
||||
tan: +1.410142e+001
|
||||
tanh: +9.051483e-001
|
||||
trunc: +1.000000e+000
|
||||
n: +2.600000e+000
|
||||
asin: NaN
|
||||
asinh: +1.683743e+000
|
||||
acos: NaN
|
||||
acosh: +1.609438e+000
|
||||
atan: +1.203622e+000
|
||||
atanh: NaN
|
||||
atan2: +1.494024e+000
|
||||
cbrt: +1.375069e+000
|
||||
ceil: +3.000000e+000
|
||||
cos: -8.568888e-001
|
||||
cosh: +6.769006e+000
|
||||
erf: +9.997640e-001
|
||||
erfc: +2.360344e-004
|
||||
exp: +1.346374e+001
|
||||
expm1: +1.246374e+001
|
||||
exp2: +6.062866e+000
|
||||
floor: +2.000000e+000
|
||||
frexp: +6.500000e-001 2
|
||||
hypot: +5.813777e+000
|
||||
ldexp: +1.040000e+001
|
||||
log: +9.555114e-001
|
||||
log1p: +1.280934e+000
|
||||
log10: +4.149733e-001
|
||||
log2: +1.378512e+000
|
||||
max: +3.600000e+000
|
||||
min: +2.600000e+000
|
||||
mod: +2.600000e+000
|
||||
modf: +2.000000e+000 +6.000000e-001
|
||||
pow: +1.199308e+001
|
||||
remainder: -2.000000e-001
|
||||
sin: +5.155014e-001
|
||||
sinh: +6.694732e+000
|
||||
tan: -6.015966e-001
|
||||
tanh: +9.890274e-001
|
||||
trunc: +2.000000e+000
|
||||
n: -1.100000e+000
|
||||
asin: NaN
|
||||
asinh: -9.503469e-001
|
||||
acos: NaN
|
||||
acosh: NaN
|
||||
atan: -8.329813e-001
|
||||
atanh: NaN
|
||||
atan2: -1.390943e+000
|
||||
cbrt: -1.032280e+000
|
||||
ceil: -1.000000e+000
|
||||
cos: +4.535961e-001
|
||||
cosh: +1.668519e+000
|
||||
erf: -8.802051e-001
|
||||
erfc: +1.880205e+000
|
||||
exp: +3.328711e-001
|
||||
expm1: -6.671289e-001
|
||||
exp2: +4.665165e-001
|
||||
floor: -2.000000e+000
|
||||
frexp: -5.500000e-001 1
|
||||
hypot: +2.459675e+000
|
||||
ldexp: -4.400000e+000
|
||||
log: NaN
|
||||
log1p: NaN
|
||||
log10: NaN
|
||||
log2: NaN
|
||||
max: -1.000000e-001
|
||||
min: -1.100000e+000
|
||||
mod: -1.000000e-001
|
||||
modf: -1.000000e+000 -1.000000e-001
|
||||
pow: NaN
|
||||
remainder: -2.000000e-001
|
||||
sin: -8.912074e-001
|
||||
sinh: -1.335647e+000
|
||||
tan: -1.964760e+000
|
||||
tanh: -8.004990e-001
|
||||
trunc: -1.000000e+000
|
||||
n: -3.100000e+000
|
||||
asin: NaN
|
||||
asinh: -1.849604e+000
|
||||
acos: NaN
|
||||
acosh: NaN
|
||||
atan: -1.258754e+000
|
||||
atanh: NaN
|
||||
atan2: -1.506369e+000
|
||||
cbrt: -1.458100e+000
|
||||
ceil: -3.000000e+000
|
||||
cos: -9.991352e-001
|
||||
cosh: +1.112150e+001
|
||||
erf: -9.999884e-001
|
||||
erfc: +1.999988e+000
|
||||
exp: +4.504920e-002
|
||||
expm1: -9.549508e-001
|
||||
exp2: +1.166291e-001
|
||||
floor: -4.000000e+000
|
||||
frexp: -7.750000e-001 2
|
||||
hypot: +6.931811e+000
|
||||
ldexp: -1.240000e+001
|
||||
log: NaN
|
||||
log1p: NaN
|
||||
log10: NaN
|
||||
log2: NaN
|
||||
max: -2.100000e+000
|
||||
min: -3.100000e+000
|
||||
mod: -1.000000e+000
|
||||
modf: -3.000000e+000 -1.000000e-001
|
||||
pow: NaN
|
||||
remainder: -2.000000e-001
|
||||
sin: -4.158066e-002
|
||||
sinh: -1.107645e+001
|
||||
tan: +4.161665e-002
|
||||
tanh: -9.959494e-001
|
||||
trunc: -3.000000e+000
|
||||
n: -3.800000e+000
|
||||
asin: NaN
|
||||
asinh: -2.045028e+000
|
||||
acos: NaN
|
||||
acosh: NaN
|
||||
atan: -1.313473e+000
|
||||
atanh: NaN
|
||||
atan2: -1.518213e+000
|
||||
cbrt: -1.560491e+000
|
||||
ceil: -3.000000e+000
|
||||
cos: -7.909677e-001
|
||||
cosh: +2.236178e+001
|
||||
erf: -9.999999e-001
|
||||
erfc: +2.000000e+000
|
||||
exp: +2.237077e-002
|
||||
expm1: -9.776292e-001
|
||||
exp2: +7.179365e-002
|
||||
floor: -4.000000e+000
|
||||
frexp: -9.500000e-001 2
|
||||
hypot: +8.497058e+000
|
||||
ldexp: -1.520000e+001
|
||||
log: NaN
|
||||
log1p: NaN
|
||||
log10: NaN
|
||||
log2: NaN
|
||||
max: -2.800000e+000
|
||||
min: -3.800000e+000
|
||||
mod: -1.000000e+000
|
||||
modf: -3.000000e+000 -8.000000e-001
|
||||
pow: NaN
|
||||
remainder: -2.000000e-001
|
||||
sin: +6.118579e-001
|
||||
sinh: -2.233941e+001
|
||||
tan: -7.735561e-001
|
||||
tanh: -9.989996e-001
|
||||
trunc: -3.000000e+000
|
||||
Vendored
+292
@@ -0,0 +1,292 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type (
|
||||
myint int
|
||||
myslice []byte
|
||||
myslice2 []myint
|
||||
)
|
||||
|
||||
func main() {
|
||||
println("matching types")
|
||||
println(reflect.TypeOf(int(3)) == reflect.TypeOf(int(5)))
|
||||
println(reflect.TypeOf(int(3)) == reflect.TypeOf(uint(5)))
|
||||
println(reflect.TypeOf(myint(3)) == reflect.TypeOf(int(5)))
|
||||
println(reflect.TypeOf(myslice{}) == reflect.TypeOf([]byte{}))
|
||||
println(reflect.TypeOf(myslice2{}) == reflect.TypeOf([]myint{}))
|
||||
println(reflect.TypeOf(myslice2{}) == reflect.TypeOf([]int{}))
|
||||
|
||||
println("\nvalues of interfaces")
|
||||
var zeroSlice []byte
|
||||
var zeroFunc func()
|
||||
var zeroMap map[string]int
|
||||
var zeroChan chan int
|
||||
n := 42
|
||||
for _, v := range []interface{}{
|
||||
// basic types
|
||||
true,
|
||||
false,
|
||||
int(2000),
|
||||
int(-2000),
|
||||
uint(2000),
|
||||
int8(-3),
|
||||
int8(3),
|
||||
uint8(200),
|
||||
int16(-300),
|
||||
int16(300),
|
||||
uint16(50000),
|
||||
int32(7 << 20),
|
||||
int32(-7 << 20),
|
||||
uint32(7 << 20),
|
||||
int64(9 << 40),
|
||||
int64(-9 << 40),
|
||||
uint64(9 << 40),
|
||||
uintptr(12345),
|
||||
float32(3.14),
|
||||
float64(3.14),
|
||||
complex64(1.2 + 0.3i),
|
||||
complex128(1.3 + 0.4i),
|
||||
myint(32),
|
||||
"foo",
|
||||
unsafe.Pointer(new(int)),
|
||||
// channels
|
||||
zeroChan,
|
||||
// pointers
|
||||
new(int),
|
||||
new(error),
|
||||
&n,
|
||||
// slices
|
||||
[]byte{1, 2, 3},
|
||||
make([]uint8, 2, 5),
|
||||
[]rune{3, 5},
|
||||
[]string{"xyz", "Z"},
|
||||
zeroSlice,
|
||||
[]byte{},
|
||||
[]float32{1, 1.32},
|
||||
[]float64{1, 1.64},
|
||||
[]complex64{1, 1.64 + 0.3i},
|
||||
[]complex128{1, 1.128 + 0.4i},
|
||||
// array
|
||||
[4]int{1, 2, 3, 4},
|
||||
// functions
|
||||
zeroFunc,
|
||||
emptyFunc,
|
||||
// maps
|
||||
zeroMap,
|
||||
map[string]int{},
|
||||
// structs
|
||||
struct{}{},
|
||||
struct{ error }{},
|
||||
} {
|
||||
showValue(reflect.ValueOf(v), "")
|
||||
}
|
||||
|
||||
// test sizes
|
||||
println("\nsizes:")
|
||||
println("int8", int(reflect.TypeOf(int8(0)).Size()))
|
||||
println("int16", int(reflect.TypeOf(int16(0)).Size()))
|
||||
println("int32", int(reflect.TypeOf(int32(0)).Size()))
|
||||
println("int64", int(reflect.TypeOf(int64(0)).Size()))
|
||||
println("uint8", int(reflect.TypeOf(uint8(0)).Size()))
|
||||
println("uint16", int(reflect.TypeOf(uint16(0)).Size()))
|
||||
println("uint32", int(reflect.TypeOf(uint32(0)).Size()))
|
||||
println("uint64", int(reflect.TypeOf(uint64(0)).Size()))
|
||||
println("float32", int(reflect.TypeOf(float32(0)).Size()))
|
||||
println("float64", int(reflect.TypeOf(float64(0)).Size()))
|
||||
println("complex64", int(reflect.TypeOf(complex64(0)).Size()))
|
||||
println("complex128", int(reflect.TypeOf(complex128(0)).Size()))
|
||||
assertSize(reflect.TypeOf(uintptr(0)).Size() == unsafe.Sizeof(uintptr(0)), "uintptr")
|
||||
assertSize(reflect.TypeOf("").Size() == unsafe.Sizeof(""), "string")
|
||||
assertSize(reflect.TypeOf(new(int)).Size() == unsafe.Sizeof(new(int)), "*int")
|
||||
|
||||
// SetBool
|
||||
rv := reflect.ValueOf(new(bool)).Elem()
|
||||
rv.SetBool(true)
|
||||
if rv.Bool() != true {
|
||||
panic("could not set bool with SetBool()")
|
||||
}
|
||||
|
||||
// SetInt
|
||||
for _, v := range []interface{}{
|
||||
new(int),
|
||||
new(int8),
|
||||
new(int16),
|
||||
new(int32),
|
||||
new(int64),
|
||||
} {
|
||||
rv := reflect.ValueOf(v).Elem()
|
||||
rv.SetInt(99)
|
||||
if rv.Int() != 99 {
|
||||
panic("could not set integer with SetInt()")
|
||||
}
|
||||
}
|
||||
|
||||
// SetUint
|
||||
for _, v := range []interface{}{
|
||||
new(uint),
|
||||
new(uint8),
|
||||
new(uint16),
|
||||
new(uint32),
|
||||
new(uint64),
|
||||
new(uintptr),
|
||||
} {
|
||||
rv := reflect.ValueOf(v).Elem()
|
||||
rv.SetUint(99)
|
||||
if rv.Uint() != 99 {
|
||||
panic("could not set integer with SetUint()")
|
||||
}
|
||||
}
|
||||
|
||||
// SetFloat
|
||||
for _, v := range []interface{}{
|
||||
new(float32),
|
||||
new(float64),
|
||||
} {
|
||||
rv := reflect.ValueOf(v).Elem()
|
||||
rv.SetFloat(2.25)
|
||||
if rv.Float() != 2.25 {
|
||||
panic("could not set float with SetFloat()")
|
||||
}
|
||||
}
|
||||
|
||||
// SetComplex
|
||||
for _, v := range []interface{}{
|
||||
new(complex64),
|
||||
new(complex128),
|
||||
} {
|
||||
rv := reflect.ValueOf(v).Elem()
|
||||
rv.SetComplex(3 + 2i)
|
||||
if rv.Complex() != 3+2i {
|
||||
panic("could not set complex with SetComplex()")
|
||||
}
|
||||
}
|
||||
|
||||
// SetString
|
||||
rv = reflect.ValueOf(new(string)).Elem()
|
||||
rv.SetString("foo")
|
||||
if rv.String() != "foo" {
|
||||
panic("could not set string with SetString()")
|
||||
}
|
||||
|
||||
// Set int
|
||||
rv = reflect.ValueOf(new(int)).Elem()
|
||||
rv.SetInt(33)
|
||||
rv.Set(reflect.ValueOf(22))
|
||||
if rv.Int() != 22 {
|
||||
panic("could not set int with Set()")
|
||||
}
|
||||
|
||||
// Set uint8
|
||||
rv = reflect.ValueOf(new(uint8)).Elem()
|
||||
rv.SetUint(33)
|
||||
rv.Set(reflect.ValueOf(uint8(22)))
|
||||
if rv.Uint() != 22 {
|
||||
panic("could not set uint8 with Set()")
|
||||
}
|
||||
|
||||
// Set string
|
||||
rv = reflect.ValueOf(new(string)).Elem()
|
||||
rv.SetString("foo")
|
||||
rv.Set(reflect.ValueOf("bar"))
|
||||
if rv.String() != "bar" {
|
||||
panic("could not set string with Set()")
|
||||
}
|
||||
|
||||
// Set complex128
|
||||
rv = reflect.ValueOf(new(complex128)).Elem()
|
||||
rv.SetComplex(3 + 2i)
|
||||
rv.Set(reflect.ValueOf(4 + 8i))
|
||||
if rv.Complex() != 4+8i {
|
||||
panic("could not set complex128 with Set()")
|
||||
}
|
||||
|
||||
// Set to slice
|
||||
rv = reflect.ValueOf([]int{3, 5})
|
||||
rv.Index(1).SetInt(7)
|
||||
if rv.Index(1).Int() != 7 {
|
||||
panic("could not set int in slice")
|
||||
}
|
||||
rv.Index(1).Set(reflect.ValueOf(8))
|
||||
if rv.Index(1).Int() != 8 {
|
||||
panic("could not set int in slice")
|
||||
}
|
||||
if rv.Len() != 2 || rv.Index(0).Int() != 3 {
|
||||
panic("slice was changed while setting part of it")
|
||||
}
|
||||
}
|
||||
|
||||
func emptyFunc() {
|
||||
}
|
||||
|
||||
func showValue(rv reflect.Value, indent string) {
|
||||
rt := rv.Type()
|
||||
if rt.Kind() != rv.Kind() {
|
||||
panic("type kind is different from value kind")
|
||||
}
|
||||
print(indent+"reflect type: ", rt.Kind().String())
|
||||
if rv.CanSet() {
|
||||
print(" settable=", rv.CanSet())
|
||||
}
|
||||
println()
|
||||
switch rt.Kind() {
|
||||
case reflect.Bool:
|
||||
println(indent+" bool:", rv.Bool())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
println(indent+" int:", rv.Int())
|
||||
case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
println(indent+" uint:", rv.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
println(indent+" float:", rv.Float())
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
println(indent+" complex:", rv.Complex())
|
||||
case reflect.String:
|
||||
println(indent+" string:", rv.String(), rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
showValue(rv.Index(i), indent+" ")
|
||||
}
|
||||
case reflect.UnsafePointer:
|
||||
println(indent+" pointer:", rv.Pointer() != 0)
|
||||
case reflect.Array:
|
||||
println(indent + " array")
|
||||
case reflect.Chan:
|
||||
println(indent+" chan:", rt.Elem().Kind().String())
|
||||
println(indent+" nil:", rv.IsNil())
|
||||
case reflect.Func:
|
||||
println(indent + " func")
|
||||
println(indent+" nil:", rv.IsNil())
|
||||
case reflect.Interface:
|
||||
println(indent + " interface")
|
||||
println(indent+" nil:", rv.IsNil())
|
||||
case reflect.Map:
|
||||
println(indent + " map")
|
||||
println(indent+" nil:", rv.IsNil())
|
||||
case reflect.Ptr:
|
||||
println(indent+" pointer:", rv.Pointer() != 0, rt.Elem().Kind().String())
|
||||
println(indent+" nil:", rv.IsNil())
|
||||
if !rv.IsNil() {
|
||||
showValue(rv.Elem(), indent+" ")
|
||||
}
|
||||
case reflect.Slice:
|
||||
println(indent+" slice:", rt.Elem().Kind().String(), rv.Len(), rv.Cap())
|
||||
println(indent+" pointer:", rv.Pointer() != 0)
|
||||
println(indent+" nil:", rv.IsNil())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
println(indent+" indexing:", i)
|
||||
showValue(rv.Index(i), indent+" ")
|
||||
}
|
||||
case reflect.Struct:
|
||||
println(indent + " struct")
|
||||
default:
|
||||
println(indent + " unknown type kind!")
|
||||
}
|
||||
}
|
||||
|
||||
func assertSize(ok bool, typ string) {
|
||||
if !ok {
|
||||
panic("size mismatch for type " + typ)
|
||||
}
|
||||
}
|
||||
Vendored
+215
@@ -0,0 +1,215 @@
|
||||
matching types
|
||||
true
|
||||
false
|
||||
false
|
||||
false
|
||||
false
|
||||
false
|
||||
|
||||
values of interfaces
|
||||
reflect type: bool
|
||||
bool: true
|
||||
reflect type: bool
|
||||
bool: false
|
||||
reflect type: int
|
||||
int: 2000
|
||||
reflect type: int
|
||||
int: -2000
|
||||
reflect type: uint
|
||||
uint: 2000
|
||||
reflect type: int8
|
||||
int: -3
|
||||
reflect type: int8
|
||||
int: 3
|
||||
reflect type: uint8
|
||||
uint: 200
|
||||
reflect type: int16
|
||||
int: -300
|
||||
reflect type: int16
|
||||
int: 300
|
||||
reflect type: uint16
|
||||
uint: 50000
|
||||
reflect type: int32
|
||||
int: 7340032
|
||||
reflect type: int32
|
||||
int: -7340032
|
||||
reflect type: uint32
|
||||
uint: 7340032
|
||||
reflect type: int64
|
||||
int: 9895604649984
|
||||
reflect type: int64
|
||||
int: -9895604649984
|
||||
reflect type: uint64
|
||||
uint: 9895604649984
|
||||
reflect type: uintptr
|
||||
uint: 12345
|
||||
reflect type: float32
|
||||
float: +3.140000e+000
|
||||
reflect type: float64
|
||||
float: +3.140000e+000
|
||||
reflect type: complex64
|
||||
complex: (+1.200000e+000+3.000000e-001i)
|
||||
reflect type: complex128
|
||||
complex: (+1.300000e+000+4.000000e-001i)
|
||||
reflect type: int
|
||||
int: 32
|
||||
reflect type: string
|
||||
string: foo 3
|
||||
reflect type: uint8
|
||||
uint: 102
|
||||
reflect type: uint8
|
||||
uint: 111
|
||||
reflect type: uint8
|
||||
uint: 111
|
||||
reflect type: unsafe.Pointer
|
||||
pointer: true
|
||||
reflect type: chan
|
||||
chan: int
|
||||
nil: true
|
||||
reflect type: ptr
|
||||
pointer: true int
|
||||
nil: false
|
||||
reflect type: int settable=true
|
||||
int: 0
|
||||
reflect type: ptr
|
||||
pointer: true interface
|
||||
nil: false
|
||||
reflect type: interface settable=true
|
||||
interface
|
||||
nil: true
|
||||
reflect type: ptr
|
||||
pointer: true int
|
||||
nil: false
|
||||
reflect type: int settable=true
|
||||
int: 42
|
||||
reflect type: slice
|
||||
slice: uint8 3 3
|
||||
pointer: true
|
||||
nil: false
|
||||
indexing: 0
|
||||
reflect type: uint8 settable=true
|
||||
uint: 1
|
||||
indexing: 1
|
||||
reflect type: uint8 settable=true
|
||||
uint: 2
|
||||
indexing: 2
|
||||
reflect type: uint8 settable=true
|
||||
uint: 3
|
||||
reflect type: slice
|
||||
slice: uint8 2 5
|
||||
pointer: true
|
||||
nil: false
|
||||
indexing: 0
|
||||
reflect type: uint8 settable=true
|
||||
uint: 0
|
||||
indexing: 1
|
||||
reflect type: uint8 settable=true
|
||||
uint: 0
|
||||
reflect type: slice
|
||||
slice: int32 2 2
|
||||
pointer: true
|
||||
nil: false
|
||||
indexing: 0
|
||||
reflect type: int32 settable=true
|
||||
int: 3
|
||||
indexing: 1
|
||||
reflect type: int32 settable=true
|
||||
int: 5
|
||||
reflect type: slice
|
||||
slice: string 2 2
|
||||
pointer: true
|
||||
nil: false
|
||||
indexing: 0
|
||||
reflect type: string settable=true
|
||||
string: xyz 3
|
||||
reflect type: uint8
|
||||
uint: 120
|
||||
reflect type: uint8
|
||||
uint: 121
|
||||
reflect type: uint8
|
||||
uint: 122
|
||||
indexing: 1
|
||||
reflect type: string settable=true
|
||||
string: Z 1
|
||||
reflect type: uint8
|
||||
uint: 90
|
||||
reflect type: slice
|
||||
slice: uint8 0 0
|
||||
pointer: false
|
||||
nil: true
|
||||
reflect type: slice
|
||||
slice: uint8 0 0
|
||||
pointer: true
|
||||
nil: false
|
||||
reflect type: slice
|
||||
slice: float32 2 2
|
||||
pointer: true
|
||||
nil: false
|
||||
indexing: 0
|
||||
reflect type: float32 settable=true
|
||||
float: +1.000000e+000
|
||||
indexing: 1
|
||||
reflect type: float32 settable=true
|
||||
float: +1.320000e+000
|
||||
reflect type: slice
|
||||
slice: float64 2 2
|
||||
pointer: true
|
||||
nil: false
|
||||
indexing: 0
|
||||
reflect type: float64 settable=true
|
||||
float: +1.000000e+000
|
||||
indexing: 1
|
||||
reflect type: float64 settable=true
|
||||
float: +1.640000e+000
|
||||
reflect type: slice
|
||||
slice: complex64 2 2
|
||||
pointer: true
|
||||
nil: false
|
||||
indexing: 0
|
||||
reflect type: complex64 settable=true
|
||||
complex: (+1.000000e+000+0.000000e+000i)
|
||||
indexing: 1
|
||||
reflect type: complex64 settable=true
|
||||
complex: (+1.640000e+000+3.000000e-001i)
|
||||
reflect type: slice
|
||||
slice: complex128 2 2
|
||||
pointer: true
|
||||
nil: false
|
||||
indexing: 0
|
||||
reflect type: complex128 settable=true
|
||||
complex: (+1.000000e+000+0.000000e+000i)
|
||||
indexing: 1
|
||||
reflect type: complex128 settable=true
|
||||
complex: (+1.128000e+000+4.000000e-001i)
|
||||
reflect type: array
|
||||
array
|
||||
reflect type: func
|
||||
func
|
||||
nil: true
|
||||
reflect type: func
|
||||
func
|
||||
nil: false
|
||||
reflect type: map
|
||||
map
|
||||
nil: true
|
||||
reflect type: map
|
||||
map
|
||||
nil: false
|
||||
reflect type: struct
|
||||
struct
|
||||
reflect type: struct
|
||||
struct
|
||||
|
||||
sizes:
|
||||
int8 1
|
||||
int16 2
|
||||
int32 4
|
||||
int64 8
|
||||
uint8 1
|
||||
uint16 2
|
||||
uint32 4
|
||||
uint64 8
|
||||
float32 4
|
||||
float64 8
|
||||
complex64 8
|
||||
complex128 16
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("stdin: ", os.Stdin.Fd())
|
||||
fmt.Println("stdout:", os.Stdout.Fd())
|
||||
fmt.Println("stderr:", os.Stderr.Fd())
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
stdin: 0
|
||||
stdout: 1
|
||||
stderr: 2
|
||||
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
// version of this package.
|
||||
// Update this value before release of new version of software.
|
||||
const version = "0.3.0"
|
||||
Reference in New Issue
Block a user