Compare commits

..

82 Commits

Author SHA1 Message Date
Ayke van Laethem b1744db2c8 main: version 0.4.0 2019-03-09 20:41:38 +01:00
Ayke van Laethem bd6a7b69ce compiler: inline slice bounds checking
This improves code size in all tests by about 1% and up to 5% in some
cases, likely because LLVM can better reason about inline bounds checks.
2019-03-08 19:11:22 +01:00
Ayke van Laethem 051ad07755 compiler: refactor slice related asserts
Move these asserts into compiler/asserts.go, to keep them together.

The make([]T) asserts aren't moved yet because that code is (still!)
quite ugly and in need of some clean up.
2019-03-08 19:11:22 +01:00
Ron Evans 09e85b7859 machine/stm32f103xx: correct convertion for fractional timing of RTC as used in ticks() function
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-08 17:52:51 +01:00
Ayke van Laethem 622d0ebde6 compiler: implement nil checks
This commit implements nil checks for all platforms. These nil checks
can be optimized on systems with a MMU, but since a major target is
systems without MMU, keep it this way for now.

It implements three checks:
  * Nil checks before dereferencing a pointer.
  * Nil checks before calculating an address (*ssa.FieldAddr and
    *ssa.IndexAddr)
  * Nil checks before calling a function pointer.

The first check has by far the biggest impact, with around 5% increase
in code size. The other checks only trigger in only some test cases and
have a minimal impact on code size.
This first nil check is also the one that is easiest to avoid on systems
with MMU, if necessary.
2019-03-08 17:36:53 +01:00
Ayke van Laethem b7cdf8cd0c interp: refactor to eliminate lots of code
This may cause a small performance penalty, but the code is easier to
maange as a result.
2019-03-08 17:36:53 +01:00
Ayke van Laethem cfc1a66e8d interp: use correct initialization order on panic() calls
Whenever interp hits an unreachable instruction, it bails out at that
point. However, it used to insert new instructions at the bottom with
the old init calls still at the top. So when a panic() happened in a
non-main package, the last packages to init would actually be called
first.

This commit fixes this by setting the insert point at the top of
runtime.initAll before starting interpretation, so the initialization
order is still correct when a panic() happens during init.
2019-03-07 16:22:06 +01:00
Ayke van Laethem 4ad9bd8643 wasm: ignore arguments and environment variables
The wasm_exec.js file copied from the main Go repository did write those
values to address 4096 in linear memory, which led to memory corruption
in linear memory. Remove these things for now, until they're actually
supported, if support is ever added.
2019-03-07 13:13:11 +01:00
Ron Evans 2a1dd98661 compiler: support output file using UF2 bootloader format
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-06 18:18:23 +01:00
Ayke van Laethem 2c03192691 LICENSE: update author and year 2019-03-06 17:15:31 +01:00
Ron Evans 9d6df2b4c7 machine/samd21: implement ADC
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-06 17:01:16 +01:00
Ayke van Laethem 5939729c45 main: only run WebAssembly tests on Linux
The WebAssembly target is not yet considered stable in LLVM 7, but has
been enabled in the Debian builds so tests can run on Debian. However,
the Homebrew builds don't have it enabled which results in test
failures.

Temporarily run WebAssembly tests only on Linux to fix this. This can be
reverted after a switch to LLVM 8, which has WebAssembly enabled by
default.
2019-03-06 11:28:59 +01:00
Ayke van Laethem c7b91da8c4 compiler: support function pointers outside of addrspace 0
In LLVM 8, the AVR backend has moved all function pointers to address
space 1 by default. Much of the code still assumes function pointers
live in address space 0, leading to assertion failures.

This commit fixes this problem by autodetecting function pointers and
avoiding them in interface pseudo-calls.
2019-03-05 19:54:55 +01:00
Ayke van Laethem c7fdb6741f compiler: rename biggestInt → capacityType 2019-03-05 19:25:42 +01:00
Ayke van Laethem b837c94366 compiler: calculate max number of entries in slice at compile time
This avoids difficult multiply-with-overflow code and avoids a multiply
at runtime.
2019-03-05 19:25:42 +01:00
Ayke van Laethem 26e7e93478 compiler: make sure make([]T, ...) checks for Ts bigger than 1
Without this, the following code would not panic:

    func getInt(i int) { return i }
    make([][1<<18], getInt(1<<18))

Or this code would be allowed to compile for 32-bit systems:

    make([][1<<18], 1<<18)
2019-03-05 19:25:42 +01:00
Ayke van Laethem 8e99c3313b compiler: fix make([]T, ...) with big integers on 32-bit systems or less
Previously, this would have resulted in a LLVM verification error
because runtime.sliceBoundsCheckMake would not accept 64-bit integers on
these platforms.
2019-03-05 19:25:42 +01:00
Ron Evans 28987ae061 docs: update README with recently added Adafruit Circuit Playground Express board
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-05 10:08:39 +01:00
Ayke van Laethem b594f212fb test: add WebAssembly tests 2019-03-04 21:58:40 +01:00
Ayke van Laethem 41e093d7bb wasm: switch emulator to node.js
Unfortunately, the olin/cwa emulator does not handle floats correctly.
Node.js does, and because it is also supported by the Go WebAssembly
implementation it has better support in general.
2019-03-04 21:58:40 +01:00
Ron Evans 665c3bdaa6 machine/samd21: implement SPI interface for currently supported SAMD21 boards
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 21:47:09 +01:00
Ayke van Laethem ea3d232c84 circleci: replace Linux tests on Travis CI with CircleCI
CircleCI is faster and has more features than Travis CI. Additionally,
based on the recent news, the future of Travis CI is rather uncertain.

Keep using Travis CI for macOS testing at the moment, as open source
projects will need to get special permission to use CircleCI for macOS
tests.
2019-03-04 21:42:12 +01:00
Ayke van Laethem 4f932b6e66 all: use internal objcopy implementation
This lessens the dependency on binutils (e.g. arm-none-eabi-objcopy).
2019-03-04 21:17:56 +01:00
Ron Evans 3538ba943c machine/samd21: move definitions for I2C interfaces into board files, since pin connections on each SAMD21-based board implementation can differ
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 20:54:13 +01:00
Ron Evans 543696eafc machine/samd21: correct get/setPinCfg and get/setPMux functions for PORTB pins
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 20:53:07 +01:00
Ron Evans 6e5ae83302 machine/samd21: init all SERCOM clocks to better handle board variants
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 17:17:03 +01:00
Ayke van Laethem 9b4071237f arm: switch to hardfloat ABI for Linux
This avoids an error on the Raspberry Pi 3.
2019-03-01 20:36:12 +01:00
Ayke van Laethem 1c68da89af main: version 0.3.0 2019-02-27 12:14:04 +01:00
Ron Evans 4424fe087d machine/circuitplay_express: add basic support for Adafruit Circuit Playground express pin mappings
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-24 23:01:22 +01:00
Ron Evans 34939ab422 machine/atsamd21: add GPIO_INPUT_PULLUP and GPIO_INPUT_PULLDOWN GPIO pin config options
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-24 21:20:59 +01:00
Ron Evans c56b2a45fa machine/samd21: handle PINMUX and PINCFG registers correctly for PORTB pins
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-24 17:43:11 +01:00
Ayke van Laethem b1c70d85f7 nrf: add CPU frequency 2019-02-24 13:45:10 +01:00
Ayke van Laethem 714d98354c arm: provide intrinsics to disable/enable interrupts 2019-02-23 18:52:49 +01:00
Ayke van Laethem 6e8df2fc40 samd21: define and use hardware pin numbers 2019-02-23 16:20:56 +01:00
Ayke van Laethem 902f40867f samd21: add GPIO support for port B 2019-02-23 13:53:59 +01:00
Ron Evans 5438f16fcb machine/atsamd21: support for USB CDC aka serial interface
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-23 13:34:00 +01:00
Ron Evans 7f027ddd33 machine/samd21: correct calculation for runtime ticks() function so that go routine scheduling can function as expected as described in issue #149
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-23 13:22:24 +01:00
Ron Evans acaf096586 compiler: extend flash command to support different output file types, based on contents of flash key in target file
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-23 11:31:38 +01:00
Ron Evans 942d4903ce machine/atsamd21: extracts functionality for processor family into shared files.
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-20 14:16:09 +01:00
Ayke van Laethem 0b212cf2f6 all: add macOS support 2019-02-19 15:54:36 +01:00
Ron Evans 2d5bc836f5 build: correct Makefile to build tinygo executable correctly when build directory does not exist, such as after running 'make clean'
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-19 12:28:50 +01:00
Ayke van Laethem 856e5fa179 ir: remove old cgo related code
There is now a custom implementation of CGo based on libclang.
2019-02-19 09:08:13 +01:00
Ayke van Laethem 07733ca056 compiler: remove some dead code reported by go vet 2019-02-19 09:08:13 +01:00
Ayke van Laethem 92d9b780b5 all: remove init interpretation during IR construction
The interp package does a much better job at interpretation, and is
implemented as a pass on the IR which makes it much easier to compose.
Also, the implementation works much better as it is based on LLVM IR
instead of Go SSA.
2019-02-19 09:08:13 +01:00
Ayke van Laethem da345e8723 cgo: implement bool/float/complex types 2019-02-18 17:17:56 +01:00
Ayke van Laethem fab38a0749 compiler: use Clang data layout for complex numbers
Match data layout of complex numbers to that of Clang, for better
interoperability. This makes alignment of complex numbes the same as the
individual elements (real and imaginary), as is required by the C spec
and implemented in Clang, but unlike the gc compler. The Go language
specification is silent on this matter.

> Each complex type has the same object representation and alignment
> requirements as an array of two elements of the corresponding real
> type (float for float complex, double for double complex, long double
> for long double complex). The first element of the array holds the
> real part, and the second element of the array holds the imaginary
> component.

Source: https://en.cppreference.com/w/c/language/arithmetic_types
2019-02-18 17:17:56 +01:00
Daniel Esteban 0a3dbbd1cb Added regular pins const for bbc:microbit (#181)
* Added "GPIO/Analog" pins const for bbc:microbit
2019-02-11 16:33:10 +01:00
admin 4c29f0fdb6 wasm: support wasm example on Safari 2019-02-11 14:20:20 +01:00
Ayke van Laethem fbc2099ee3 main: version v0.2.0 2019-02-08 16:54:50 +01:00
Ayke van Laethem 95d895646a loader/cgo: add support for function pointers 2019-02-08 13:19:02 +01:00
Ayke van Laethem 35fb594f8f loader/cgo: add support for pointer types 2019-02-08 13:19:02 +01:00
Ayke 01f6aff422 loader: support global variables in CGo (#173)
Global variables (like functions) must be declared in the import "C" preamble and can then be used from Go.
2019-02-08 13:04:03 +01:00
Ron Evans 7dd5839f47 build: display output file sizes for target smoke test builds on Travis (#175)
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-08 13:03:20 +01:00
Ron Evans 403fee7e06 Add tinygo version subcommand (#172)
* cmd: add tinygo version subcommand to display current software version. Also displayed when usage is displayed
2019-02-08 11:22:47 +01:00
Ron Evans 7657238c24 docs: refactor README content (#171)
* docs: refactor README to avoid duplication with information on the web site, and to reorder to make it easier for new users.
2019-02-08 08:59:06 +01:00
Ayke van Laethem 3cba36f2ba compiler: add syscalls for 64-bit arm 2019-02-07 07:00:37 +01:00
Ayke van Laethem 93d5269fef compiler: add syscalls for 32-bit arm 2019-02-07 07:00:37 +01:00
Ayke van Laethem 4b477fad55 all: update Travis CI to Ubuntu Xenial
This lets us test with a more recent base, and should fix various
issues.
2019-02-05 19:39:33 +01:00
Ayke van Laethem 6360e318a7 runtime: add support for math package
The math package uses routines written in Go assembly language which
LLVM/Clang cannot parse. Additionally, not all instruction sets are
supported.

Redirect all math functions written in assembly to their Go equivalent.
This is not the fastest option, but it gets packages requiring math
functions to work.
2019-02-05 19:37:21 +01:00
Ayke van Laethem 0757eb5919 main: link with --gc-sections
This may help reduce code size in some cases.
2019-02-05 19:37:21 +01:00
Ayke van Laethem 013a71aa3d compiler: support NaN in float comparisons
LLVM supports both "ordered" and "unordered" floating point comparisons.
The difference is that unordered comparisons ignore NaN and produce
incorrect results when presented with a NaN value.
This commit switches these comparisons from ordered to unordered.
2019-02-05 19:37:21 +01:00
Ron Evans f0091b31b5 Add CONTRIBUTING.md to help us help you (#169)
* docs: add contributing guidelines to make it easier to help the project

Signed-off-by: Ron Evans <ron@hybridgroup.com>

* docs: add info about Slack channel to contributing guidelines

Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-02-05 18:25:19 +01:00
Ayke van Laethem 709a296150 os: add basic OS functionality 2019-02-05 17:37:55 +01:00
Ayke van Laethem f7b2a2c977 compiler: implement syscall.Syscall* as builtins
Treating them as builtins is easier to implement and likely reduces code
size.
2019-02-05 17:37:55 +01:00
Ayke van Laethem 6ae4b43eb2 interp: fix recursive scanning
There were a few issues that made interp not perform as it should:

  * The scan was non-recursive due to a bug.
  * Recursive scanning would always return the severity level, which is
    not always the best strategy.
2019-02-05 17:37:55 +01:00
Ayke van Laethem bece6b9648 interp: remove init call when hitting 'unreachable'
The interp package interprets calls in runtime.initAll and replaces
these calls with non-interpretable instructions if needed.
When hitting an unreachable instruction, this call should be removed,
but it wasn't. This commit makes sure the call is removed even before
trying to interpret the package init function.
2019-02-05 17:37:55 +01:00
Ayke van Laethem 553f00bdb8 interp: fix uintptr type context in interface
This bug led to a non-obvious validation error after interp had run.
2019-02-05 17:37:55 +01:00
Ayke van Laethem a789108926 test: add -short flag that only tests on the host 2019-02-05 17:37:55 +01:00
Ayke van Laethem d63ce0646c compiler: avoid all debug info with -no-debug 2019-02-05 17:37:55 +01:00
Ayke van Laethem 003211b4ff reflect: implement Value.Set*() for basic types 2019-02-05 17:11:09 +01:00
Ayke van Laethem e6720d7ddc compiler: add support for comparing complex numbers 2019-02-05 17:11:09 +01:00
Ayke van Laethem dfef168139 reflect: add limited support for all type kinds
This commit makes sure all Go types can be encoded in the interface type
code, so that Type.Kind() always returns a proper type kind for any
non-nil interface.
2019-02-05 17:11:09 +01:00
Ayke van Laethem 101f2e519b compiler: add support for zero channel constant 2019-02-05 17:11:09 +01:00
Ayke van Laethem eb34afde4b amd64: align on 16 bytes instead of 8
Some instructions emitted by LLVM (like movaps) expect 16-byte
alignment, while the allocator assumed that 8-byte alignment is good
enough.

TODO: this issue came to light with LLVM optimizing a complex128 store
to the movaps instruction, which must be aligned. It looks like this
means that the <2 x double> IR type is actually 16-byte aligned instead
of 8-byte like a double. If this is the case, the alignment of complex
numbers needs to be updated in the whole compiler.
2019-02-05 17:11:09 +01:00
Ayke van Laethem 63f2a3dfe9 reflect: support slices and indexing of strings and slices 2019-02-05 17:11:09 +01:00
Ayke van Laethem fb23e9c212 reflect: add support for non-named basic types 2019-02-05 17:11:09 +01:00
Ayke van Laethem 222c4c75b1 test: check for errors when globbing test packages 2019-02-05 17:11:08 +01:00
Ayke van Laethem 5a509f5bfe compiler: support some more types in interfaces 2019-02-05 17:11:08 +01:00
Ayke van Laethem 2e46275c45 compiler: support complex64 constants 2019-02-05 17:11:08 +01:00
Ayke van Laethem 1d34f868da compiler: sort interface type codes in reverse order
This makes sure the most commonly used types have the lowest type codes.
This was intended to be the case, but apparently I forgot to sort them
the right way.
2019-02-05 17:11:08 +01:00
Ayke van Laethem f0904779a5 reflect: add reflect.TypeOf
This is the beginning of true reflection support in TinyGo.
2019-02-05 17:11:08 +01:00
Samuel Lang 70f1064f36 making Docker build resilient (#168)
Currently, if the user hasn't run
`git submodule update --init` beforehand, the docker build will fail

This little addition makes the build atomic and ready for automatic CI tests for the future
2019-02-05 15:57:52 +01:00
80 changed files with 6982 additions and 2895 deletions
+102
View File
@@ -0,0 +1,102 @@
version: 2.1
commands:
submodules:
steps:
- run:
name: "Pull submodules"
command: git submodule update --init
apt-dependencies:
parameters:
llvm:
type: string
steps:
- run:
name: "Install apt dependencies"
command: |
echo 'deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
sudo apt-get update
sudo apt-get install \
llvm \
python3 \
llvm<<parameters.llvm>>-dev \
clang<<parameters.llvm>> \
libclang<<parameters.llvm>>-dev \
lld<<parameters.llvm>> \
gcc-arm-linux-gnueabihf \
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
install-node:
steps:
- run:
name: "Install node.js"
command: |
wget https://nodejs.org/dist/v10.15.1/node-v10.15.1-linux-x64.tar.xz
sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
rm node-v10.15.1-linux-x64.tar.xz
dep:
steps:
- run:
name: "Install Go dependencies"
command: |
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure --vendor-only
smoketest:
steps:
- run: tinygo build -size short -o test.elf -target=pca10040 examples/blinky1
- run: tinygo build -size short -o test.elf -target=pca10040 examples/blinky2
- run: tinygo build -size short -o blinky2 examples/blinky2
- run: tinygo build -size short -o test.elf -target=pca10040 examples/test
- run: tinygo build -size short -o test.elf -target=microbit examples/echo
- run: tinygo build -size short -o test.elf -target=nrf52840-mdk examples/blinky1
- run: tinygo build -size short -o test.elf -target=pca10031 examples/blinky1
- run: tinygo build -size short -o test.elf -target=bluepill examples/blinky1
- run: tinygo build -size short -o test.elf -target=arduino examples/blinky1
- run: tinygo build -size short -o test.elf -target=digispark examples/blinky1
- run: tinygo build -size short -o test.elf -target=reelboard examples/blinky1
- run: tinygo build -size short -o test.elf -target=reelboard examples/blinky2
- run: tinygo build -size short -o test.elf -target=pca10056 examples/blinky1
- run: tinygo build -size short -o test.elf -target=pca10056 examples/blinky2
- run: tinygo build -size short -o test.elf -target=itsybitsy-m0 examples/blinky1
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
jobs:
test-llvm7-go111:
docker:
- image: circleci/golang:1.11
working_directory: /go/src/github.com/tinygo-org/tinygo
steps:
- checkout
- submodules
- apt-dependencies:
llvm: "-7"
- install-node
- restore_cache:
keys:
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-{{ checksum "Gopkg.lock" }}
- dep
- run: go install .
- run: make test
- run: make gen-device -j4
- smoketest
- save_cache:
key: go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- ~/.cache/tinygo
workflows:
test-all:
jobs:
- test-llvm7-go111
+28 -22
View File
@@ -1,14 +1,21 @@
language: go
go:
- "1.11"
matrix:
include:
- os: osx
go: "1.11"
env: PATH="/usr/local/opt/llvm/bin:$PATH"
before_install:
- mkdir -p /Users/travis/gopath/bin
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:
homebrew:
update: true
taps: ArmMbed/homebrew-formulae
packages:
- llvm@7
- qemu
- arm-none-eabi-gcc
install:
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
@@ -18,17 +25,16 @@ 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
- 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
+57
View File
@@ -0,0 +1,57 @@
0.4.0
---
- **compiler**
- switch to the hardfloat ABI on ARM, which is more widely used
- avoid a dependency on `objcopy` (`arm-none-eabi-objcopy` etc.)
- fix a bug in `make([]T, n)` where `n` is 64-bits on a 32-bit platform
- adapt to a change in the AVR backend in LLVM 8
- directly support the .uf2 firmware format as used on Adafruit boards
- fix a bug when calling `panic()` at init time outside of the main package
- implement nil checks, which results in a ~5% increase in code size
- inline slice bounds checking, which results in a ~1% decrease in code size
- **targets**
- `samd21`: fix a bug in port B pins
- `samd21`: implement SPI peripheral
- `samd21`: implement ADC peripheral
- `stm32`: fix a bug in timekeeping
- `wasm`: fix a bug in `wasm_exec.js` that caused corruption in linear memory
when running on Node.js.
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
+48
View File
@@ -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
View File
@@ -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
+14 -4
View File
@@ -3,7 +3,15 @@
[[projects]]
branch = "master"
digest = "1:84316faef4ea12d34dde3b3e6dab682715a23b1c2bb8ab82cec9ab619766e214"
digest = "1:00b45e06c7843541372fc17d982242bd6adfc2fc382b6f2e9ef9ce53d87a50b9"
name = "github.com/marcinbor85/gohex"
packages = ["."]
pruneopts = "UT"
revision = "7a43cd876e46e0f6ddc553f10f91731a78e6e949"
[[projects]]
branch = "master"
digest = "1:ba70784a3deee74c0ca3c87bcac3c2f93d3b2d27d8f237b768c358b45ba47da8"
name = "golang.org/x/tools"
packages = [
"go/ast/astutil",
@@ -11,20 +19,22 @@
"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 = [
"github.com/marcinbor85/gohex",
"golang.org/x/tools/go/ast/astutil",
"golang.org/x/tools/go/ssa",
"tinygo.org/x/go-llvm",
]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2018 Ayke van Laethem. All rights reserved.
Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
+2 -2
View File
@@ -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 .
+70 -132
View File
@@ -1,145 +1,72 @@
# TinyGo - Go compiler for microcontrollers
# TinyGo - Go compiler for small places
[![Build Status](https://travis-ci.com/aykevl/tinygo.svg?branch=master)](https://travis-ci.com/tinygo-org/tinygo)
[![Travis CI](https://travis-ci.com/tinygo-org/tinygo.svg?branch=dev)](https://travis-ci.com/tinygo-org/tinygo)
[![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
> 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 Circuit Playground Express](https://www.adafruit.com/product/3333)
* [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 +81,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.
+129
View File
@@ -0,0 +1,129 @@
package compiler
// This file implements functions that do certain safety checks that are
// required by the Go programming language.
import (
"go/types"
"tinygo.org/x/go-llvm"
)
// emitLookupBoundsCheck emits a bounds check before doing a lookup into a
// slice. This is required by the Go language spec: an index out of bounds must
// cause a panic.
func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Value, indexType types.Type) {
if frame.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
}
if index.Type().IntTypeWidth() < arrayLen.Type().IntTypeWidth() {
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
// correctly extend that type.
if indexType.(*types.Basic).Info()&types.IsUnsigned == 0 {
index = c.builder.CreateZExt(index, arrayLen.Type(), "")
} else {
index = c.builder.CreateSExt(index, arrayLen.Type(), "")
}
} else if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() {
// The index is bigger than the array length type, so extend it.
arrayLen = c.builder.CreateZExt(arrayLen, index.Type(), "")
}
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "lookup.outofbounds")
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "lookup.next")
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Now do the bounds check: index >= arrayLen
outOfBounds := c.builder.CreateICmp(llvm.IntUGE, index, arrayLen, "")
c.builder.CreateCondBr(outOfBounds, faultBlock, nextBlock)
// Fail: this is a nil pointer, exit with a panic.
c.builder.SetInsertPointAtEnd(faultBlock)
c.createRuntimeCall("lookuppanic", nil, "")
c.builder.CreateUnreachable()
// Ok: this is a valid pointer.
c.builder.SetInsertPointAtEnd(nextBlock)
}
// emitSliceBoundsCheck emits a bounds check before a slicing operation to make
// sure it is within bounds.
func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.Value, lowType, highType *types.Basic) {
if frame.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
}
// Extend the capacity integer to be at least as wide as low and high.
capacityType := capacity.Type()
if low.Type().IntTypeWidth() > capacityType.IntTypeWidth() {
capacityType = low.Type()
}
if high.Type().IntTypeWidth() > capacityType.IntTypeWidth() {
capacityType = high.Type()
}
if capacityType != capacity.Type() {
capacity = c.builder.CreateZExt(capacity, capacityType, "")
}
// Extend low and high to be the same size as capacity.
if low.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
if lowType.Info()&types.IsUnsigned != 0 {
low = c.builder.CreateZExt(low, capacityType, "")
} else {
low = c.builder.CreateSExt(low, capacityType, "")
}
}
if high.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
if highType.Info()&types.IsUnsigned != 0 {
high = c.builder.CreateZExt(high, capacityType, "")
} else {
high = c.builder.CreateSExt(high, capacityType, "")
}
}
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "slice.outofbounds")
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "slice.next")
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Now do the bounds check: low > high || high > capacity
outOfBounds1 := c.builder.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh")
outOfBounds2 := c.builder.CreateICmp(llvm.IntUGT, high, capacity, "slice.highcap")
outOfBounds := c.builder.CreateOr(outOfBounds1, outOfBounds2, "slice.outofbounds")
c.builder.CreateCondBr(outOfBounds, faultBlock, nextBlock)
// Fail: this is a nil pointer, exit with a panic.
c.builder.SetInsertPointAtEnd(faultBlock)
c.createRuntimeCall("slicepanic", nil, "")
c.builder.CreateUnreachable()
// Ok: this is a valid pointer.
c.builder.SetInsertPointAtEnd(nextBlock)
}
// emitNilCheck checks whether the given pointer is nil, and panics if it is. It
// has no effect in well-behaved programs, but makes sure no uncaught nil
// pointer dereferences exist in valid Go code.
func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string) {
// Check whether this is a nil pointer.
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".nil")
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".next")
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Compare against nil.
nilptr := llvm.ConstPointerNull(ptr.Type())
isnil := c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
c.builder.CreateCondBr(isnil, faultBlock, nextBlock)
// Fail: this is a nil pointer, exit with a panic.
c.builder.SetInsertPointAtEnd(faultBlock)
c.createRuntimeCall("nilpanic", nil, "")
c.builder.CreateUnreachable()
// Ok: this is a valid pointer.
c.builder.SetInsertPointAtEnd(nextBlock)
}
+197 -492
View File
File diff suppressed because it is too large Load Diff
+20 -31
View File
@@ -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
@@ -313,7 +304,7 @@ func (p *lowerInterfacesPass) run() {
// interface value should already have returned false.
// Replace the function pointer with undef (which will then be
// called), indicating to the optimizer this code is unreachable.
use.ReplaceAllUsesWith(llvm.Undef(p.i8ptrType))
use.ReplaceAllUsesWith(llvm.Undef(p.uintptrType))
use.EraseFromParentAsInstruction()
} else if len(itf.types) == 1 {
// There is only one implementation of the given type.
@@ -323,12 +314,12 @@ func (p *lowerInterfacesPass) run() {
// There are multiple types implementing this interface, thus there
// are multiple possible functions to call. Delegate calling the
// right function to a special wrapper function.
bitcasts := getUses(use)
if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() {
panic("expected exactly one bitcast use of runtime.interfaceMethod")
inttoptrs := getUses(use)
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
panic("expected exactly one inttoptr use of runtime.interfaceMethod")
}
bitcast := bitcasts[0]
calls := getUses(bitcast)
inttoptr := inttoptrs[0]
calls := getUses(inttoptr)
if len(calls) != 1 || calls[0].IsACallInst().IsNil() {
panic("expected exactly one call use of runtime.interfaceMethod")
}
@@ -349,14 +340,14 @@ func (p *lowerInterfacesPass) run() {
// call, after selecting the right concrete type.
redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
// Replace the old lookup/bitcast/call with the new call.
// Replace the old lookup/inttoptr/call with the new call.
p.builder.SetInsertPointBefore(call)
retval := p.builder.CreateCall(redirector, params, "")
if retval.Type().TypeKind() != llvm.VoidTypeKind {
call.ReplaceAllUsesWith(retval)
}
call.EraseFromParentAsInstruction()
bitcast.EraseFromParentAsInstruction()
inttoptr.EraseFromParentAsInstruction()
use.EraseFromParentAsInstruction()
}
}
@@ -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 {
@@ -553,22 +542,22 @@ func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo {
return p.signatures[name]
}
// replaceInvokeWithCall replaces a runtime.interfaceMethod + bitcast with a
// replaceInvokeWithCall replaces a runtime.interfaceMethod + inttoptr with a
// concrete method. This can be done when only one type implements the
// interface.
func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) {
bitcasts := getUses(use)
if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() {
panic("expected exactly one bitcast use of runtime.interfaceMethod")
inttoptrs := getUses(use)
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
panic("expected exactly one inttoptr use of runtime.interfaceMethod")
}
bitcast := bitcasts[0]
inttoptr := inttoptrs[0]
function := typ.getMethod(signature).function
if bitcast.Type() != function.Type() {
if inttoptr.Type() != function.Type() {
p.builder.SetInsertPointBefore(use)
function = p.builder.CreateBitCast(function, bitcast.Type(), "")
function = p.builder.CreateBitCast(function, inttoptr.Type(), "")
}
bitcast.ReplaceAllUsesWith(function)
bitcast.EraseFromParentAsInstruction()
inttoptr.ReplaceAllUsesWith(function)
inttoptr.EraseFromParentAsInstruction()
use.EraseFromParentAsInstruction()
}
@@ -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.
+108 -24
View File
@@ -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) {
@@ -119,7 +203,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
}
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
signatureGlobal,
llvm.ConstBitCast(fn, c.i8ptrType),
llvm.ConstPtrToInt(fn, c.uintptrType),
})
methods[i] = methodInfo
}
@@ -316,7 +400,7 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
c.getMethodSignature(instr.Method),
}
fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func")
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
fnCast := c.builder.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast")
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
args := []llvm.Value{receiverValue}
+156
View File
@@ -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
}
}
+176
View File
@@ -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)
}
}
+18 -17
View File
@@ -84,15 +84,18 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
// Memory operators
case !inst.IsAAllocaInst().IsNil():
fr.locals[inst] = &AllocaValue{
Underlying: getZeroValue(inst.Type().ElementType()),
Dirty: false,
allocType := inst.Type().ElementType()
alloca := llvm.AddGlobal(fr.Mod, allocType, fr.pkgName+"$alloca")
alloca.SetInitializer(getZeroValue(allocType))
alloca.SetLinkage(llvm.InternalLinkage)
fr.locals[inst] = &LocalValue{
Underlying: alloca,
Eval: fr.Eval,
}
case !inst.IsALoadInst().IsNil():
operand := fr.getLocal(inst.Operand(0))
operand := fr.getLocal(inst.Operand(0)).(*LocalValue)
var value llvm.Value
if !operand.IsConstant() || inst.IsVolatile() {
if !operand.IsConstant() || inst.IsVolatile() || operand.Underlying.Opcode() == llvm.BitCast {
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
} else {
value = operand.Load()
@@ -173,11 +176,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
continue // special case: bitcast of alloc
}
}
value := fr.getLocal(operand)
if bc, ok := value.(*PointerCastValue); ok {
value = bc.Underlying // avoid double bitcasts
}
fr.locals[inst] = &PointerCastValue{Eval: fr.Eval, Underlying: value, CastType: inst.Type()}
value := fr.getLocal(operand).(*LocalValue)
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
// Other operators
case !inst.IsAICmpInst().IsNil():
@@ -222,7 +222,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
alloc := llvm.AddGlobal(fr.Mod, allocType, fr.pkgName+"$alloc")
alloc.SetInitializer(getZeroValue(allocType))
alloc.SetLinkage(llvm.InternalLinkage)
result := &GlobalValue{
result := &LocalValue{
Underlying: alloc,
Eval: fr.Eval,
}
@@ -246,15 +246,15 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
m := fr.getLocal(inst.Operand(0)).(*MapValue)
// "key" is a Go string value, which in the TinyGo calling convention is split up
// into separate pointer and length parameters.
keyBuf := fr.getLocal(inst.Operand(1))
keyLen := fr.getLocal(inst.Operand(2))
valPtr := fr.getLocal(inst.Operand(3))
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
m.PutString(keyBuf, keyLen, valPtr)
case callee.Name() == "runtime.hashmapBinarySet":
// set a binary (int etc.) key in the map
m := fr.getLocal(inst.Operand(0)).(*MapValue)
keyBuf := fr.getLocal(inst.Operand(1))
valPtr := fr.getLocal(inst.Operand(2))
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
m.PutBinary(keyBuf, valPtr)
case callee.Name() == "runtime.stringConcat":
// adding two strings together
@@ -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.
+14 -8
View File
@@ -42,10 +42,19 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
initAll := mod.NamedFunction(name)
bb := initAll.EntryBasicBlock()
e.builder.SetInsertPointBefore(bb.LastInstruction())
// Create a dummy alloca in the entry block that we can set the insert point
// to. This is necessary because otherwise we might be removing the
// instruction (init call) that we are removing after successful
// interpretation.
e.builder.SetInsertPointBefore(bb.FirstInstruction())
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
e.builder.SetInsertPointBefore(dummy)
e.builder.SetInstDebugLocation(bb.FirstInstruction())
var initCalls []llvm.Value
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst == dummy {
continue
}
if !inst.IsAReturnInst().IsNil() {
break // ret void
}
@@ -63,14 +72,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
@@ -114,11 +124,7 @@ func (e *Eval) function(fn llvm.Value, params []Value, pkgName, indent string) (
// getValue determines what kind of LLVM value it gets and returns the
// appropriate Value type.
func (e *Eval) getValue(v llvm.Value) Value {
if !v.IsAGlobalVariable().IsNil() {
return &GlobalValue{e, v}
} else {
return &LocalValue{e, v}
}
return &LocalValue{e, v}
}
// markDirty marks the passed-in LLVM value dirty, recursively. For example,
+21 -7
View File
@@ -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
}
}
+72 -309
View File
@@ -36,11 +36,17 @@ func (v *LocalValue) Type() llvm.Type {
}
func (v *LocalValue) IsConstant() bool {
if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok {
return false
}
return v.Underlying.IsConstant()
}
// Load loads a constant value if this is a constant GEP, otherwise it panics.
// Load loads a constant value if this is a constant pointer.
func (v *LocalValue) Load() llvm.Value {
if !v.Underlying.IsAGlobalVariable().IsNil() {
return v.Underlying.Initializer()
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr:
indices := v.getConstGEPIndices()
@@ -50,21 +56,32 @@ func (v *LocalValue) Load() llvm.Value {
global := v.Eval.getValue(v.Underlying.Operand(0))
agg := global.Load()
return llvm.ConstExtractValue(agg, indices[1:])
case llvm.BitCast:
panic("interp: load from a bitcast")
default:
panic("interp: load from a constant")
}
}
// Store stores to the underlying value if the value type is a constant GEP,
// Store stores to the underlying value if the value type is a pointer type,
// otherwise it panics.
func (v *LocalValue) Store(value llvm.Value) {
if !v.Underlying.IsAGlobalVariable().IsNil() {
if !value.IsConstant() {
v.MarkDirty()
v.Eval.builder.CreateStore(value, v.Underlying)
} else {
v.Underlying.SetInitializer(value)
}
return
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr:
indices := v.getConstGEPIndices()
if indices[0] != 0 {
panic("invalid GEP")
}
global := &GlobalValue{v.Eval, v.Underlying.Operand(0)}
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
agg := global.Load()
agg = llvm.ConstInsertValue(agg, value, indices[1:])
global.Store(agg)
@@ -74,10 +91,13 @@ func (v *LocalValue) Store(value llvm.Value) {
}
}
// GetElementPtr returns a constant GEP when the underlying value is also a
// constant GEP. It panics when the underlying value is not a constant GEP:
// getting the pointer to a constant is not possible.
// GetElementPtr returns a GEP when the underlying value is of pointer type.
func (v *LocalValue) GetElementPtr(indices []uint32) Value {
if !v.Underlying.IsAGlobalVariable().IsNil() {
int32Type := v.Underlying.Type().Context().Int32Type()
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
return &LocalValue{v.Eval, gep}
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr, llvm.IntToPtr:
int32Type := v.Underlying.Type().Context().Int32Type()
@@ -107,283 +127,18 @@ func (v *LocalValue) getConstGEPIndices() []uint32 {
return indices
}
// GlobalValue wraps a LLVM global variable.
type GlobalValue struct {
Eval *Eval
Underlying llvm.Value
}
// Value returns the initializer for this global variable.
func (v *GlobalValue) Value() llvm.Value {
return v.Underlying
}
// Type returns the type of this global variable, which is a pointer type. Use
// Type().ElementType() to get the actual global variable type.
func (v *GlobalValue) Type() llvm.Type {
return v.Underlying.Type()
}
// IsConstant returns true if this global is not dirty, false otherwise.
func (v *GlobalValue) IsConstant() bool {
if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok {
return false
}
return true
}
// Load returns the initializer of the global variable.
func (v *GlobalValue) Load() llvm.Value {
return v.Underlying.Initializer()
}
// Store sets the initializer of the global variable.
func (v *GlobalValue) Store(value llvm.Value) {
if !value.IsConstant() {
v.MarkDirty()
v.Eval.builder.CreateStore(value, v.Underlying)
} else {
v.Underlying.SetInitializer(value)
}
}
// GetElementPtr returns a constant GEP on this global, which can be used in
// load and store instructions.
func (v *GlobalValue) GetElementPtr(indices []uint32) Value {
int32Type := v.Underlying.Type().Context().Int32Type()
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
return &LocalValue{v.Eval, gep}
}
func (v *GlobalValue) String() string {
return "&GlobalValue{" + v.Underlying.Name() + "}"
}
// MarkDirty marks this global as dirty, meaning that every load from and store
// to this global (from now on) must be performed at runtime.
func (v *GlobalValue) MarkDirty() {
func (v *LocalValue) MarkDirty() {
if v.Underlying.IsAGlobalVariable().IsNil() {
panic("trying to mark a non-global as dirty")
}
if !v.IsConstant() {
return // already dirty
}
v.Eval.dirtyGlobals[v.Underlying] = struct{}{}
}
// An alloca represents a local alloca, which is a stack allocated variable.
// It is emulated by storing the constant of the alloca.
type AllocaValue struct {
Eval *Eval
Underlying llvm.Value // the constant value itself if not dirty, otherwise the alloca instruction
Dirty bool // this value must be evaluated at runtime
}
// Value turns this alloca into a runtime alloca instead of a compile-time
// constant (if not already converted), and returns the alloca itself.
func (v *AllocaValue) Value() llvm.Value {
if !v.Dirty {
// Mark this alloca a dirty, meaning it is run at runtime instead of
// compile time.
alloca := v.Eval.builder.CreateAlloca(v.Underlying.Type(), "")
v.Eval.builder.CreateStore(v.Underlying, alloca)
v.Dirty = true
v.Underlying = alloca
}
return v.Underlying
}
// Type returns the type of this alloca, which is always a pointer.
func (v *AllocaValue) Type() llvm.Type {
if v.Dirty {
return v.Underlying.Type()
} else {
return llvm.PointerType(v.Underlying.Type(), 0)
}
}
func (v *AllocaValue) IsConstant() bool {
return !v.Dirty
}
// Load returns the value this alloca contains, which may be evaluated at
// runtime.
func (v *AllocaValue) Load() llvm.Value {
if v.Dirty {
ret := v.Eval.builder.CreateLoad(v.Underlying, "")
if ret.IsNil() {
panic("alloca is nil")
}
return ret
} else {
if v.Underlying.IsNil() {
panic("alloca is nil")
}
return v.Underlying
}
}
// Store updates the value of this alloca.
func (v *AllocaValue) Store(value llvm.Value) {
if v.Underlying.Type() != value.Type() {
panic("interp: trying to store to an alloca with a different type")
}
if v.Dirty || !value.IsConstant() {
v.Eval.builder.CreateStore(value, v.Value())
} else {
v.Underlying = value
}
}
// GetElementPtr returns a value (a *GetElementPtrValue) that keeps a reference
// to this alloca, so that Load() and Store() continue to work.
func (v *AllocaValue) GetElementPtr(indices []uint32) Value {
return &GetElementPtrValue{v, indices}
}
func (v *AllocaValue) String() string {
return "&AllocaValue{Type: " + v.Type().String() + "}"
}
// GetElementPtrValue wraps an alloca, keeping track of what the GEP points to
// so it can be used as a pointer value (with Load() and Store()).
type GetElementPtrValue struct {
Alloca *AllocaValue
Indices []uint32
}
// Type returns the type of this GEP, which is always of type pointer.
func (v *GetElementPtrValue) Type() llvm.Type {
if v.Alloca.Dirty {
return v.Value().Type()
} else {
return llvm.PointerType(v.Load().Type(), 0)
}
}
func (v *GetElementPtrValue) IsConstant() bool {
return v.Alloca.IsConstant()
}
// Value creates the LLVM GEP instruction of this GetElementPtrValue wrapper and
// returns it.
func (v *GetElementPtrValue) Value() llvm.Value {
if v.Alloca.Dirty {
alloca := v.Alloca.Value()
int32Type := v.Alloca.Type().Context().Int32Type()
llvmIndices := getLLVMIndices(int32Type, v.Indices)
return v.Alloca.Eval.builder.CreateGEP(alloca, llvmIndices, "")
} else {
panic("interp: todo: pointer to alloca gep")
}
}
// Load deferences the pointer this GEP points to. For a constant GEP, it
// extracts the value from the underlying alloca.
func (v *GetElementPtrValue) Load() llvm.Value {
if v.Alloca.Dirty {
gep := v.Value()
return v.Alloca.Eval.builder.CreateLoad(gep, "")
} else {
underlying := v.Alloca.Load()
indices := v.Indices
if indices[0] != 0 {
panic("invalid GEP")
}
return llvm.ConstExtractValue(underlying, indices[1:])
}
}
// Store stores to the pointer this GEP points to. For a constant GEP, it
// updates the underlying allloca.
func (v *GetElementPtrValue) Store(value llvm.Value) {
if v.Alloca.Dirty || !value.IsConstant() {
alloca := v.Alloca.Value()
int32Type := v.Alloca.Type().Context().Int32Type()
llvmIndices := getLLVMIndices(int32Type, v.Indices)
gep := v.Alloca.Eval.builder.CreateGEP(alloca, llvmIndices, "")
v.Alloca.Eval.builder.CreateStore(value, gep)
} else {
underlying := v.Alloca.Load()
indices := v.Indices
if indices[0] != 0 {
panic("invalid GEP")
}
underlying = llvm.ConstInsertValue(underlying, value, indices[1:])
v.Alloca.Store(underlying)
}
}
func (v *GetElementPtrValue) GetElementPtr(indices []uint32) Value {
if v.Alloca.Dirty {
panic("interp: todo: gep on a dirty gep")
} else {
combined := append([]uint32{}, v.Indices...)
combined[len(combined)-1] += indices[0]
combined = append(combined, indices[1:]...)
return &GetElementPtrValue{v.Alloca, combined}
}
}
func (v *GetElementPtrValue) String() string {
indices := ""
for _, n := range v.Indices {
if indices != "" {
indices += ", "
}
indices += strconv.Itoa(int(n))
}
return "&GetElementPtrValue{Alloca: " + v.Alloca.String() + ", Indices: [" + indices + "]}"
}
// PointerCastValue represents a bitcast operation on a pointer.
type PointerCastValue struct {
Eval *Eval
Underlying Value
CastType llvm.Type
}
// Value returns a constant bitcast value.
func (v *PointerCastValue) Value() llvm.Value {
from := v.Underlying.Value()
return llvm.ConstBitCast(from, v.CastType)
}
// Type returns the type this pointer has been cast to.
func (v *PointerCastValue) Type() llvm.Type {
return v.CastType
}
func (v *PointerCastValue) IsConstant() bool {
return v.Underlying.IsConstant()
}
// Load tries to load and bitcast the given value. If this value cannot be
// bitcasted, Load panics.
func (v *PointerCastValue) Load() llvm.Value {
if v.Underlying.IsConstant() {
typeFrom := v.Underlying.Type().ElementType()
typeTo := v.CastType.ElementType()
if isScalar(typeFrom) && isScalar(typeTo) && v.Eval.TargetData.TypeAllocSize(typeFrom) == v.Eval.TargetData.TypeAllocSize(typeTo) {
return llvm.ConstBitCast(v.Underlying.Load(), v.CastType.ElementType())
}
}
panic("interp: load from a pointer bitcast: " + v.String())
}
// Store panics: it is not (yet) possible to store directly to a bitcast.
func (v *PointerCastValue) Store(value llvm.Value) {
panic("interp: store on a pointer bitcast")
}
// GetElementPtr panics: it is not (yet) possible to do a GEP operation on a
// bitcast.
func (v *PointerCastValue) GetElementPtr(indices []uint32) Value {
panic("interp: GEP on a pointer bitcast")
}
func (v *PointerCastValue) String() string {
return "&PointerCastValue{Value: " + v.Underlying.String() + ", CastType: " + v.CastType.String() + "}"
}
// MapValue implements a Go map which is created at compile time and stored as a
// global variable.
type MapValue struct {
@@ -534,27 +289,24 @@ func (v *MapValue) GetElementPtr(indices []uint32) Value {
// PutString does a map assign operation, assuming that the map is of type
// map[string]T.
func (v *MapValue) PutString(keyBuf, keyLen, valPtr Value) {
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
if !v.Underlying.IsNil() {
panic("map already created")
}
var value llvm.Value
switch valPtr := valPtr.(type) {
case *PointerCastValue:
value = valPtr.Underlying.Load()
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
}
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
}
value := valPtr.Load()
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
}
default:
panic("interp: todo: handle map value pointer")
}
keyType := v.Eval.Mod.GetTypeByName("runtime._string")
@@ -569,31 +321,42 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr Value) {
}
// PutBinary does a map assign operation.
func (v *MapValue) PutBinary(keyPtr, valPtr Value) {
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
if !v.Underlying.IsNil() {
panic("map already created")
}
var value llvm.Value
switch valPtr := valPtr.(type) {
case *PointerCastValue:
value = valPtr.Underlying.Load()
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
}
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
}
value := valPtr.Load()
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
}
default:
panic("interp: todo: handle map value pointer")
}
key := keyPtr.(*PointerCastValue).Underlying.Load()
v.KeyType = key.Type()
if keyPtr.Underlying.Opcode() == llvm.BitCast {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
}
key := keyPtr.Load()
if v.KeyType.IsNil() {
v.KeyType = key.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
panic("interp: map store key type has the wrong size")
}
} else {
if key.Type() != v.KeyType {
panic("interp: map store key type is inconsistent")
}
}
// TODO: avoid duplicate keys
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
-522
View File
@@ -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
}
+27 -30
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
}
}
+4 -2
View File
@@ -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"
+56 -38
View File
@@ -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")
}
@@ -260,19 +259,19 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
}
}
// Get an Intel .hex file or .bin file from the .elf file.
if outext == ".hex" || outext == ".bin" {
// Get an Intel .hex file or .bin file from the .elf file.
tmppath = filepath.Join(dir, "main"+outext)
format := map[string]string{
".hex": "ihex",
".bin": "binary",
}[outext]
cmd := exec.Command(spec.Objcopy, "-O", format, executable, tmppath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
err := Objcopy(executable, tmppath)
if err != nil {
return &commandError{"failed to extract " + format + " from", executable, err}
return err
}
} else if outext == ".uf2" {
// Get UF2 from the .elf file.
tmppath = filepath.Join(dir, "main"+outext)
err := ConvertELFFileToUF2File(executable, tmppath)
if err != nil {
return err
}
}
return action(tmppath)
@@ -319,14 +318,31 @@ 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"
case strings.Contains(spec.Flasher, "{uf2}"):
fileExt = ".uf2"
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 +471,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 +516,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 +536,6 @@ func main() {
dumpSSA: *dumpSSA,
debug: !*nodebug,
printSizes: *printSize,
initInterp: *initInterp,
wasmAbi: *wasmAbi,
}
@@ -601,6 +617,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()
+43 -3
View File
@@ -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,55 @@ 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-gnueabihf", 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-gnu", t)
})
}
t.Log("running tests for WebAssembly...")
for _, path := range matches {
if path == "testdata/gc.go" {
continue // known to fail
}
t.Run(path, func(t *testing.T) {
runTest(path, tmpdir, "wasm", t)
})
}
}
}
func runTest(path, tmpdir string, target string, t *testing.T) {
@@ -76,7 +116,7 @@ func runTest(path, tmpdir string, target string, t *testing.T) {
dumpSSA: false,
debug: false,
printSizes: "",
initInterp: true,
wasmAbi: "js",
}
binary := filepath.Join(tmpdir, "test")
err = Build("./"+path, binary, target, config)
+78
View File
@@ -0,0 +1,78 @@
package main
import (
"debug/elf"
"os"
"path/filepath"
"github.com/marcinbor85/gohex"
)
// ObjcopyError is an error returned by functions that act like objcopy.
type ObjcopyError struct {
Op string
Err error
}
func (e ObjcopyError) Error() string {
if e.Err == nil {
return e.Op
}
return e.Op + ": " + e.Err.Error()
}
// ExtractTextSegment returns the .text segment and the first address from the
// ELF file in the given path.
func ExtractTextSegment(path string) (uint64, []byte, error) {
f, err := elf.Open(path)
if err != nil {
return 0, nil, ObjcopyError{"failed to open ELF file to extract text segment", err}
}
defer f.Close()
text := f.Section(".text")
if text == nil {
return 0, nil, ObjcopyError{"file does not contain .text segment: " + path, nil}
}
data, err := text.Data()
if err != nil {
return 0, nil, ObjcopyError{"failed to extract .text segment from ELF file", err}
}
return text.Addr, data, nil
}
// Objcopy converts an ELF file to a different (simpler) output file format:
// .bin or .hex. It extracts only the .text section.
func Objcopy(infile, outfile string) error {
f, err := os.OpenFile(outfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer f.Close()
// Read the .text segment.
addr, data, err := ExtractTextSegment(infile)
if err != nil {
return err
}
// Write to the file, in the correct format.
switch filepath.Ext(outfile) {
case ".bin":
// The address is not stored in a .bin file (therefore you
// should use .hex files in most cases).
_, err := f.Write(data)
return err
case ".hex":
mem := gohex.NewMemory()
mem.SetStartAddress(uint32(addr)) // ignored in most cases (Intel-specific)
err := mem.AddBinary(uint32(addr), data)
if err != nil {
return ObjcopyError{"failed to create .hex file", err}
}
mem.DumpIntelHex(f, 32) // TODO: handle error
return nil
default:
panic("unreachable")
}
}
+18
View File
@@ -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")
}
+17 -5
View File
@@ -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();
+93
View File
@@ -0,0 +1,93 @@
// +build sam,atsamd21,circuitplay_express
package machine
import "device/sam"
// 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 (logical UART0)
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART0 pins (logical UART1)
const (
UART_TX_PIN = PB08 // PORTB
UART_RX_PIN = PB09 // PORTB
)
// I2C pins
const (
SDA_PIN = PB02 // I2C0 external
SCL_PIN = PB03 // I2C0 external
SDA1_PIN = PA00 // I2C1 internal
SCL1_PIN = PA01 // I2C1 internal
)
// I2C on the Circuit Playground Express.
var (
I2C0 = I2C{Bus: sam.SERCOM5_I2CM} // external device
I2C1 = I2C{Bus: sam.SERCOM1_I2CM} // internal device
)
// SPI pins (internal flash)
const (
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
SPI0_MOSI_PIN = PA20 // MOSI: SERCOM3/PAD[2]
SPI0_MISO_PIN = PA16 // MISO: SERCOM3/PAD[0]
)
// SPI on the Circuit Playground Express.
var (
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
)
+49 -24
View File
@@ -1,40 +1,48 @@
// +build sam,atsamd21g18a,itsybitsy_m0
// +build sam,atsamd21,itsybitsy_m0
package machine
import "device/sam"
// 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 +50,23 @@ 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]
)
// I2C on the ItsyBitsy M0.
var (
I2C0 = I2C{Bus: sam.SERCOM3_I2CM}
)
// SPI pins
const (
SPI0_SCK_PIN = PB11 // SCK: SERCOM4/PAD[3]
SPI0_MOSI_PIN = PB10 // MOSI: SERCOM4/PAD[2]
SPI0_MISO_PIN = PA12 // MISO: SERCOM4/PAD[0]
)
// SPI on the ItsyBitsy M0.
var (
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
)
+21
View File
@@ -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
-908
View File
@@ -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
}
}
+2
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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 {
+1 -1
View File
@@ -1,4 +1,4 @@
// +build nrf stm32f103xx
// +build nrf stm32f103xx atsamd21g18a
package machine
+599
View File
@@ -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)
}
+40
View File
@@ -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
}
+24
View File
@@ -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))
}
+34
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+4 -2
View File
@@ -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
}
+6 -4
View File
@@ -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.
@@ -37,8 +39,8 @@ func interfaceTypeAssert(ok bool) {
// See compiler/interface-lowering.go for details.
type interfaceMethodInfo struct {
signature *uint8 // external *i8 with a name identifying the Go function signature
funcptr *uint8 // bitcast from the actual function pointer
signature *uint8 // external *i8 with a name identifying the Go function signature
funcptr uintptr // bitcast from the actual function pointer
}
// Pseudo function call used while putting a concrete value in an interface,
@@ -57,4 +59,4 @@ func interfaceImplements(typecode uintptr, interfaceMethodSet **uint8) bool
// Pseudo function that returns a function pointer to the method to call.
// See the interface lowering pass for how this is lowered to a real call.
func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) *uint8
func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) uintptr
+224
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
// +build darwin
package runtime
const GOOS = "darwin"
+18 -25
View File
@@ -27,38 +27,31 @@ func _recover() interface{} {
return nil
}
// Check for bounds in *ssa.Index, *ssa.IndexAddr and *ssa.Lookup.
func lookupBoundsCheck(length uintptr, index int) {
if index < 0 || index >= int(length) {
runtimePanic("index out of range")
}
// Panic when trying to dereference a nil pointer.
func nilpanic() {
runtimePanic("nil pointer dereference")
}
// Check for bounds in *ssa.Index, *ssa.IndexAddr and *ssa.Lookup.
// Supports 64-bit indexes.
func lookupBoundsCheckLong(length uintptr, index int64) {
if index < 0 || index >= int64(length) {
runtimePanic("index out of range")
}
// Panic when trying to acces an array or slice out of bounds.
func lookuppanic() {
runtimePanic("index out of range")
}
// Check for bounds in *ssa.Slice.
func sliceBoundsCheck(capacity, low, high uintptr) {
if !(0 <= low && low <= high && high <= capacity) {
runtimePanic("slice out of range")
}
}
// Check for bounds in *ssa.Slice. Supports 64-bit indexes.
func sliceBoundsCheck64(capacity uintptr, low, high uint64) {
if !(0 <= low && low <= high && high <= uint64(capacity)) {
runtimePanic("slice out of range")
}
// Panic when trying to slice a slice out of bounds.
func slicepanic() {
runtimePanic("slice out of range")
}
// Check for bounds in *ssa.MakeSlice.
func sliceBoundsCheckMake(length, capacity uint) {
if !(0 <= length && length <= capacity) {
func sliceBoundsCheckMake(length, capacity uintptr, max uintptr) {
if length > capacity || capacity > max {
runtimePanic("slice size out of range")
}
}
// Check for bounds in *ssa.MakeSlice. Supports 64-bit indexes.
func sliceBoundsCheckMake64(length, capacity uint64, max uintptr) {
if length > capacity || capacity > uint64(max) {
runtimePanic("slice size out of range")
}
}
@@ -1,4 +1,4 @@
// +build sam,atsamd21g18a
// +build sam,atsamd21
package runtime
@@ -22,10 +22,11 @@ func main() {
func init() {
initClocks()
initRTC()
initUARTClock()
initI2CClock()
initSERCOMClocks()
initUSBClock()
initADCClock()
// 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
@@ -296,7 +293,7 @@ func handleRTC() {
timerWakeup = true
}
func initUARTClock() {
func initSERCOMClocks() {
// Turn on clock to SERCOM0 for UART0
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM0_
@@ -309,29 +306,64 @@ func initUARTClock() {
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM1 for UART1
// Turn on clock to SERCOM1
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM1_
// Use GCLK0 for SERCOM1 aka UART1
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
// GCLK_CLKCTRL_CLKEN ;
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM1_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
}
func initI2CClock() {
// Turn on clock to SERCOM3 for I2C0
// Turn on clock to SERCOM2
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM2_
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM2_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM3
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM3_
// Use GCLK0 for SERCOM3 aka I2C0
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
// GCLK_CLKCTRL_CLKEN ;
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM3_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM4
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM4_
// Use GCLK0 for SERCOM4
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM4_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM5
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM5_
// Use GCLK0 for SERCOM5
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM5_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
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()
}
func initADCClock() {
// Turn on clock for ADC
sam.PM.APBCMASK |= sam.PM_APBCMASK_ADC_
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer for ADC.
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_ADC << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
}
+2 -2
View File
@@ -124,8 +124,8 @@ func ticks() timeUnit {
// convert RTC counter from seconds to microseconds
timerCounter := uint64(stm32.RTC.CNTH<<16|stm32.RTC.CNTL) * 1000 * 1000
// add the fractional part of current time using DIV registers
timerCounter += (uint64(stm32.RTC.DIVH<<16|stm32.RTC.DIVL) / 1024 * 32 * 32) * 1000 * 1000
// add the fractional part of current time using DIV register
timerCounter += uint64(0x8000-stm32.RTC.DIVL) * 31
// change since last measurement
offset := (timerCounter - timerLastCounter)
+1 -1
View File
@@ -1,4 +1,4 @@
// +build linux
// +build darwin linux
package runtime
-13
View File
@@ -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")
}
+17 -12
View File
@@ -31,7 +31,6 @@ type TargetSpec struct {
CFlags []string `json:"cflags"`
LDFlags []string `json:"ldflags"`
ExtraFiles []string `json:"extra-files"`
Objcopy string `json:"objcopy"`
Emulator []string `json:"emulator"`
Flasher string `json:"flash"`
OCDDaemon []string `json:"ocd-daemon"`
@@ -72,9 +71,6 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
spec.CFlags = append(spec.CFlags, spec2.CFlags...)
spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...)
spec.ExtraFiles = append(spec.ExtraFiles, spec2.ExtraFiles...)
if spec2.Objcopy != "" {
spec.Objcopy = spec2.Objcopy
}
if len(spec2.Emulator) != 0 {
spec.Emulator = spec2.Emulator
}
@@ -162,6 +158,9 @@ func LoadTarget(target string) (*TargetSpec, error) {
llvmarch = goarch
}
target = llvmarch + "--" + llvmos
if goarch == "arm" {
target += "-gnueabihf"
}
return defaultTarget(goos, goarch, target)
}
@@ -189,6 +188,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 +213,25 @@ 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" {
spec.Linker = "arm-linux-gnueabi-gcc"
spec.Objcopy = "arm-linux-gnueabi-objcopy"
spec.GDB = "arm-linux-gnueabi-gdb"
if goarch == "arm" && goos == "linux" {
spec.Linker = "arm-linux-gnueabihf-gcc"
spec.GDB = "arm-linux-gnueabihf-gdb"
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
}
if goarch == "arm64" {
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"
-1
View File
@@ -4,7 +4,6 @@
"goarch": "wasm",
"compiler": "avr-gcc",
"linker": "avr-gcc",
"objcopy": "avr-objcopy",
"ldflags": [
"-T", "targets/avr.ld",
"-Wl,--gc-sections"
+5
View File
@@ -0,0 +1,5 @@
{
"inherits": ["atsamd21g18a"],
"build-tags": ["sam", "atsamd21g18a", "circuitplay_express"],
"flash": "uf2conv.py {bin}"
}
-1
View File
@@ -16,6 +16,5 @@
"ldflags": [
"--gc-sections"
],
"objcopy": "arm-none-eabi-objcopy",
"gdb": "arm-none-eabi-gdb"
}
+1 -1
View File
@@ -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}"
}
+1
View File
@@ -4,6 +4,7 @@
"build-tags": ["nrf52", "nrf"],
"cflags": [
"--target=armv7em-none-eabi",
"-mfloat-abi=soft",
"-Qunused-arguments",
"-DNRF52832_XXAA",
"-Ilib/CMSIS/CMSIS/Include"
+1
View File
@@ -4,6 +4,7 @@
"build-tags": ["nrf52840", "nrf"],
"cflags": [
"--target=armv7em-none-eabi",
"-mfloat-abi=soft",
"-Qunused-arguments",
"-DNRF52840_XXAA",
"-Ilib/CMSIS/CMSIS/Include"
+1 -1
View File
@@ -12,5 +12,5 @@
"ldflags": [
"-allow-undefined"
],
"emulator": ["cwa"]
"emulator": ["node", "targets/wasm_exec.js"]
}
+2 -43
View File
@@ -73,13 +73,6 @@
global.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
this.exit = (code) => {
if (code !== 0) {
console.warn("exit code:", code);
}
};
this._callbackTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
@@ -342,36 +335,6 @@
const mem = new DataView(this._inst.exports.memory.buffer)
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
let offset = 4096;
const strPtr = (str) => {
let ptr = offset;
new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0"));
offset += str.length + (8 - (str.length % 8));
return ptr;
};
const argc = this.argv.length;
const argvPtrs = [];
this.argv.forEach((arg) => {
argvPtrs.push(strPtr(arg));
});
const keys = Object.keys(this.env).sort();
argvPtrs.push(keys.length);
keys.forEach((key) => {
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
});
const argv = offset;
argvPtrs.forEach((ptr) => {
mem.setUint32(offset, ptr, true);
mem.setUint32(offset + 4, 0, true);
offset += 8;
});
while (true) {
const callbackPromise = new Promise((resolve) => {
this._resolveCallbackPromise = () => {
@@ -381,7 +344,7 @@
setTimeout(resolve, 0); // make sure it is asynchronous
};
});
this._inst.exports.cwa_main(argc, argv);
this._inst.exports.cwa_main();
if (this.exited) {
break;
}
@@ -413,21 +376,17 @@
}
if (isNodeJS) {
if (process.argv.length < 3) {
if (process.argv.length != 3) {
process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n");
process.exit(1);
}
const go = new Go();
go.argv = process.argv.slice(2);
go.env = process.env;
go.exit = process.exit;
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
process.on("exit", (code) => { // Node.js exits if no callback is pending
if (code === 0 && !go.exited) {
// deadlock, make Go print error and stack traces
go._callbackShutdown = true;
go._inst.exports.run();
}
});
return go.run(result.instance);
+17
View File
@@ -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 {
+13
View File
@@ -41,3 +41,16 @@ true
false
true
false
complex numbers
true
false
false
false
true
true
false
false
false
true
true
true
+17
View File
@@ -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;
}
+25
View File
@@ -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
}
+18
View File
@@ -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;
+11
View File
@@ -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)
+46
View File
@@ -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))
}
}
+216
View File
@@ -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
+292
View File
@@ -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)
}
}
+215
View File
@@ -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
+26 -11
View File
@@ -13,17 +13,17 @@ func main() {
println("sum foo:", sum(foo))
// creating a slice with uncommon len, cap types
assert(len(make([]int, int(2), int(3))) == 2)
assert(len(make([]int, int8(2), int8(3))) == 2)
assert(len(make([]int, int16(2), int16(3))) == 2)
assert(len(make([]int, int32(2), int32(3))) == 2)
assert(len(make([]int, int64(2), int64(3))) == 2)
assert(len(make([]int, uint(2), uint(3))) == 2)
assert(len(make([]int, uint8(2), uint8(3))) == 2)
assert(len(make([]int, uint16(2), uint16(3))) == 2)
assert(len(make([]int, uint32(2), uint32(3))) == 2)
assert(len(make([]int, uint64(2), uint64(3))) == 2)
assert(len(make([]int, uintptr(2), uintptr(3))) == 2)
assert(len(make([]int, makeInt(2), makeInt(3))) == 2)
assert(len(make([]int, makeInt8(2), makeInt8(3))) == 2)
assert(len(make([]int, makeInt16(2), makeInt16(3))) == 2)
assert(len(make([]int, makeInt32(2), makeInt32(3))) == 2)
assert(len(make([]int, makeInt64(2), makeInt64(3))) == 2)
assert(len(make([]int, makeUint(2), makeUint(3))) == 2)
assert(len(make([]int, makeUint8(2), makeUint8(3))) == 2)
assert(len(make([]int, makeUint16(2), makeUint16(3))) == 2)
assert(len(make([]int, makeUint32(2), makeUint32(3))) == 2)
assert(len(make([]int, makeUint64(2), makeUint64(3))) == 2)
assert(len(make([]int, makeUintptr(2), makeUintptr(3))) == 2)
// indexing into a slice with uncommon index types
assert(foo[int(2)] == 4)
@@ -120,3 +120,18 @@ func assert(ok bool) {
panic("assert failed")
}
}
// Helper functions used to hide const values from the compiler during IR
// construction.
func makeInt(x int) int { return x }
func makeInt8(x int8) int8 { return x }
func makeInt16(x int16) int16 { return x }
func makeInt32(x int32) int32 { return x }
func makeInt64(x int64) int64 { return x }
func makeUint(x uint) uint { return x }
func makeUint8(x uint8) uint8 { return x }
func makeUint16(x uint16) uint16 { return x }
func makeUint32(x uint32) uint32 { return x }
func makeUint64(x uint64) uint64 { return x }
func makeUintptr(x uintptr) uintptr { return x }
+12
View File
@@ -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())
}
+3
View File
@@ -0,0 +1,3 @@
stdin: 0
stdout: 1
stderr: 2
+130
View File
@@ -0,0 +1,130 @@
// Converts firmware files from BIN to UF2 format before flashing.
//
// For more information about the UF2 firmware file format, please see:
// https://github.com/Microsoft/uf2
//
//
package main
import (
"bytes"
"encoding/binary"
"io/ioutil"
)
// ConvertELFFileToUF2File converts an ELF file to a UF2 file.
func ConvertELFFileToUF2File(infile, outfile string) error {
// Read the .text segment.
_, data, err := ExtractTextSegment(infile)
if err != nil {
return err
}
output, _ := ConvertBinToUF2(data)
return ioutil.WriteFile(outfile, output, 0644)
}
// ConvertBinToUF2 converts the binary bytes in input to UF2 formatted data.
func ConvertBinToUF2(input []byte) ([]byte, int) {
blocks := split(input, 256)
output := make([]byte, 0)
bl := NewUF2Block()
bl.SetNumBlocks(len(blocks))
for i := 0; i < len(blocks); i++ {
bl.SetBlockNo(i)
bl.SetData(blocks[i])
output = append(output, bl.Bytes()...)
bl.IncrementAddress(bl.payloadSize)
}
return output, len(blocks)
}
const (
uf2MagicStart0 = 0x0A324655 // "UF2\n"
uf2MagicStart1 = 0x9E5D5157 // Randomly selected
uf2MagicEnd = 0x0AB16F30 // Ditto
uf2StartAddress = 0x2000
)
// UF2Block is the structure used for each UF2 code block sent to device.
type UF2Block struct {
magicStart0 uint32
magicStart1 uint32
flags uint32
targetAddr uint32
payloadSize uint32
blockNo uint32
numBlocks uint32
familyID uint32
data []uint8
magicEnd uint32
}
// NewUF2Block returns a new UF2Block struct that has been correctly populated
func NewUF2Block() *UF2Block {
return &UF2Block{magicStart0: uf2MagicStart0,
magicStart1: uf2MagicStart1,
magicEnd: uf2MagicEnd,
targetAddr: uf2StartAddress,
flags: 0x0,
familyID: 0x0,
payloadSize: 256,
data: make([]byte, 476),
}
}
// Bytes converts the UF2Block to a slice of bytes that can be written to file.
func (b *UF2Block) Bytes() []byte {
buf := bytes.NewBuffer(make([]byte, 0, 512))
binary.Write(buf, binary.LittleEndian, b.magicStart0)
binary.Write(buf, binary.LittleEndian, b.magicStart1)
binary.Write(buf, binary.LittleEndian, b.flags)
binary.Write(buf, binary.LittleEndian, b.targetAddr)
binary.Write(buf, binary.LittleEndian, b.payloadSize)
binary.Write(buf, binary.LittleEndian, b.blockNo)
binary.Write(buf, binary.LittleEndian, b.numBlocks)
binary.Write(buf, binary.LittleEndian, b.familyID)
binary.Write(buf, binary.LittleEndian, b.data)
binary.Write(buf, binary.LittleEndian, b.magicEnd)
return buf.Bytes()
}
// IncrementAddress moves the target address pointer forward by count bytes.
func (b *UF2Block) IncrementAddress(count uint32) {
b.targetAddr += b.payloadSize
}
// SetData sets the data to be used for the current block.
func (b *UF2Block) SetData(d []byte) {
b.data = make([]byte, 476)
copy(b.data[:], d)
}
// SetBlockNo sets the current block number to be used.
func (b *UF2Block) SetBlockNo(bn int) {
b.blockNo = uint32(bn)
}
// SetNumBlocks sets the total number of blocks for this UF2 file.
func (b *UF2Block) SetNumBlocks(total int) {
b.numBlocks = uint32(total)
}
// split splits a slice of bytes into a slice of byte slices of a specific size limit.
func split(input []byte, limit int) [][]byte {
var block []byte
output := make([][]byte, 0, len(input)/limit+1)
for len(input) >= limit {
block, input = input[:limit], input[limit:]
output = append(output, block)
}
if len(input) > 0 {
output = append(output, input[:len(input)])
}
return output
}
+5
View File
@@ -0,0 +1,5 @@
package main
// version of this package.
// Update this value before release of new version of software.
const version = "0.4.0"