Compare commits

..

83 Commits

Author SHA1 Message Date
sago35 46e84a09f8 go mod tidy 2021-12-07 08:44:22 +09:00
sago35 954acdbdf5 smoketest: run with go script 2021-12-07 08:43:28 +09:00
sago35 0285171484 main: improve help 2021-12-03 16:14:41 +01:00
Dan Kegel 1a22d92217 os: remove as-yet unused function splitPath 2021-12-03 09:38:40 +01:00
Dan Kegel 8416bb61d8 os: implement MkdirAll 2021-12-03 09:38:40 +01:00
Damian Gryski 85ad157f3f src/os: fix build tags for non-windows PathSeparator 2021-12-01 18:39:37 +01:00
Dan Kegel b123ffcea4 os: implement CreateTemp
Until tinygo implements fastrand(), use a placeholder RNG here.
2021-12-01 12:53:04 +01:00
Dan Kegel ec9fd3fb38 os, syscall: implement Stat and Lstat
File.Stat is left as a stub for now.

Tests are a bit stubbed down because os.ReadDir, os.Symlink, and t.TempDir are not yet (fully) implemented.
TODO: reimport tests from upstream as those materialize.
2021-12-01 00:23:23 +01:00
Ayke van Laethem 5127b9d65b all: add LLVM 12 support
Originally based on a PR by @QuLogic, but extended a lot to get all
tests to pass.
2021-11-30 21:53:16 +01:00
Ayke van Laethem 74b20ca234 runtime: use LLVM intrinsic to read the stack pointer
This should result in smaller code.
2021-11-30 10:01:44 +01:00
Ayke van Laethem 3d73ee77d3 machine: add Device constant
This field contains the microcontroller name that we're compiling for,
or "generic" if we're not running on a microcontroller.
2021-11-30 00:47:11 +01:00
Dan Kegel 6b0f2cd697 os: TempDir(): obey TMPDIR on unix, TMP on win, etc
Uses upstream go code, lightly adjusted.

Adds smoke test.
2021-11-30 00:10:09 +01:00
Dan Kegel e354a3523b os.Remove: avoid double-wrapping err; fixes TODO in test 2021-11-29 20:49:28 +01:00
Dan Kegel f79f6b0e62 os, syscall: implement ReadAt for unix
Windows will take more work, so test is skipped there.
2021-11-29 20:13:29 +01:00
Damian Gryski 47db50b273 os: add a stub for File.Truncate 2021-11-26 18:53:27 +01:00
Olivier Fauchon b0fce80b50 Improvements for stm32wle targets :
- board/stm32: Add STM32 Nucleo WL55JC board
- stm32/stm32wl: Set 48Mhz HSE32/PLL as default Clock, SPI implementation
2021-11-26 14:15:11 +01:00
Ayke van Laethem 718746dd21 os: stub out support for some more features
This is necessary for the following:

  - to make sure os/exec can be imported
  - to make sure internal/testenv can be imported

The internal/testenv package (which imports os/exec) is used by a lot of
tests. By adding support for it, more tests can be run.

This commit adds a bunch of new packages that now pass all tests.
2021-11-26 08:05:35 +01:00
Kenneth Bell 6cbaed75c8 stm32: fix timeout for i2c comms
ticks changed to 16ns causing timeouts to be very, very short.  This change updates timeouts for 16ns ticks.
2021-11-26 00:38:00 +01:00
Kenneth Bell a6200920f7 stm32: pull-up on I2C lines
fixes #2310
2021-11-24 23:57:46 +01:00
Ayke van Laethem 79467baf12 all: remove FreeBSD support
FreeBSD support has been broken for a long time, probably since
https://github.com/tinygo-org/tinygo/pull/1860 (merged in May). Nobody
has complained yet, so I am going to assume nobody uses it.

This doesn't remove support for FreeBSD entirely: the code necessary to
build TinyGo on FreeBSD is still there. It just removes the code
necessary to build binaries targetting FreeBSD. But again, it could very
well be broken as we don't test it.

If anybody wants to re-enable support for FreeBSD, they would be welcome
to do that. But I think it would at the very least need a smoke test of
some sort.
2021-11-24 22:21:22 +01:00
Ayke van Laethem c31aef06ba cgo: add support for C.CString and related functions 2021-11-24 21:09:29 +01:00
Ayke van Laethem 6bd18af5ef cgo: simplify construction of in-memory AST
Instead of writing the entire AST by hand, write it in Go code and parse
it to create the base AST to build upon.
2021-11-24 21:09:29 +01:00
Ayke van Laethem 1789570f52 cgo: add //go: pragmas to generated functions and globals
This patch adds //go: pragmas directly to declared functions and
globals found during CGo processing. This simplifies the logic in the
compiler: it no longer has to consider special "C." prefixed function
names. It also makes the cgo pass more flexible in the pragmas it emits
for functions and global variables.
2021-11-24 21:09:29 +01:00
Damian Gryski a536ddcda8 tinygo: support -run for tests
Fixes #2294
2021-11-24 17:27:11 +01:00
Damian Gryski 18242bc26a runtime: allow comparing interfaces in reflectValueEqual() 2021-11-24 14:17:47 +01:00
Ayke van Laethem 18f4ffd656 ci: move Linux release builds to GitHub Actions 2021-11-24 13:00:15 +01:00
Ayke van Laethem 7238c0a16f ci: cache Go cache for faster build times 2021-11-22 19:43:03 +01:00
Ayke van Laethem c177e4dbc0 ci: move assert-test-linux to GitHub Actions 2021-11-21 12:17:34 +01:00
Ayke van Laethem 1d2c17753a tests: improve wasm tests slightly
These wasm tests weren't passing in GitHub Actions and also weren't
passing on my laptop. I'm not sure why, I think there are a few race
conditions that are going on.

This commit attempts to fix this at least to a degree:

  - The context deadline is increased from 5 seconds to 10 seconds.
  - The tests are not running in parallel anymore.
  - Some `Sleep` calls were removed, they do not appear to be necessary
    (and if they were, sleeping is the wrong solution to solve race
    conditions).

Overall the tests are taking a few seconds more, but on the other hand
they seem to be passing more reliable. At least for me, on my laptop
(and hopefully also in CI).
2021-11-21 12:17:34 +01:00
Kenneth Bell 470cbd5f53 housekeeping: wasm optional in smoketest and fix make clean 2021-11-20 13:30:50 -05:00
Kenneth Bell 62d4a6a77f stm32: add minimal stm32wlex5 / lorae5 target
credit to ofauchon
2021-11-20 12:07:11 +01:00
Dan Kegel 339301b709 os: Implement and smoke test Mkdir() and Remove() 2021-11-20 10:39:27 +01:00
Dan Kegel aea4f57a2b syscall: hoist cstring() out of two functions to reduce repetition and allocations 2021-11-20 10:39:27 +01:00
sago35 2a17a3e56c board: add M5Stack 2021-11-20 09:37:24 +01:00
Ayke van Laethem 34011c4800 targets: change LLVM features to match vanilla Clang
I mistakenly believed the difference was in LLVM version 11.0.0 vs LLVM
11.1.0. However, the difference is in whether we use the Debian version
of Clang.

The Debian version has had lots of patches. I'm not sure which is to
blame, but it could be this one:
https://salsa.debian.org/pkg-llvm-team/llvm-toolchain/-/blob/snapshot/debian/patches/clang-arm-default-vfp3-on-armv7a.patch
2021-11-20 02:48:23 +01:00
Damian Gryski f8206b9bcc syscall: fix array size for mmap slice creation 2021-11-20 00:43:24 +01:00
Ayke van Laethem b01ce95cb5 ci: run stdlib tests on macos 2021-11-20 00:43:24 +01:00
Ayke van Laethem 0658e4896f syscall: add support for Mmap and Mprotect
This is necessary to run crypto/sha1 tests.
2021-11-20 00:43:24 +01:00
Damian Gryski 9d87d658ba testing: add a stub for CoverMode
This allows the strings package test to compile.
2021-11-20 00:36:42 +01:00
Ayke van Laethem ec95d3560f ci: fix Binaryen cache on Windows
The wrong path was used to cache binaryen, so it wasn't actually getting
cached. Therefore, wasm-opt was rebuilt on every new PR (slowing down
the "Build TinyGo release tarball" a lot).
2021-11-19 14:46:20 -05:00
Ayke van Laethem bf076aed61 ci: only build wasm-opt, don't build other Binaryen tools 2021-11-19 14:46:20 -05:00
Ayke van Laethem dee5602e56 builder: fix off-by-one in size calculation
> There are two hard things in computer science: cache invalidation,
> naming things, and off-by-one errors.

Because of this bug, sometimes the last object in a section might not be
attributed correctly to a source location.
2021-11-19 12:14:32 +01:00
Ayke van Laethem 4ef340f228 windows: add support for the -size= flag
Like WebAssembly, it misses information on strings and other anonymous
symbols. However, most code (.text) is covered.
2021-11-19 00:21:21 +01:00
Damian Gryski 843a7ac445 add asyncify to scheduler flag help 2021-11-18 18:09:48 -05:00
deadprogram 8ed7d197a7 docs: update CONTRIBUTING links to point to web site. Also replace Azure build badge with Windows build on GH Actions.
Signed-off-by: deadprogram <ron@hybridgroup.com>
2021-11-18 17:49:13 -05:00
Nia Waldvogel 39ce7111bd goenv: update version for start of 0.22.0 development cycle 2021-11-18 23:19:48 +01:00
Ayke van Laethem 06df31944c all: release v0.21.0 2021-11-18 14:20:59 +01:00
Damian Gryski 3ba8bc92cd testdata: update binop.go for string comparison tests 2021-11-18 11:07:45 +01:00
Damian Gryski 0e7a129de7 compiler: update testdata/string.ll
$ go test ./compiler -update
2021-11-18 11:07:45 +01:00
Damian Gryski aeddcd9c5f compiler: fix string compare functions
Before:
	x < x false
	x <= x true
	x == x true
	x >= x false
	x > x true

After:
	x < x false
	x <= x true
	x == x true
	x >= x true
	x > x false
2021-11-18 11:07:45 +01:00
Patricio Whittingslow bf0b05e32c machine/rp2040: refactor PWM code. fix Period calculation 2021-11-18 10:22:26 +01:00
Damian Gryski 348a02d697 src/runtime/debug: stub debug.Stack() 2021-11-17 19:25:52 +01:00
Damian Gryski 7273a2b5fd src/testing stub AllocsPerRun 2021-11-17 19:25:52 +01:00
Damian Gryski 44bb381220 src/runtime/debug: add SetMaxStack to allow compress/flate tests to build 2021-11-17 19:25:52 +01:00
Damian Gryski 2e6b92fc56 src/testing: add testing.Verbose() 2021-11-17 19:25:52 +01:00
Ayke van Laethem 51290e5842 wasm: support -scheduler=none
Previously, -scheduler=none wasn't possible for WASM targets:

    $ tinygo run -target=wasm -scheduler=none ./testdata/stdlib.go
    src/runtime/runtime_wasm_js.go:34:2: attempted to start a goroutine without a scheduler

With this commit, it works just fine:

    $ tinygo run -target=wasm -scheduler=none ./testdata/stdlib.go
    stdin:  /dev/stdin
    stdout: /dev/stdout
    stderr: /dev/stderr
    pseudorandom number: 1298498081
    strings.IndexByte: 2
    strings.Replace: An-example-string

Supporting `-scheduler=none` has some benefits:

  * it reduces file size a lot compared to having a scheduler
  * it allows JavaScript to call exported functions
2021-11-17 19:03:20 +01:00
Ayke van Laethem 22c35c8e31 ci: move all cross compilation tests to Linux
The idea here is as follows:
  - Run all Linux and cross compilation tests in the asser-test-linux
    job.
  - Only run native tests on MacOS and Windows.

This reduces testing time on MacOS and Windows, which are generally more
expensive in CI. Also, by not duplicating tests in Windows and MacOS we
can reduce overall CI usage a bit.

I've also changed the assert-test-linux job a bit to so that the tests
that are more likely to break and the tests that are only run in
assert-test-linux are run first.
2021-11-17 14:52:32 +01:00
Ayke van Laethem 2081380b5c ci: simplify build-linux job
Split building the release and smoke-testing the release in two, and
don't redo some tests that are already done by assert-test-linux.

Some benefits:
  - Lower overall CI time because tests aren't done multiple times.
  - TinyHCI can run earlier because the build-linux job is finished as
    soon as the build artifact is ready.

It does however have the downside of an extra job, which costs a few
seconds to spin up and a few seconds to push and pull the workspace. But
even with this, overall CI time is down by a few minutes per workflow
run.
2021-11-16 18:43:32 +01:00
Ayke van Laethem 3d0c6dd02d ci: simplify test-linux jobs
Instead of doing lots of repetitive tests in test-llvm11-go115 and
test-llvm11-go116, do those tests only once in assert-test-linux and
only run smoke tests for older Go versions.

Benefits:
  - This should reduce total CI time, because these jobs don't do tests
    that are done elsewere anyway. They only do the minimal work
    necessary to prove that the given Go/LLVM version works.
  - Doing all tests in assert-test-linux hopefully catches bugs that
    might not be found in regular LLVM builds.
2021-11-16 16:23:59 +01:00
Ayke van Laethem 869e917dc6 all: add support for windows/amd64
This uses Mingw-w64, which seems to be the de facto standard for porting
Unixy programs to Windows.
2021-11-16 11:08:30 +01:00
Ayke van Laethem 41bcad9c19 runtime: only use CRLF on baremetal systems
We should only do this on baremetal systems, not on Linux, macOS, and
Windows. In fact, Windows will convert LF into CRLF in its putchar
function.
2021-11-16 11:08:30 +01:00
Ayke van Laethem 73ab44c178 builder: support -size= flag on the esp32
This fixes a small mistake when calculating binary size for an Xtensa
file. Previously it would exit with the following error:

    $ tinygo build -o test.elf -size=short -target=esp32-mini32 examples/serial
    panic: runtime error: index out of range [65521] with length 18

Now it runs as expected:

    $ tinygo build -o test.elf -size=short -target=esp32-mini32 examples/serial
       code    data     bss |   flash     ram
       2897       0    4136 |    2897    4136
2021-11-16 07:42:58 +01:00
Dan Kegel b70b6076e3 net/ip, syscall/errno: Reduce code duplication by switching to internal/itoa.
internal/itoa wasn't around back in go 1.12 days when tinygo's syscall/errno.go was written.
It was only added as of go 1.17 ( https://github.com/golang/go/commit/061a6903a232cb868780b )
so we have to have an internal copy for now.
The internal copy should be deleted when tinygo drops support for go 1.16.

FWIW, the new version seems nicer.
It uses no allocations when converting 0,
and although the optimizer might make this moot, uses
a multiplication x 10 instead of a mod operation.
2021-11-16 02:13:52 +01:00
sago35 7b41d92198 device: add build tag for go1.17 2021-11-16 02:10:03 +01:00
Ayke van Laethem 2f4f3bd1ba wasi: restore support for -scheduler=none
The assembly symbols were not marked as hidden and so were exported,
leading to unreferenced symbols.

Example error message:

    Error: failed to run main module `/tmp/tinygo3961039405/main`

    Caused by:
        0: failed to instantiate "/tmp/tinygo3961039405/main"
        1: unknown import: `asyncify::stop_rewind` has not been defined

This commit fixes this issue.
2021-11-15 22:17:40 +01:00
Ayke van Laethem 7cb44fb373 all: add support for GOARM
This environment variable can be set to 5, 6, or 7 and controls which
ARM version (ARMv5, ARMv6, ARMv7) is used when compiling for GOARCH=arm.

I have picked the default value ARMv6, which I believe is supported on
most common single board computers including all Raspberry Pis. The
difference in code size is pretty big.

We could even go further and support ARMv4 if anybody is interested. It
should be pretty simple to add this if needed.
2021-11-15 11:53:44 +01:00
Ayke van Laethem 73ad825b67 ci: update Windows runner to windows-2019
The windows-2016 runner will soon be removed:
https://github.blog/changelog/2021-10-19-github-actions-the-windows-2016-runner-image-will-be-removed-from-github-hosted-runners-on-march-15-2022/
Therefore, switch to the next version.
2021-11-14 13:18:54 +01:00
Nia Waldvogel 539e1324c8 fix binaryen build in docker
Oops I forgot to install cmake and ninja.
2021-11-14 10:49:28 +01:00
Nia Waldvogel 641dcd7c16 internal/task: use asyncify on webassembly
This change implements a new "scheduler" for WebAssembly using binaryen's asyncify transform.
This is more reliable than the current "coroutines" transform, and works with non-Go code in the call stack.

runtime (js/wasm): handle scheduler nesting

If WASM calls into JS which calls back into WASM, it is possible for the scheduler to nest.
The event from the callback must be handled immediately, so the task cannot simply be deferred to the outer scheduler.
This creates a minimal scheduler loop which is used to handle such nesting.
2021-11-14 10:49:28 +01:00
Yurii Soldak 523d1f28cf nano-33-ble: internal i2c config, power led and sensors switch 2021-11-13 12:46:54 +01:00
Damian Gryski 782c57cc11 pass testing arguments to wasmtime 2021-11-13 12:01:20 +01:00
Ayke van Laethem c1d697f868 compiler: fix indices into strings and arrays
This PR fixes two bugs at once:

 1. Indices were incorrectly extended to a bigger type. Specifically,
    unsigned integers were sign extended and signed integers were zero
    extended. This commit swaps them around.
 2. The getelementptr instruction was given the raw index, even if it
    was a uint8 for example. However, getelementptr assumes the indices
    are signed, and therefore an index of uint8(200) was interpreted as
    an index of int8(-56).
2021-11-13 11:04:24 +01:00
Ayke van Laethem 335fb71d2f reflect: add support for DeepEqual
The implementation has been mostly copied from the Go reference
implementation with some small changes to fit TinyGo.

Source: https://github.com/golang/go/blob/77a11c05d6a6f766c75f804ea9b8796f9a9f85a3/src/reflect/deepequal.go

In addition, this commit also contains the following:

  - A set of tests copied from the Go reflect package.
  - An increased stack size for the riscv-qemu and hifive1-qemu targets
    (because they otherwise fail to run the tests). Because these
    targets are only used for testing, this seems fine to me.
2021-11-12 21:27:27 +01:00
Ayke van Laethem 5866a47e77 reflect: fix Value.Index() in a specific case
In the case where:

 - Value.Index() was called on an array
 - that array was bigger than a pointer
 - the element type fits in a pointer
 - the 'indirect' flag isn't set

the Value.Index() method would still (incorrectly) load the value.
This commit fixes that.

The next commit adds a test which would have triggered this bug so works
as a regression test.
2021-11-12 21:27:27 +01:00
Ayke van Laethem 823c9c25cf reflect: implement Value.Elem() for interface values 2021-11-12 21:27:27 +01:00
Ayke van Laethem d15e32fb89 reflect: don't construct an interface-in-interface value
v.Interaface() could construct an interface in interface value if v was
of type interface. This is not correct, and doesn't follow upstream Go
behavior. Instead, it should return the interface value itself.
2021-11-12 21:27:27 +01:00
soypat b534dd67e0 machine/rp2040: add interrupt API 2021-11-12 10:38:02 +01:00
Ayke van Laethem 6c02b4956c interp: fix reverting of extractvalue/insertvalue with multiple indices 2021-11-11 10:36:22 +01:00
Ayke van Laethem 1681ed02d3 interp: take care of constant globals
Constant globals can't have been modified, even if a pointer is passed
externally. Therefore, don't treat it as such in hasExternalStore.

In addition, it doesn't make sense to update values of constant globals
after the interp pass is finished. So don't do this.

TODO: track whether objects are actually modified and only update the
globals if this is the case.
2021-11-11 08:59:32 +01:00
Ayke van Laethem 7e68980c39 ci: improve caching for GitHub Actions
Previously the cache would be stale for every new branch.
With this change, PRs use the cache from the base branch and therefore
don't need to rebuild LLVM from scratch.
2021-11-10 18:55:17 +01:00
Ayke van Laethem cf640290a3 compiler: add "target-cpu" and "target-features" attributes
This matches Clang, and with that, it adds support for inlining between
Go and C because LLVM only allows inlining if the "target-cpu" and
"target-features" string attributes match.

For example, take a look at the following code:

    // int add(int a, int b) {
    //   return a + b;
    // }
    import "C"

    func main() {
        println(C.add(3, 5))
    }

The 'add' function is not inlined into the main function before this
commit, but after it, it can be inlined and trivially be optimized to
`println(8)`.
2021-11-10 11:16:13 +01:00
Ayke van Laethem 78fec3719f all: add target-features string to all targets
This makes sure that the LLVM target features match the one generated by
Clang:

  - This fixes a bug introduced when setting the target CPU for all
    targets: Cortex-M4 would now start using floating point operations
    while they were disabled in C.
  - This will make it possible in the future to inline C functions in Go
    and vice versa. This will need some more work though.

There is a code size impact. Cortex-M4 targets are increased slightly in
binary size while Cortex-M0 targets tend to be reduced a little bit.
Other than that, there is little impact.
2021-11-07 09:26:46 +01:00
Ayke van Laethem af4d0fe191 compileopts: fix reversed append in the target file
With this fix, `cflags` in the target JSON files is correctly ordered.
Previously, the cflags of a parent JSON file would come after the ones
in the child JSON file, which makes it hard to override properties in
the child JSON file.

Specifically, this fixes the case where targets/riscv32.json sets
`-march=rv32imac` and targets/esp32c3.json wants to override this using
`-march=rv32imc` but can't do this because its `-march` comes before the
riscv32.json one.
2021-11-07 09:26:46 +01:00
253 changed files with 7484 additions and 7477 deletions
+58 -231
View File
@@ -6,49 +6,6 @@ commands:
- run:
name: "Pull submodules"
command: git submodule update --init
apt-dependencies:
parameters:
llvm:
type: string
steps:
- run:
name: "Install apt dependencies"
command: |
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<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 --no-install-recommends \
llvm-<<parameters.llvm>>-dev \
clang-<<parameters.llvm>> \
libclang-<<parameters.llvm>>-dev \
lld-<<parameters.llvm>> \
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
install-chrome:
steps:
- run:
name: "Install Chrome"
command: |
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
install-wasmtime:
steps:
- run:
name: "Install wasmtime"
command: |
curl https://wasmtime.dev/install.sh -sSf | bash
sudo ln -s ~/.wasmtime/bin/wasmtime /usr/local/bin/wasmtime
install-xtensa-toolchain:
parameters:
variant:
@@ -65,29 +22,37 @@ commands:
steps:
- restore_cache:
keys:
- llvm-source-11-v2
- llvm-source-12-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-11-v2
key: llvm-source-12-v1
paths:
- llvm-project/clang/lib/Headers
- llvm-project/clang/include
- llvm-project/lld/include
- llvm-project/llvm/include
build-wasi-libc:
hack-ninja-jobs:
steps:
- run:
name: "Hack Ninja to use less jobs"
command: |
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
build-binaryen-linux:
steps:
- restore_cache:
keys:
- wasi-libc-sysroot-v4
- binaryen-linux-v1
- run:
name: "Build wasi-libc"
command: make wasi-libc
name: "Build Binaryen"
command: |
make binaryen
- save_cache:
key: wasi-libc-sysroot-v4
key: binaryen-linux-v1
paths:
- lib/wasi-libc/sysroot
- build/wasm-opt
test-linux:
parameters:
llvm:
@@ -95,11 +60,23 @@ commands:
steps:
- checkout
- submodules
- apt-dependencies:
llvm: "<<parameters.llvm>>"
- install-node
- install-chrome
- install-wasmtime
- run:
name: "Install apt dependencies"
command: |
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<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 --no-install-recommends \
llvm-<<parameters.llvm>>-dev \
clang-<<parameters.llvm>> \
libclang-<<parameters.llvm>>-dev \
lld-<<parameters.llvm>> \
gcc-avr \
avr-libc \
cmake \
ninja-build
- hack-ninja-jobs
- build-binaryen-linux
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
@@ -114,165 +91,14 @@ commands:
key: wasi-libc-sysroot-systemclang-v3
paths:
- lib/wasi-libc/sysroot
- run:
name: "Test TinyGo"
command: go test -v -timeout=20m -tags=llvm<<parameters.llvm>> ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
no_output_timeout: 20m
- run: make gen-device -j4
- run: make smoketest XTENSA=0
- run: make tinygo-test
- run: make wasmtest
- save_cache:
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
- run: make fmt-check
assert-test-linux:
steps:
- checkout
- submodules
- run:
name: "Install apt dependencies"
command: |
sudo apt-get update
sudo apt-get install --no-install-recommends \
libgnutls30 libssl1.0.2 \
qemu-system-arm \
qemu-user \
gcc-avr \
avr-libc
- install-node
- install-wasmtime
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v2-{{ checksum "go.mod" }}
- llvm-source-linux
- restore_cache:
keys:
- llvm-build-11-linux-v4-assert
- run:
name: "Build LLVM"
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# install dependencies
sudo apt-get install cmake ninja-build
# hack ninja to use less jobs
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
# build!
make ASSERT=1 llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi
- save_cache:
key: llvm-build-11-linux-v4-assert
paths:
llvm-build
- run: make ASSERT=1
- build-wasi-libc
- run:
name: "Test TinyGo"
command: make ASSERT=1 test
no_output_timeout: 20m
environment:
# Note: -p=2 limits parallelism to two jobs at a time, which is
# necessary to keep memory consumption down and avoid OOM (for a
# 2CPU/4GB executor).
GOFLAGS: -p=2
- save_cache:
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
- run: make gen-device -j4
- run: make smoketest TINYGO=build/tinygo
build-linux:
steps:
- checkout
- submodules
- run:
name: "Install apt dependencies"
command: |
sudo apt-get update
sudo apt-get install --no-install-recommends \
libgnutls30 libssl1.0.2 \
qemu-system-arm \
qemu-user \
gcc-avr \
avr-libc
- install-node
- install-wasmtime
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v2-{{ checksum "go.mod" }}
- llvm-source-linux
- restore_cache:
keys:
- llvm-build-11-linux-v4-noassert
- run:
name: "Build LLVM"
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# install dependencies
sudo apt-get install cmake ninja-build
# hack ninja to use less jobs
echo -e '#!/bin/sh\n/usr/bin/ninja -j3 "$@"' > /go/bin/ninja
chmod +x /go/bin/ninja
# build!
make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi
- save_cache:
key: llvm-build-11-linux-v4-noassert
paths:
llvm-build
- build-wasi-libc
- run:
name: "Test TinyGo"
command: make test
no_output_timeout: 20m
- run:
name: "Install fpm"
command: |
sudo apt-get install ruby ruby-dev
sudo gem install --no-document fpm
- run:
name: "Build TinyGo release"
command: |
make release deb -j3
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_amd64.deb
- store_artifacts:
path: /tmp/tinygo.linux-amd64.tar.gz
- store_artifacts:
path: /tmp/tinygo_amd64.deb
- save_cache:
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
- run:
name: "Extract release tarball"
command: |
mkdir -p ~/lib
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
tinygo version
- run: make smoketest
build-macos:
steps:
- checkout
@@ -283,7 +109,7 @@ commands:
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
- install-xtensa-toolchain:
variant: "macos"
- restore_cache:
@@ -292,12 +118,12 @@ commands:
- go-cache-macos-v3-{{ checksum "go.mod" }}
- restore_cache:
keys:
- llvm-source-11-macos-v3
- llvm-source-12-macos-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-11-macos-v3
key: llvm-source-12-macos-v1
paths:
- llvm-project/clang/lib/Headers
- llvm-project/clang/include
@@ -305,25 +131,37 @@ commands:
- llvm-project/llvm/include
- restore_cache:
keys:
- llvm-build-11-macos-v5
- llvm-build-12-macos-v1
- run:
name: "Build LLVM"
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# fetch LLVM source
# fetch LLVM source (may only have headers right now)
rm -rf llvm-project
make llvm-source
# install dependencies
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
# build!
make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi
- save_cache:
key: llvm-build-11-macos-v5
key: llvm-build-12-macos-v1
paths:
llvm-build
- restore_cache:
keys:
- binaryen-macos-v1
- run:
name: "Build Binaryen"
command: |
if [ ! -f build/wasm-opt ]
then
make binaryen
fi
- save_cache:
key: binaryen-macos-v1
paths:
- build/wasm-opt
- restore_cache:
keys:
- wasi-libc-sysroot-macos-v4
@@ -336,7 +174,7 @@ commands:
- lib/wasi-libc/sysroot
- run:
name: "Test TinyGo"
command: make test
command: make test GOTESTFLAGS="-v -short"
no_output_timeout: 20m
- run:
name: "Build TinyGo release"
@@ -352,6 +190,7 @@ commands:
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
tinygo version
- run: make tinygo-test
- run: make smoketest AVR=0
- save_cache:
key: go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
@@ -366,22 +205,12 @@ jobs:
steps:
- test-linux:
llvm: "11"
test-llvm11-go116:
test-llvm12-go116:
docker:
- image: circleci/golang:1.16-buster
steps:
- test-linux:
llvm: "11"
assert-test-linux:
docker:
- image: circleci/golang:1.17-stretch
steps:
- assert-test-linux
build-linux:
docker:
- image: circleci/golang:1.17-stretch
steps:
- build-linux
llvm: "12"
build-macos:
macos:
xcode: "11.1.0" # macOS 10.14
@@ -394,7 +223,5 @@ workflows:
test-all:
jobs:
- test-llvm11-go115
- test-llvm11-go116
- build-linux
- test-llvm12-go116
- build-macos
- assert-test-linux
+232
View File
@@ -0,0 +1,232 @@
name: Linux
on:
pull_request:
push:
branches:
- dev
- release
jobs:
build-linux:
# Build Linux binaries, ready for release.
# This intentionally uses an older Linux image, so that we compile against
# an older glibc version and therefore are compatible with a wide range of
# Linux distributions.
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
ninja-build
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.17'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-v1-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-12-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-12-linux-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
run: |
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# build!
make llvm-build
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: make binaryen
- name: Cache wasi-libc
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-asserts-v2
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Install fpm
run: |
sudo apt-get install ruby ruby-dev
sudo gem install --no-document fpm
- name: Build TinyGo release
run: |
make release deb -j3
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_amd64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v2
with:
name: release-double-zipped
path: |
/tmp/tinygo.linux-amd64.tar.gz
/tmp/tinygo_amd64.deb
test-linux-build:
# Test the binaries built in the build-linux job by running the smoke tests.
runs-on: ubuntu-latest
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.17'
- name: Download release artifact
uses: actions/download-artifact@v2
with:
name: release-double-zipped
- name: Extract release tarball
run: |
mkdir -p ~/lib
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
gcc-avr \
avr-libc
- name: "Install Xtensa toolchain"
run: |
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
- run: make smoketest
assert-test-linux:
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
# potential bugs.
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
qemu-system-arm \
qemu-system-riscv32 \
qemu-user \
gcc-avr \
avr-libc \
ninja-build
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.17'
- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: '12'
- name: Install wasmtime
run: |
curl https://wasmtime.dev/install.sh -sSf | bash
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-asserts-v1-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-12-linux-asserts-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-12-linux-asserts-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
run: |
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# build!
make llvm-build ASSERT=1
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-asserts-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: make binaryen
- name: Cache wasi-libc
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-asserts-v2
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Test TinyGo
run: make ASSERT=1 test
- name: Build TinyGo
run: |
make ASSERT=1
echo "$(pwd)/build" >> $GITHUB_PATH
- name: Test stdlib packages
run: make tinygo-test
- name: Install Xtensa toolchain
run: |
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-linux-amd64.tar.gz
- run: make gen-device -j4
- run: make smoketest
- run: make wasmtest
+30 -10
View File
@@ -1,29 +1,40 @@
name: Windows
on: push
on:
pull_request:
push:
branches:
- dev
- release
jobs:
build-windows:
runs-on: windows-2016
runs-on: windows-2019
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.17'
- name: Install QEMU
- name: Install Ninja
shell: bash
run: |
choco install qemu --version=2020.06.12
echo "C:\Program Files\QEMU" >> $GITHUB_PATH
choco install ninja
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-windows-v1-${{ hashFiles('go.mod') }}
path: |
~/AppData/Local/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-11-windows-v1
key: llvm-source-12-windows-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -36,7 +47,7 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-11-windows-v2
key: llvm-build-12-windows-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -45,8 +56,6 @@ jobs:
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# install dependencies
choco install ninja
# build!
make llvm-build
# Remove unnecessary object files (to reduce cache size).
@@ -60,9 +69,18 @@ jobs:
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Cache Binaryen
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-windows-v2
path: build/wasm-opt.exe
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: make binaryen
- name: Test TinyGo
shell: bash
run: make test
run: make test GOTESTFLAGS="-v -short"
- name: Build TinyGo release tarball
run: make build/release -j4
- name: Make release artifact
@@ -83,3 +101,5 @@ jobs:
- name: Smoke tests
shell: bash
run: make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
- name: Test stdlib packages
run: make tinygo-test
+4 -1
View File
@@ -1,4 +1,3 @@
build
docs/_build
src/device/avr/*.go
src/device/avr/*.ld
@@ -21,8 +20,12 @@ src/device/rp/*.s
vendor
llvm-build
llvm-project
build/*
# Ignore files generated by smoketest
test.bin
test.elf
test.exe
test.gba
test.hex
test.nro
+6
View File
@@ -26,3 +26,9 @@
[submodule "lib/musl"]
path = lib/musl
url = git://git.musl-libc.org/musl
[submodule "lib/binaryen"]
path = lib/binaryen
url = https://github.com/WebAssembly/binaryen.git
[submodule "lib/mingw-w64"]
path = lib/mingw-w64
url = https://github.com/mingw-w64/mingw-w64.git
+94
View File
@@ -1,3 +1,97 @@
0.21.0
---
* **command line**
- drop support for LLVM 10
- `build`: drop support for LLVM targets in the -target flag
- `build`: fix paths in error messages on Windows
- `build`: add -p flag to set parallelism
- `lldb`: implement `tinygo lldb` subcommand
- `test`: use emulator exit code instead of parsing test output
- `test`: pass testing arguments to wasmtime
* **compiler**
- use -opt flag for optimization level in CFlags (-Os, etc)
- `builder`: improve accuracy of the -size=full flag
- `builder`: hardcode some more frame sizes for __aeabi_* functions
- `builder`: add support for -size= flag for WebAssembly
- `cgo`: fix line/column reporting in syntax error messages
- `cgo`: support function definitions in CGo headers
- `cgo`: implement rudimentary C array decaying
- `cgo`: add support for stdio in picolibc and wasi-libc
- `cgo`: run CGo parser per file, not per CGo fragment
- `compiler`: fix unintentionally exported math functions
- `compiler`: properly implement div and rem operations
- `compiler`: add support for recursive function types
- `compiler`: add support for the `go` keyword on interface methods
- `compiler`: add minsize attribute for -Oz
- `compiler`: add "target-cpu" and "target-features" attributes
- `compiler`: fix indices into strings and arrays
- `compiler`: fix string compare functions
- `interp`: simplify some code to avoid some errors
- `interp`: support recursive globals (like linked lists) in globals
- `interp`: support constant globals
- `interp`: fix reverting of extractvalue/insertvalue with multiple indices
- `transform`: work around renamed return type after merging LLVM modules
* **standard library**
- `internal/bytealg`: fix indexing error in Compare()
- `machine`: support Pin.Get() function when the pin is configured as output
- `net`, `syscall`: Reduce code duplication by switching to internal/itoa.
- `os`: don't try to read executable path on baremetal
- `os`: implement Getwd
- `os`: add File.WriteString and File.WriteAt
- `reflect`: fix type.Size() to account for struct padding
- `reflect`: don't construct an interface-in-interface value
- `reflect`: implement Value.Elem() for interface values
- `reflect`: fix Value.Index() in a specific case
- `reflect`: add support for DeepEqual
- `runtime`: add another set of invalid unicode runes to encodeUTF8()
- `runtime`: only initialize os.runtime_args when needed
- `runtime`: only use CRLF on baremetal systems for println
- `runtime/debug`: stub `debug.SetMaxStack`
- `runtime/debug`: stub `debug.Stack`
- `testing`: add a stub for t.Parallel()
- `testing`: add support for -test.short flag
- `testing`: stub B.ReportAllocs()
- `testing`: add `testing.Verbose`
- `testing`: stub `testing.AllocsPerRun`
* **targets**
- fix gen-device-svd to handle 64-bit values
- add CPU and Features property to all targets
- match LLVM triple to the one Clang uses
- `atsam`: simplify definition of SERCOM UART, I2C and SPI peripherals
- `atsam`: move I2S0 to machine file
- `esp32`: fix SPI configuration
- `esp32c3`: add support for GDB debugging
- `esp32c3`: add support for CPU interrupts
- `esp32c3`: use tasks scheduler by default
- `fe310`: increase CPU frequency from 16MHz to 320MHz
- `fe310`: add support for bit banging drivers
- `linux`: build static binaries using musl
- `linux`: reduce binary size by calling `write` instead of `putchar`
- `linux`: add support for GOARM
- `riscv`: implement 32-bit atomic operations
- `riscv`: align the heap to 16 bytes
- `riscv`: switch to tasks-based scheduler
- `rp2040`: add CPUFrequency()
- `rp2040`: improve I2C baud rate configuration
- `rp2040`: add pin interrupt API
- `rp2040`: refactor PWM code and fix Period calculation
- `stm32f103`: fix SPI
- `stm32f103`: make SPI frequency selection more flexible
- `qemu`: signal correct exit code to QEMU
- `wasi`: run C/C++ constructors at startup
- `wasm`: ensure heapptr is aligned
- `wasm`: update wasi-libc dependency
- `wasm`: wasi: use asyncify
- `wasm`: support `-scheduler=none`
- `windows`: add support for Windows (amd64 only for now)
* **boards**
- `feather-stm32f405`, `feather-rp2040`: add I2C pin names
- `m5stack-core2`: add M5Stack Core2
- `nano-33-ble`: SoftDevice s140v7 support
- `nano-33-ble`: add constants for more on-board pins
0.20.0
---
+1 -52
View File
@@ -1,52 +1 @@
# 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.
Please open a Github issue. We want to help, and also make sure that there is no duplications of efforts. Sometimes what you need is already being worked on by someone else.
A long tail of small (and large) language features haven't been implemented yet. In almost all cases, the compiler will show a `todo:` error from `compiler/compiler.go` when you try to use it. You can try implementing it, or open a bug report with a small code sample that fails to compile.
### Some specific hardware you want to use does not appear to be in TinyGo
As above, we probably have not implemented it yet. Your contribution adding the hardware support to TinyGo would be greatly appreciated.
Please start by opening a Github issue. We want to help you to help us to help you.
Lots of targets/boards are still unsupported. Adding an architecture often requires a few compiler changes, but if the architecture is supported you can try implementing support for a new chip or board in `src/runtime`. For details, see [this wiki entry on adding archs/chips/boards](https://github.com/tinygo-org/tinygo/wiki/Adding-a-new-board).
Microcontrollers have lots of peripherals (I2C, SPI, ADC, etc.) and many don't have an implementation yet in the `machine` package. Adding support for new peripherals is very useful.
## How to use our Github repository
The `release` 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
```
Please take a look at our [Contributing](https://tinygo.org/docs/guides/contributing/) page on our web site for details. Thank you.
+3 -2
View File
@@ -29,8 +29,9 @@ COPY --from=tinygo-base /tinygo/targets /tinygo/targets
RUN cd /tinygo/ && \
apt-get update && \
apt-get install -y make clang-11 libllvm11 lld-11 && \
make wasi-libc
apt-get install -y make clang-11 libllvm11 lld-11 cmake ninja-build && \
mkdir build && \
make wasi-libc binaryen
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
FROM tinygo-base AS tinygo-avr
+63 -15
View File
@@ -18,6 +18,9 @@ LLVM_NM ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-nm))$(call detec
GO ?= go
export GOROOT = $(shell $(GO) env GOROOT)
# Flags to pass to go test.
GOTESTFLAGS ?= -v
# md5sum binary
MD5SUM = md5sum
@@ -38,7 +41,7 @@ endif
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine frontendopenmp instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
ifeq ($(OS),Windows_NT)
EXE = .exe
@@ -54,18 +57,15 @@ ifeq ($(OS),Windows_NT)
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
CGO_LDFLAGS_EXTRA += -lversion
LIBCLANG_NAME = libclang
BINARYEN_OPTION += -DCMAKE_EXE_LINKER_FLAGS='-static-libgcc -static-libstdc++'
else ifeq ($(shell uname -s),Darwin)
MD5SUM = md5
LIBCLANG_NAME = clang
else ifeq ($(shell uname -s),FreeBSD)
MD5SUM = md5
LIBCLANG_NAME = clang
START_GROUP = -Wl,--start-group
END_GROUP = -Wl,--end-group
else
LIBCLANG_NAME = clang
START_GROUP = -Wl,--start-group
END_GROUP = -Wl,--end-group
endif
@@ -81,19 +81,22 @@ LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
# Other libraries that are needed to link TinyGo.
EXTRA_LIB_NAMES = LLVMInterpreter
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
# These build targets appear to be the only ones necessary to build all TinyGo
# dependencies. Only building a subset significantly speeds up rebuilding LLVM.
# The Makefile rules convert a name like lldELF to lib/liblldELF.a to match the
# library path (for ninja).
# This list also includes a few tools that are necessary as part of the full
# TinyGo build.
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIBCLANG_NAME) $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)))
NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(addsuffix .a,$(LIB_NAMES)))
# For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CXXFLAGS=-std=c++14
CGO_LDFLAGS+=$(abspath $(LLVM_BUILDDIR))/lib/lib$(LIBCLANG_NAME).a -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
endif
@@ -157,18 +160,25 @@ gen-device-rp: build/gen-device-svd
# Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm:
git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
git clone -b xtensa_release_12.0.1 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja: llvm-source
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
# Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
cd $(LLVM_BUILDDIR); ninja $(NINJA_BUILD_TARGETS)
cd $(LLVM_BUILDDIR) && ninja $(NINJA_BUILD_TARGETS)
# Build Binaryen
.PHONY: binaryen
binaryen: build/wasm-opt$(EXE)
build/wasm-opt$(EXE):
mkdir -p build
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
# Build wasi-libc sysroot
.PHONY: wasi-libc
@@ -184,37 +194,51 @@ tinygo:
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
TEST_PACKAGES = \
compress/bzip2 \
compress/flate \
compress/zlib \
container/heap \
container/list \
container/ring \
crypto/des \
crypto/dsa \
crypto/elliptic/internal/fiat \
crypto/internal/subtle \
crypto/md5 \
crypto/rc4 \
crypto/sha1 \
crypto/sha256 \
crypto/sha512 \
debug/macho \
encoding \
encoding/ascii85 \
encoding/base32 \
encoding/csv \
encoding/hex \
go/scanner \
hash \
hash/adler32 \
hash/fnv \
hash/crc64 \
hash/fnv \
html \
index/suffixarray \
internal/itoa \
internal/profile \
math \
math/cmplx \
net/http/internal/ascii \
net/mail \
os \
path \
reflect \
testing \
testing/iotest \
text/scanner \
text/scanner \
unicode \
unicode/utf16 \
unicode/utf8 \
# Test known-working standard library packages.
@@ -223,8 +247,11 @@ TEST_PACKAGES = \
tinygo-test:
$(TINYGO) test $(TEST_PACKAGES)
.PHONY: smoketest
.PHONY: smoketest smoketest-commands
smoketest:
@go run ./src/cmd/run-smoketest make smoketest-commands
smoketest-commands:
$(TINYGO) version
# test all examples (except pwm)
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
@@ -258,6 +285,7 @@ smoketest:
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
@$(MD5SUM) test.hex
# test simulated boards on play.tinygo.org
ifneq ($(WASM), 0)
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=hifive1b examples/blinky1
@@ -270,6 +298,7 @@ smoketest:
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
@$(MD5SUM) test.wasm
endif
# test all targets/boards
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
@$(MD5SUM) test.hex
@@ -407,6 +436,8 @@ ifneq ($(STM32), 0)
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-wl55jc examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
@@ -415,6 +446,8 @@ ifneq ($(STM32), 0)
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=lorae5 examples/blinky1
@$(MD5SUM) test.hex
endif
ifneq ($(AVR), 0)
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
@@ -443,6 +476,8 @@ ifneq ($(XTENSA), 0)
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target m5stack-core2 examples/serial
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target m5stack examples/serial
@$(MD5SUM) test.bin
endif
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
@$(MD5SUM) test.bin
@@ -452,8 +487,10 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
@$(MD5SUM) test.hex
ifneq ($(WASM), 0)
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
endif
# test various compiler flags
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
@$(MD5SUM) test.hex
@@ -466,7 +503,10 @@ endif
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
@$(MD5SUM) test.hex
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
ifneq ($(OS),Windows_NT)
# TODO: this does not yet work on Windows. Somehow, unused functions are
# not garbage collected.
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
endif
@@ -474,11 +514,13 @@ endif
wasmtest:
$(GO) test ./tests/wasm
build/release: tinygo gen-device wasi-libc
build/release: tinygo gen-device wasi-libc binaryen
@mkdir -p build/release/tinygo/bin
@mkdir -p build/release/tinygo/lib/clang/include
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@mkdir -p build/release/tinygo/lib/musl/arch
@mkdir -p build/release/tinygo/lib/musl/crt
@mkdir -p build/release/tinygo/lib/musl/src
@@ -491,6 +533,7 @@ build/release: tinygo gen-device wasi-libc
@mkdir -p build/release/tinygo/pkg/armv7em-unknown-unknown-eabi
@echo copying source files
@cp -p build/tinygo$(EXE) build/release/tinygo/bin
@cp -p build/wasm-opt$(EXE) build/release/tinygo/bin
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
@@ -518,6 +561,11 @@ build/release: tinygo gen-device wasi-libc
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
+6 -3
View File
@@ -1,6 +1,6 @@
# TinyGo - Go compiler for small places
[![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev) [![Build Status](https://dev.azure.com/tinygo/tinygo/_apis/build/status/tinygo-CI?branchName=dev)](https://dev.azure.com/tinygo/tinygo/_build/latest?definitionId=1&branchName=dev)
[![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev) [![Windows](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml)
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (Wasm), and command-line tools.
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 71 microcontroller boards are currently supported:
The following 74 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
@@ -84,6 +84,7 @@ The following 71 microcontroller boards are currently supported:
* [ESP32](https://www.espressif.com/en/products/socs/esp32)
* [ESP8266](https://www.espressif.com/en/products/socs/esp8266)
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
* [M5Stack](https://docs.m5stack.com/en/core/basic)
* [M5Stack Core2](https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit)
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
@@ -106,6 +107,7 @@ The following 71 microcontroller boards are currently supported:
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
@@ -113,6 +115,7 @@ The following 71 microcontroller boards are currently supported:
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
* [ST Micro "Nucleo" L432KC](https://www.st.com/ja/evaluation-tools/nucleo-l432kc.html)
* [ST Micro "Nucleo" L552ZE](https://www.st.com/en/evaluation-tools/nucleo-l552ze-q.html)
* [ST Micro "Nucleo" WL55JC](https://www.st.com/en/evaluation-tools/nucleo-wl55jc.html)
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
* [X9 Pro smartwatch](https://github.com/curtpw/nRF5x-device-reverse-engineering/tree/master/X9-nrf52832-activity-tracker/)
@@ -142,7 +145,7 @@ should arrive fairly quickly (under 1 min): https://invite.slack.golangbridge.or
Your contributions are welcome!
Please take a look at our [CONTRIBUTING.md](./CONTRIBUTING.md) document for details.
Please take a look at our [Contributing](https://tinygo.org/docs/guides/contributing/) page on our web site for details.
## Project Scope
+47 -23
View File
@@ -3,6 +3,7 @@ package builder
import (
"bytes"
"debug/elf"
"debug/pe"
"encoding/binary"
"errors"
"fmt"
@@ -39,29 +40,42 @@ func makeArchive(arfile *os.File, objs []string) error {
}
// Read the symbols and add them to the symbol table.
dbg, err := elf.NewFile(objfile)
if err != nil {
return fmt.Errorf("failed to open file %s: %w", objpath, err)
}
symbols, err := dbg.Symbols()
if err != nil {
return err
}
for _, symbol := range symbols {
bind := elf.ST_BIND(symbol.Info)
if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK {
// Don't include local symbols (STB_LOCAL).
continue
if dbg, err := elf.NewFile(objfile); err == nil {
symbols, err := dbg.Symbols()
if err != nil {
return err
}
if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC && elf.ST_TYPE(symbol.Info) != elf.STT_OBJECT {
// Not a function.
continue
for _, symbol := range symbols {
bind := elf.ST_BIND(symbol.Info)
if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK {
// Don't include local symbols (STB_LOCAL).
continue
}
if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC && elf.ST_TYPE(symbol.Info) != elf.STT_OBJECT {
// Not a function.
continue
}
// Include in archive.
symbolTable = append(symbolTable, struct {
name string
fileIndex int
}{symbol.Name, i})
}
// Include in archive.
symbolTable = append(symbolTable, struct {
name string
fileIndex int
}{symbol.Name, i})
} else if dbg, err := pe.NewFile(objfile); err == nil {
for _, symbol := range dbg.Symbols {
if symbol.StorageClass != 2 {
continue
}
if symbol.SectionNumber == 0 {
continue
}
symbolTable = append(symbolTable, struct {
name string
fileIndex int
}{symbol.Name, i})
}
} else {
return fmt.Errorf("failed to open file %s as ELF or PE/COFF: %w", objpath, err)
}
// Close file, to avoid issues with too many open files (especially on
@@ -120,11 +134,13 @@ func makeArchive(arfile *os.File, objs []string) error {
}
// Add all object files to the archive.
var copyBuf bytes.Buffer
for i, objpath := range objs {
objfile, err := os.Open(objpath)
if err != nil {
return err
}
defer objfile.Close()
// Store the start index, for when we'll update the symbol table with
// the correct file start indices.
@@ -155,13 +171,21 @@ func makeArchive(arfile *os.File, objs []string) error {
}
// Copy the file contents into the archive.
n, err := io.Copy(arwriter, objfile)
// First load all contents into a buffer, then write it all in one go to
// the archive file. This is a bit complicated, but is necessary because
// io.Copy can't deal with files that are of an odd size.
copyBuf.Reset()
n, err := io.Copy(&copyBuf, objfile)
if err != nil {
return err
return fmt.Errorf("could not copy object file into ar file: %w", err)
}
if n != st.Size() {
return errors.New("file modified during ar creation: " + arfile.Name())
}
_, err = arwriter.Write(copyBuf.Bytes())
if err != nil {
return fmt.Errorf("could not copy object file into ar file: %w", err)
}
// File is not needed anymore.
objfile.Close()
+42 -1
View File
@@ -16,9 +16,11 @@ import (
"io/ioutil"
"math/bits"
"os"
"os/exec"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/cgo"
@@ -113,6 +115,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
}
libcDependencies = append(libcDependencies, dummyCompileJob(path))
case "mingw-w64":
_, err := MinGW.load(config, dir)
if err != nil {
return err
}
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(dir)...)
case "":
// no library specified, so nothing to do
default:
@@ -136,7 +144,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(),
Debug: true,
LLVMFeatures: config.LLVMFeatures(),
}
// Load the target machine, which is the LLVM object that contains all
@@ -517,6 +524,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Prepare link command.
linkerDependencies := []*compileJob{outputObjectFileJob}
executable := filepath.Join(dir, "main")
if config.GOOS() == "windows" {
executable += ".exe"
}
tmppath := executable // final file
ldflags := append(config.LDFlags(), "-o", executable)
@@ -659,6 +669,37 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
}
}
// Run wasm-opt if necessary.
if config.Scheduler() == "asyncify" {
var optLevel, shrinkLevel int
switch config.Options.Opt {
case "none", "0":
case "1":
optLevel = 1
case "2":
optLevel = 2
case "s":
optLevel = 2
shrinkLevel = 1
case "z":
optLevel = 2
shrinkLevel = 2
default:
return fmt.Errorf("unknown opt level: %q", config.Options.Opt)
}
cmd := exec.Command(goenv.Get("WASMOPT"), "--asyncify", "-g",
"--optimize-level", strconv.Itoa(optLevel),
"--shrink-level", strconv.Itoa(shrinkLevel),
executable, "--output", executable)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("wasm-opt failed: %w", err)
}
}
// Print code size if requested.
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
packagePathMap := make(map[string]string, len(lprogram.Packages))
+28 -6
View File
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/tinygo-org/tinygo/compileopts"
@@ -12,8 +13,8 @@ import (
"tinygo.org/x/go-llvm"
)
// Test whether the Clang generated "target-cpu" attribute matches the CPU
// property in TinyGo target files.
// Test whether the Clang generated "target-cpu" and "target-features"
// attributes match the CPU and Features property in TinyGo target files.
func TestClangAttributes(t *testing.T) {
var targetNames = []string{
// Please keep this list sorted!
@@ -52,12 +53,19 @@ func TestClangAttributes(t *testing.T) {
for _, options := range []*compileopts.Options{
{GOOS: "linux", GOARCH: "386"},
{GOOS: "linux", GOARCH: "amd64"},
{GOOS: "linux", GOARCH: "arm"},
{GOOS: "linux", GOARCH: "arm", GOARM: "5"},
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
{GOOS: "linux", GOARCH: "arm64"},
{GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"},
} {
t.Run("GOOS="+options.GOOS+",GOARCH="+options.GOARCH, func(t *testing.T) {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" {
name += ",GOARM=" + options.GOARM
}
t.Run(name, func(t *testing.T) {
testClangAttributes(t, options)
})
}
@@ -112,16 +120,30 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
t.Errorf("target has LLVM triple %#v but Clang makes it LLVM triple %#v", config.Triple(), mod.Target())
}
// Check the "target-cpu" string attribute of the add function.
// Check the "target-cpu" and "target-features" string attribute of the add
// function.
add := mod.NamedFunction("add")
var cpu string
var cpu, features string
cpuAttr := add.GetStringAttributeAtIndex(-1, "target-cpu")
featuresAttr := add.GetStringAttributeAtIndex(-1, "target-features")
if !cpuAttr.IsNil() {
cpu = cpuAttr.GetStringValue()
}
if !featuresAttr.IsNil() {
features = featuresAttr.GetStringValue()
}
if cpu != config.CPU() {
t.Errorf("target has CPU %#v but Clang makes it CPU %#v", config.CPU(), cpu)
}
if features != config.Features() {
if hasBuiltinTools || runtime.GOOS != "linux" {
// Skip this step when using an external Clang invocation on Linux.
// The reason is that Debian has patched Clang in a way that
// modifies the LLVM features string, changing lots of FPU/float
// related flags. We want to test vanilla Clang, not Debian Clang.
t.Errorf("target has LLVM features\n\t%#v\nbut Clang makes it\n\t%#v", config.Features(), features)
}
}
}
// This TestMain is necessary because TinyGo may also be invoked to run certain
+5
View File
@@ -11,6 +11,11 @@ bool tinygo_link_elf(int argc, char **argv) {
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
}
bool tinygo_link_mingw(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc);
return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
}
bool tinygo_link_wasm(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc);
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
+91
View File
@@ -0,0 +1,91 @@
package builder
import (
"io"
"os"
"path/filepath"
"strings"
"github.com/tinygo-org/tinygo/goenv"
)
var MinGW = Library{
name: "mingw-w64",
makeHeaders: func(target, includeDir string) error {
// copy _mingw.h
srcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "mingw-w64")
outf, err := os.Create(includeDir + "/_mingw.h")
if err != nil {
return err
}
defer outf.Close()
inf, err := os.Open(srcDir + "/mingw-w64-headers/crt/_mingw.h.in")
if err != nil {
return err
}
_, err = io.Copy(outf, inf)
return err
},
cflags: func(target, headerPath string) []string {
// No flags necessary because there are no files to compile.
return nil
},
librarySources: func(target string) []string {
// We only use the UCRT DLL file. No source files necessary.
return nil
},
}
// makeMinGWExtraLibs returns a slice of jobs to import the correct .dll
// libraries. This is done by converting input .def files to .lib files which
// can then be linked as usual.
//
// TODO: cache the result. At the moment, it costs a few hundred milliseconds to
// compile these files.
func makeMinGWExtraLibs(tmpdir string) []*compileJob {
var jobs []*compileJob
root := goenv.Get("TINYGOROOT")
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
// .lib file. But to simplify things, we're going to leave them as separate
// files.
for _, name := range []string{
"kernel32.def.in",
"api-ms-win-crt-conio-l1-1-0.def",
"api-ms-win-crt-convert-l1-1-0.def",
"api-ms-win-crt-environment-l1-1-0.def",
"api-ms-win-crt-filesystem-l1-1-0.def",
"api-ms-win-crt-heap-l1-1-0.def",
"api-ms-win-crt-locale-l1-1-0.def",
"api-ms-win-crt-math-l1-1-0.def.in",
"api-ms-win-crt-multibyte-l1-1-0.def",
"api-ms-win-crt-private-l1-1-0.def.in",
"api-ms-win-crt-process-l1-1-0.def",
"api-ms-win-crt-runtime-l1-1-0.def.in",
"api-ms-win-crt-stdio-l1-1-0.def",
"api-ms-win-crt-string-l1-1-0.def",
"api-ms-win-crt-time-l1-1-0.def",
"api-ms-win-crt-utility-l1-1-0.def",
} {
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
job := &compileJob{
description: "create lib file " + inpath,
result: outpath,
run: func(job *compileJob) error {
defpath := inpath
if strings.HasSuffix(inpath, ".in") {
// .in files need to be preprocessed by a preprocessor (-E)
// first.
defpath = outpath + ".def"
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/")
if err != nil {
return err
}
}
return link("ld.lld", "-m", "i386pep", "-o", outpath, defpath)
},
}
jobs = append(jobs, job)
}
return jobs
}
+91 -3
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"debug/dwarf"
"debug/elf"
"debug/pe"
"encoding/binary"
"fmt"
"io"
@@ -96,6 +97,17 @@ const (
memoryStack
)
func (t memoryType) String() string {
return [...]string{
0: "-",
memoryCode: "code",
memoryData: "data",
memoryROData: "rodata",
memoryBSS: "bss",
memoryStack: "stack",
}[t]
}
// Regular expressions to match particular symbol names. These are not stored as
// DWARF variables because they have no mapping to source code global variables.
var (
@@ -288,6 +300,13 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE {
continue
}
if symbol.Section >= elf.SHN_LORESERVE {
// Not a regular section, so skip it.
// One example is elf.SHN_ABS, which is used for symbols
// declared with an absolute value such as the memset function
// on the ESP32 which is defined in the mask ROM.
continue
}
section := file.Sections[symbol.Section]
if section.Flags&elf.SHF_ALLOC == 0 {
continue
@@ -349,6 +368,72 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
})
}
}
} else if file, err := pe.NewFile(f); err == nil {
// Read DWARF information. The error is intentionally ignored.
data, _ := file.DWARF()
if data != nil {
addresses, err = readProgramSizeFromDWARF(data, 0)
if err != nil {
// However, _do_ report an error here. Something must have gone
// wrong while trying to parse DWARF data.
return nil, err
}
}
// Read COFF sections.
optionalHeader := file.OptionalHeader.(*pe.OptionalHeader64)
for _, section := range file.Sections {
// For more information:
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_section_header
const (
IMAGE_SCN_CNT_CODE = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000
IMAGE_SCN_MEM_READ = 0x40000000
IMAGE_SCN_MEM_WRITE = 0x80000000
)
if section.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
// Debug sections, etc.
continue
}
address := uint64(section.VirtualAddress) + optionalHeader.ImageBase
if section.Characteristics&IMAGE_SCN_CNT_CODE != 0 {
// .text
sections = append(sections, memorySection{
Address: address,
Size: uint64(section.VirtualSize),
Type: memoryCode,
})
} else if section.Characteristics&IMAGE_SCN_CNT_INITIALIZED_DATA != 0 {
if section.Characteristics&IMAGE_SCN_MEM_WRITE != 0 {
// .data
sections = append(sections, memorySection{
Address: address,
Size: uint64(section.Size),
Type: memoryData,
})
if section.Size < section.VirtualSize {
// Equivalent of a .bss section.
// Note: because of how the PE/COFF format is
// structured, not all zero-initialized data is marked
// as such. A portion may be at the end of the .data
// section and is thus marked as initialized data.
sections = append(sections, memorySection{
Address: address + uint64(section.Size),
Size: uint64(section.VirtualSize) - uint64(section.Size),
Type: memoryBSS,
})
}
} else if section.Characteristics&IMAGE_SCN_MEM_READ != 0 {
// .rdata, .buildid, .pdata
sections = append(sections, memorySection{
Address: address,
Size: uint64(section.VirtualSize),
Type: memoryROData,
})
}
}
}
} else if file, err := wasm.Parse(f); err == nil {
// File is in WebAssembly format.
@@ -506,8 +591,11 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
// section. We start at the beginning.
addr := section.Address
sectionEnd := section.Address + section.Size
if sizesDebug {
fmt.Printf("%08x..%08x %5d: %s\n", addr, sectionEnd, section.Size, section.Type)
}
for _, line := range addresses {
if line.Address < section.Address || line.Address+line.Length >= sectionEnd {
if line.Address < section.Address || line.Address+line.Length > sectionEnd {
// Check that this line is entirely within the section.
// Don't bother dealing with line entries that cross sections (that
// seems rather unlikely anyway).
@@ -518,7 +606,7 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
// previous line entry.
addSize("(unknown)", line.Address-addr, false)
if sizesDebug {
fmt.Printf("%08x..%08x %4d: unknown (gap)\n", addr, line.Address, line.Address-addr)
fmt.Printf("%08x..%08x %5d: unknown (gap)\n", addr, line.Address, line.Address-addr)
}
}
if addr > line.Address+line.Length {
@@ -543,7 +631,7 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
// There is a gap at the end of the section.
addSize("(unknown)", sectionEnd-addr, false)
if sizesDebug {
fmt.Printf("%08x..%08x %4d: unknown (end)\n", addr, sectionEnd, sectionEnd-addr)
fmt.Printf("%08x..%08x %5d: unknown (end)\n", addr, sectionEnd, sectionEnd-addr)
}
}
}
+13 -1
View File
@@ -13,6 +13,7 @@ import (
#include <stdlib.h>
bool tinygo_clang_driver(int argc, char **argv);
bool tinygo_link_elf(int argc, char **argv);
bool tinygo_link_mingw(int argc, char **argv);
bool tinygo_link_wasm(int argc, char **argv);
*/
import "C"
@@ -24,6 +25,10 @@ const hasBuiltinTools = true
// This version actually runs the tools because TinyGo was compiled while
// linking statically with LLVM (with the byollvm build tag).
func RunTool(tool string, args ...string) error {
linker := "elf"
if tool == "ld.lld" && len(args) >= 2 && args[0] == "-m" && args[1] == "i386pep" {
linker = "mingw"
}
args = append([]string{"tinygo:" + tool}, args...)
var cflag *C.char
@@ -41,7 +46,14 @@ func RunTool(tool string, args ...string) error {
case "clang":
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
case "ld.lld":
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
switch linker {
case "elf":
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
case "mingw":
ok = C.tinygo_link_mingw(C.int(len(args)), (**C.char)(buf))
default:
return errors.New("unknown linker: " + linker)
}
case "wasm-ld":
ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf))
default:
+79 -60
View File
@@ -14,6 +14,7 @@ package cgo
import (
"fmt"
"go/ast"
"go/parser"
"go/scanner"
"go/token"
"path/filepath"
@@ -162,6 +163,39 @@ typedef long long _Cgo_longlong;
typedef unsigned long long _Cgo_ulonglong;
`
// First part of the generated Go file. Written here as Go because that's much
// easier than constructing the entire AST in memory.
// The string/bytes functions below implement C.CString etc. To make sure the
// runtime doesn't need to know the C int type, lengths are converted to uintptr
// first.
// These functions will be modified to get a "C." prefix, so the source below
// doesn't reflect the final AST.
const generatedGoFilePrefix = `
import "unsafe"
var _ unsafe.Pointer
//go:linkname C.CString runtime.cgo_CString
func CString(string) *C.char
//go:linkname C.GoString runtime.cgo_GoString
func GoString(*C.char) string
//go:linkname C.__GoStringN runtime.cgo_GoStringN
func __GoStringN(*C.char, uintptr) string
func GoStringN(cstr *C.char, length C.int) string {
return C.__GoStringN(cstr, uintptr(length))
}
//go:linkname C.__GoBytes runtime.cgo_GoBytes
func __GoBytes(unsafe.Pointer, uintptr) []byte
func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
`
// Process extracts `import "C"` statements from the AST, parses the comment
// with libclang, and modifies the AST to use this information. It returns a
// newly created *ast.File that should be added to the list of to-be-parsed
@@ -202,58 +236,31 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
p.packageDir = filepath.Dir(packagePath)
// Construct a new in-memory AST for CGo declarations of this package.
unsafeImport := &ast.ImportSpec{
Path: &ast.BasicLit{
ValuePos: p.generatedPos,
Kind: token.STRING,
Value: "\"unsafe\"",
},
EndPos: p.generatedPos,
// The first part is written as Go code that is then parsed, but more code
// is added later to the AST to declare functions, globals, etc.
goCode := "package " + files[0].Name.Name + "\n\n" + generatedGoFilePrefix
p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments)
if err != nil {
// This is always a bug in the cgo package.
panic("unexpected error: " + err.Error())
}
p.generated = &ast.File{
Package: p.generatedPos,
Name: &ast.Ident{
NamePos: p.generatedPos,
Name: files[0].Name.Name,
},
Decls: []ast.Decl{
// import "unsafe"
&ast.GenDecl{
TokPos: p.generatedPos,
Tok: token.IMPORT,
Specs: []ast.Spec{
unsafeImport,
},
},
// var _ unsafe.Pointer
// This avoids type errors when the unsafe package is never used.
&ast.GenDecl{
Tok: token.VAR,
Specs: []ast.Spec{
&ast.ValueSpec{
Names: []*ast.Ident{
{
Name: "_",
Obj: &ast.Object{
Kind: ast.Var,
Name: "_",
},
},
},
Type: &ast.SelectorExpr{
X: &ast.Ident{
Name: "unsafe",
},
Sel: &ast.Ident{
Name: "Pointer",
},
},
},
},
},
},
Imports: []*ast.ImportSpec{unsafeImport},
// If the Comments field is not set to nil, the fmt package will get
// confused about where comments should go.
p.generated.Comments = nil
// Adjust some of the functions in there.
for _, decl := range p.generated.Decls {
switch decl := decl.(type) {
case *ast.FuncDecl:
switch decl.Name.Name {
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes":
// Adjust the name to have a "C." prefix so it is correctly
// resolved.
decl.Name.Name = "C." + decl.Name.Name
}
}
}
// Patch some types, for example *C.char in C.CString.
astutil.Apply(p.generated, p.walker, nil)
// Find all C.* symbols.
for _, f := range files {
@@ -496,6 +503,14 @@ func (p *cgoPackage) addFuncDecls() {
}
args := make([]*ast.Field, len(fn.args))
decl := &ast.FuncDecl{
Doc: &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: fn.pos - 1,
Text: "//export " + name,
},
},
},
Name: &ast.Ident{
NamePos: fn.pos,
Name: "C." + name,
@@ -512,14 +527,10 @@ func (p *cgoPackage) addFuncDecls() {
},
}
if fn.variadic {
decl.Doc = &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: fn.pos,
Text: "//go:variadic",
},
},
}
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
Slash: fn.pos - 1,
Text: "//go:variadic",
})
}
obj.Decl = decl
for i, arg := range fn.args {
@@ -652,13 +663,21 @@ func (p *cgoPackage) addVarDecls() {
}
sort.Strings(names)
for _, name := range names {
global := p.globals[name]
gen := &ast.GenDecl{
TokPos: token.NoPos,
TokPos: global.pos,
Tok: token.VAR,
Lparen: token.NoPos,
Rparen: token.NoPos,
Doc: &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: global.pos - 1,
Text: "//go:extern " + name,
},
},
},
}
global := p.globals[name]
obj := &ast.Object{
Kind: ast.Var,
Name: "C." + name,
+8 -1
View File
@@ -30,7 +30,14 @@ func normalizeResult(result string) string {
func TestCGo(t *testing.T) {
var cflags = []string{"--target=armv6m-unknown-unknown-eabi"}
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
for _, name := range []string{
"basic",
"errors",
"types",
"symbols",
"flags",
"const",
} {
name := name // avoid a race condition
t.Run(name, func(t *testing.T) {
// Skip tests that require specific Go version.
+1
View File
@@ -1,4 +1,5 @@
// +build !byollvm
// +build !llvm12
package cgo
+14
View File
@@ -0,0 +1,14 @@
// +build !byollvm
// +build llvm12
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-12/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@12/include
#cgo freebsd CFLAGS: -I/usr/local/llvm12/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang
*/
import "C"
+20
View File
@@ -4,6 +4,26 @@ import "unsafe"
var _ unsafe.Pointer
//go:linkname C.CString runtime.cgo_CString
func C.CString(string) *C.char
//go:linkname C.GoString runtime.cgo_GoString
func C.GoString(*C.char) string
//go:linkname C.__GoStringN runtime.cgo_GoStringN
func C.__GoStringN(*C.char, uintptr) string
func C.GoStringN(cstr *C.char, length C.int) string {
return C.__GoStringN(cstr, uintptr(length))
}
//go:linkname C.__GoBytes runtime.cgo_GoBytes
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
+20
View File
@@ -4,6 +4,26 @@ import "unsafe"
var _ unsafe.Pointer
//go:linkname C.CString runtime.cgo_CString
func C.CString(string) *C.char
//go:linkname C.GoString runtime.cgo_GoString
func C.GoString(*C.char) string
//go:linkname C.__GoStringN runtime.cgo_GoStringN
func C.__GoStringN(*C.char, uintptr) string
func C.GoStringN(cstr *C.char, length C.int) string {
return C.__GoStringN(cstr, uintptr(length))
}
//go:linkname C.__GoBytes runtime.cgo_GoBytes
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
const C.bar = C.foo
const C.foo = 3
+20
View File
@@ -18,6 +18,26 @@ import "unsafe"
var _ unsafe.Pointer
//go:linkname C.CString runtime.cgo_CString
func C.CString(string) *C.char
//go:linkname C.GoString runtime.cgo_GoString
func C.GoString(*C.char) string
//go:linkname C.__GoStringN runtime.cgo_GoStringN
func C.__GoStringN(*C.char, uintptr) string
func C.GoStringN(cstr *C.char, length C.int) string {
return C.__GoStringN(cstr, uintptr(length))
}
//go:linkname C.__GoBytes runtime.cgo_GoBytes
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
const C.SOME_CONST_3 = 1234
type C.int16_t = int16
+20
View File
@@ -9,6 +9,26 @@ import "unsafe"
var _ unsafe.Pointer
//go:linkname C.CString runtime.cgo_CString
func C.CString(string) *C.char
//go:linkname C.GoString runtime.cgo_GoString
func C.GoString(*C.char) string
//go:linkname C.__GoStringN runtime.cgo_GoStringN
func C.__GoStringN(*C.char, uintptr) string
func C.GoStringN(cstr *C.char, length C.int) string {
return C.__GoStringN(cstr, uintptr(length))
}
//go:linkname C.__GoBytes runtime.cgo_GoBytes
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
const C.BAR = 3
const C.FOO_H = 1
+23
View File
@@ -0,0 +1,23 @@
package main
/*
// Function signatures.
int foo(int a, int b);
void variadic0();
void variadic2(int x, int y, ...);
// Global variable signatures.
extern int someValue;
*/
import "C"
// Test function signatures.
func accessFunctions() {
C.foo(3, 4)
C.variadic0()
C.variadic2(3, 5)
}
func accessGlobals() {
_ = C.someValue
}
+64
View File
@@ -0,0 +1,64 @@
package main
import "unsafe"
var _ unsafe.Pointer
//go:linkname C.CString runtime.cgo_CString
func C.CString(string) *C.char
//go:linkname C.GoString runtime.cgo_GoString
func C.GoString(*C.char) string
//go:linkname C.__GoStringN runtime.cgo_GoStringN
func C.__GoStringN(*C.char, uintptr) string
func C.GoStringN(cstr *C.char, length C.int) string {
return C.__GoStringN(cstr, uintptr(length))
}
//go:linkname C.__GoBytes runtime.cgo_GoBytes
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
//export foo
func C.foo(a C.int, b C.int) C.int
//export variadic0
//go:variadic
func C.variadic0()
//export variadic2
//go:variadic
func C.variadic2(x C.int, y C.int)
var C.foo$funcaddr unsafe.Pointer
var C.variadic0$funcaddr unsafe.Pointer
var C.variadic2$funcaddr unsafe.Pointer
//go:extern someValue
var C.someValue C.int
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
-10
View File
@@ -104,10 +104,6 @@ typedef struct {
unsigned char e : 3;
// Note that C++ allows bitfields bigger than the underlying type.
} bitfield_t;
// Function signatures.
void variadic0();
void variadic2(int x, int y, ...);
*/
import "C"
@@ -171,9 +167,3 @@ func accessUnion() {
var _ *C.int = union2d.unionfield_i()
var _ *[2]float64 = union2d.unionfield_d()
}
// Test function signatures.
func accessFunctions() {
C.variadic0()
C.variadic2(3, 5)
}
+19 -4
View File
@@ -4,10 +4,25 @@ import "unsafe"
var _ unsafe.Pointer
func C.variadic0() //go:variadic
func C.variadic2(x C.int, y C.int) //go:variadic
var C.variadic0$funcaddr unsafe.Pointer
var C.variadic2$funcaddr unsafe.Pointer
//go:linkname C.CString runtime.cgo_CString
func C.CString(string) *C.char
//go:linkname C.GoString runtime.cgo_GoString
func C.GoString(*C.char) string
//go:linkname C.__GoStringN runtime.cgo_GoStringN
func C.__GoStringN(*C.char, uintptr) string
func C.GoStringN(cstr *C.char, length C.int) string {
return C.__GoStringN(cstr, uintptr(length))
}
//go:linkname C.__GoBytes runtime.cgo_GoBytes
func C.__GoBytes(unsafe.Pointer, uintptr) []byte
func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
const C.option2A = 20
const C.optionA = 0
+26 -9
View File
@@ -34,10 +34,16 @@ func (c *Config) CPU() string {
}
// Features returns a list of features this CPU supports. For example, for a
// RISC-V processor, that could be ["+a", "+c", "+m"]. For many targets, an
// empty list will be returned.
func (c *Config) Features() []string {
return c.Target.Features
// RISC-V processor, that could be "+a,+c,+m". For many targets, an empty list
// will be returned.
func (c *Config) Features() string {
if c.Target.Features == "" {
return c.Options.LLVMFeatures
}
if c.Options.LLVMFeatures == "" {
return c.Target.Features
}
return c.Target.Features + "," + c.Options.LLVMFeatures
}
// GOOS returns the GOOS of the target. This might not always be the actual OS:
@@ -54,6 +60,12 @@ func (c *Config) GOARCH() string {
return c.Target.GOARCH
}
// GOARM will return the GOARM environment variable given to the compiler when
// building a program.
func (c *Config) GOARM() string {
return c.Options.GOARM
}
// BuildTags returns the complete list of build tags used during this build.
func (c *Config) BuildTags() []string {
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
@@ -151,7 +163,7 @@ func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
// target.
func (c *Config) FuncImplementation() string {
switch c.Scheduler() {
case "tasks":
case "tasks", "asyncify":
// A func value is implemented as a pair of pointers:
// {context, function pointer}
// where the context may be a pointer to a heap-allocated struct
@@ -260,6 +272,15 @@ func (c *Config) CFlags() []string {
case "wasi-libc":
root := goenv.Get("TINYGOROOT")
cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot")
case "mingw-w64":
root := goenv.Get("TINYGOROOT")
path, _ := c.LibcPath("mingw-w64")
cflags = append(cflags,
"--sysroot="+path,
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
"-D_UCRT",
)
case "":
// No libc specified, nothing to add.
default:
@@ -441,10 +462,6 @@ func (c *Config) WasmAbi() string {
return c.Target.WasmAbi
}
func (c *Config) LLVMFeatures() string {
return c.Options.LLVMFeatures
}
type TestConfig struct {
CompileTestBinary bool
// TODO: Filter the test functions to run, include verbose flag, etc
+2 -1
View File
@@ -8,7 +8,7 @@ import (
var (
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
validSchedulerOptions = []string{"none", "tasks", "coroutines", "asyncify"}
validSerialOptions = []string{"none", "uart", "usb"}
validPrintSizeOptions = []string{"none", "short", "full"}
validPanicStrategyOptions = []string{"print", "trap"}
@@ -21,6 +21,7 @@ var (
type Options struct {
GOOS string // environment variable
GOARCH string // environment variable
GOARM string // environment variable (only used with GOARCH=arm)
Target string
Opt string
GC string
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func TestVerifyOptions(t *testing.T) {
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, extalloc, conservative`)
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines`)
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines, asyncify`)
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
+79 -17
View File
@@ -5,15 +5,18 @@ package compileopts
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
)
// Target specification for a given target. Used for bare metal targets.
@@ -25,7 +28,7 @@ type TargetSpec struct {
Inherits []string `json:"inherits"`
Triple string `json:"llvm-target"`
CPU string `json:"cpu"`
Features []string `json:"features"`
Features string `json:"features"`
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
BuildTags []string `json:"build-tags"`
@@ -94,7 +97,7 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
dst.Set(src)
case "append", "":
// or append the field of child to spec
dst.Set(reflect.AppendSlice(src, dst))
dst.Set(reflect.AppendSlice(dst, src))
default:
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
}
@@ -165,13 +168,26 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
if options.Target == "" {
// Configure based on GOOS/GOARCH environment variables (falling back to
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
llvmarch := map[string]string{
"386": "i386",
"amd64": "x86_64",
"arm64": "aarch64",
"arm": "armv7",
}[options.GOARCH]
if llvmarch == "" {
var llvmarch string
switch options.GOARCH {
case "386":
llvmarch = "i386"
case "amd64":
llvmarch = "x86_64"
case "arm64":
llvmarch = "aarch64"
case "arm":
switch options.GOARM {
case "5":
llvmarch = "armv5"
case "6":
llvmarch = "armv6"
case "7":
llvmarch = "armv7"
default:
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
}
default:
llvmarch = options.GOARCH
}
llvmos := options.GOOS
@@ -192,6 +208,9 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
if options.GOARCH == "arm" {
target += "-gnueabihf"
}
if options.GOOS == "windows" {
target += "-gnu"
}
return defaultTarget(options.GOOS, options.GOARCH, target)
}
@@ -208,16 +227,15 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
if err != nil {
return nil, err
}
if spec.Scheduler == "asyncify" {
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_asyncify_wasm.S")
}
return spec, nil
}
// WindowsBuildNotSupportedErr is being thrown, when goos is windows and no target has been specified.
var WindowsBuildNotSupportedErr = errors.New("Building Windows binaries is currently not supported. Try specifying a different target")
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
if goos == "windows" {
return nil, WindowsBuildNotSupportedErr
}
// No target spec available. Use the default one, useful on most systems
// with a regular OS.
spec := TargetSpec{
@@ -234,12 +252,23 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
switch goarch {
case "386":
spec.CPU = "pentium4"
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "amd64":
spec.CPU = "x86-64"
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "arm":
spec.CPU = "generic"
switch strings.Split(triple, "-")[0] {
case "armv5":
spec.Features = "+armv5t,+strict-align,-thumb-mode"
case "armv6":
spec.Features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-thumb-mode"
case "armv7":
spec.Features = "+armv7-a,+d32,+dsp,+fp64,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-thumb-mode"
}
case "arm64":
spec.CPU = "generic"
spec.Features = "+neon"
}
if goos == "darwin" {
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
@@ -249,12 +278,40 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.RTLib = "compiler-rt"
spec.Libc = "musl"
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
} else if goos == "windows" {
spec.Linker = "ld.lld"
spec.Libc = "mingw-w64"
// Note: using a medium code model, low image base and no ASLR
// because Go doesn't really need those features. ASLR patches
// around issues for unsafe languages like C/C++ that are not
// normally present in Go (without explicitly opting in).
// For more discussion:
// https://groups.google.com/g/Golang-nuts/c/Jd9tlNc6jUE/m/Zo-7zIP_m3MJ?pli=1
spec.LDFlags = append(spec.LDFlags,
"-m", "i386pep",
"-Bdynamic",
"--image-base", "0x400000",
"--gc-sections",
"--no-insert-timestamp",
)
llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
if llvmMajor >= 12 {
// This flag was added in LLVM 12. At the same time, LLVM 12
// switched the default from --dynamicbase to --no-dynamicbase.
spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase")
}
} else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
}
if goarch != "wasm" {
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+".S")
suffix := ""
if goos == "windows" {
// Windows uses a different calling convention from other operating
// systems so we need separate assembly files.
suffix = "_windows"
}
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
}
if goarch != runtime.GOARCH {
// Some educated guesses as to how to invoke helper programs.
@@ -266,6 +323,11 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Emulator = []string{"qemu-aarch64"}
}
}
if goos != runtime.GOOS {
if goos == "windows" {
spec.Emulator = []string{"wine"}
}
}
return &spec, nil
}
+4 -4
View File
@@ -27,7 +27,7 @@ func TestOverrideProperties(t *testing.T) {
base := &TargetSpec{
GOOS: "baseGoos",
CPU: "baseCpu",
Features: []string{"bf1", "bf2"},
CFlags: []string{"-base-foo", "-base-bar"},
BuildTags: []string{"bt1", "bt2"},
Emulator: []string{"be1", "be2"},
DefaultStackSize: 42,
@@ -37,7 +37,7 @@ func TestOverrideProperties(t *testing.T) {
child := &TargetSpec{
GOOS: "",
CPU: "chlidCpu",
Features: []string{"cf1", "cf2"},
CFlags: []string{"-child-foo", "-child-bar"},
Emulator: []string{"ce1", "ce2"},
AutoStackSize: &childAutoStackSize,
DefaultStackSize: 64,
@@ -51,8 +51,8 @@ func TestOverrideProperties(t *testing.T) {
if base.CPU != "chlidCpu" {
t.Errorf("Overriding failed : got %v", base.CPU)
}
if !reflect.DeepEqual(base.Features, []string{"cf1", "cf2", "bf1", "bf2"}) {
t.Errorf("Overriding failed : got %v", base.Features)
if !reflect.DeepEqual(base.CFlags, []string{"-base-foo", "-base-bar", "-child-foo", "-child-bar"}) {
t.Errorf("Overriding failed : got %v", base.CFlags)
}
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
t.Errorf("Overriding failed : got %v", base.BuildTags)
+26 -49
View File
@@ -15,22 +15,16 @@ import (
// createLookupBoundsCheck 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 (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value, indexType types.Type) {
// The caller should make sure that index is at least as big as arrayLen.
func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) {
if b.info.nobounds {
// 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.Underlying().(*types.Basic).Info()&types.IsUnsigned == 0 {
index = b.CreateZExt(index, arrayLen.Type(), "")
} else {
index = b.CreateSExt(index, arrayLen.Type(), "")
}
} else if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() {
// Extend arrayLen if it's too small.
if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() {
// The index is bigger than the array length type, so extend it.
arrayLen = b.CreateZExt(arrayLen, index.Type(), "")
}
@@ -70,27 +64,9 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
}
// Extend low and high to be the same size as capacity.
if low.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
if lowType.Info()&types.IsUnsigned != 0 {
low = b.CreateZExt(low, capacityType, "")
} else {
low = b.CreateSExt(low, capacityType, "")
}
}
if high.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
if highType.Info()&types.IsUnsigned != 0 {
high = b.CreateZExt(high, capacityType, "")
} else {
high = b.CreateSExt(high, capacityType, "")
}
}
if max.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
if maxType.Info()&types.IsUnsigned != 0 {
max = b.CreateZExt(max, capacityType, "")
} else {
max = b.CreateSExt(max, capacityType, "")
}
}
low = b.extendInteger(low, lowType, capacityType)
high = b.extendInteger(high, highType, capacityType)
max = b.extendInteger(max, maxType, capacityType)
// Now do the bounds check: low > high || high > capacity
outOfBounds1 := b.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh")
@@ -125,13 +101,7 @@ func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Bas
// using an unsiged greater than.
// Make sure the len value is at least as big as a uintptr.
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
if lenType.Info()&types.IsUnsigned != 0 {
len = b.CreateZExt(len, b.uintptrType, "")
} else {
len = b.CreateSExt(len, b.uintptrType, "")
}
}
len = b.extendInteger(len, lenType, b.uintptrType)
// Determine the maximum slice size, and therefore the maximum value of the
// len parameter.
@@ -159,17 +129,8 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
return
}
// Check whether the bufSize parameter must be cast to a wider integer for
// comparison.
if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
if bufSizeType.Info()&types.IsUnsigned != 0 {
// Unsigned, so zero-extend to uint type.
bufSize = b.CreateZExt(bufSize, b.intType, "")
} else {
// Signed, so sign-extend to int type.
bufSize = b.CreateSExt(bufSize, b.intType, "")
}
}
// Make sure bufSize is at least as big as maxBufSize (an uintptr).
bufSize = b.extendInteger(bufSize, bufSizeType, b.uintptrType)
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in an
// uintptr if uintptrs were signed.
@@ -294,3 +255,19 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
// Ok: assert didn't trigger so continue normally.
b.SetInsertPointAtEnd(nextBlock)
}
// extendInteger extends the value to at least targetType using a zero or sign
// extend. The resulting value is not truncated: it may still be bigger than
// targetType.
func (b *builder) extendInteger(value llvm.Value, valueType types.Type, targetType llvm.Type) llvm.Value {
if value.Type().IntTypeWidth() < targetType.IntTypeWidth() {
if valueType.Underlying().(*types.Basic).Info()&types.IsUnsigned != 0 {
// Unsigned, so zero-extend to the target type.
value = b.CreateZExt(value, targetType, "")
} else {
// Signed, so sign-extend to the target type.
value = b.CreateSExt(value, targetType, "")
}
}
return value
}
+32 -42
View File
@@ -23,7 +23,7 @@ import (
// Version of the compiler pacakge. Must be incremented each time the compiler
// package changes in a way that affects the generated LLVM module.
// This version is independent of the TinyGo version number.
const Version = 24 // last change: add layout param to runtime.alloc calls
const Version = 25 // last change: add "target-cpu" and "target-features" attributes
func init() {
llvm.InitializeAllTargets()
@@ -43,7 +43,7 @@ type Config struct {
// Target and output information.
Triple string
CPU string
Features []string
Features string
GOOS string
GOARCH string
CodeModel string
@@ -57,7 +57,6 @@ type Config struct {
DefaultStackSize uint64
NeedsStackObjects bool
Debug bool // Whether to emit debug information in the LLVM module.
LLVMFeatures string
}
// compilerContext contains function-independent data that should still be
@@ -189,12 +188,6 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
return llvm.TargetMachine{}, err
}
feat := config.Features
if len(config.LLVMFeatures) > 0 {
feat = append(feat, config.LLVMFeatures)
}
features := strings.Join(feat, ",")
var codeModel llvm.CodeModel
var relocationModel llvm.RelocMode
@@ -222,7 +215,7 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
relocationModel = llvm.RelocDynamicNoPic
}
machine := target.CreateTargetMachine(config.Triple, config.CPU, features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
machine := target.CreateTargetMachine(config.Triple, config.CPU, config.Features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
return machine, nil
}
@@ -1629,10 +1622,14 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
array := b.getValue(expr.X)
index := b.getValue(expr.Index)
// Extend index to at least uintptr size, because getelementptr assumes
// index is a signed integer.
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
// Check bounds.
arrayLen := expr.X.Type().Underlying().(*types.Array).Len()
arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
b.createLookupBoundsCheck(arrayLenLLVM, index, expr.Index.Type())
b.createLookupBoundsCheck(arrayLenLLVM, index)
// Can't load directly from array (as index is non-constant), so have to
// do it using an alloca+gep+load.
@@ -1673,8 +1670,12 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
}
// Make sure index is at least the size of uintptr becuase getelementptr
// assumes index is a signed integer.
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
// Bounds check.
b.createLookupBoundsCheck(buflen, index, expr.Index.Type())
b.createLookupBoundsCheck(buflen, index)
switch expr.X.Type().Underlying().(type) {
case *types.Pointer:
@@ -1698,9 +1699,17 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
panic("lookup on non-string?")
}
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
// correctly extend that type for two reasons:
// 1. The lookup bounds check expects an index of at least uintptr
// size.
// 2. getelementptr has signed operands, and therefore s[uint8(x)]
// can be lowered as s[int8(x)]. That would be a bug.
index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
// Bounds check.
length := b.CreateExtractValue(value, 1, "len")
b.createLookupBoundsCheck(length, index, expr.Index.Type())
b.createLookupBoundsCheck(length, index)
// Lookup byte
buf := b.CreateExtractValue(value, 0, "")
@@ -1826,13 +1835,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
if expr.Low != nil {
lowType = expr.Low.Type().Underlying().(*types.Basic)
low = b.getValue(expr.Low)
if low.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
if lowType.Info()&types.IsUnsigned != 0 {
low = b.CreateZExt(low, b.uintptrType, "")
} else {
low = b.CreateSExt(low, b.uintptrType, "")
}
}
low = b.extendInteger(low, lowType, b.uintptrType)
} else {
lowType = types.Typ[types.Uintptr]
low = llvm.ConstInt(b.uintptrType, 0, false)
@@ -1841,13 +1844,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
if expr.High != nil {
highType = expr.High.Type().Underlying().(*types.Basic)
high = b.getValue(expr.High)
if high.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
if highType.Info()&types.IsUnsigned != 0 {
high = b.CreateZExt(high, b.uintptrType, "")
} else {
high = b.CreateSExt(high, b.uintptrType, "")
}
}
high = b.extendInteger(high, highType, b.uintptrType)
} else {
highType = types.Typ[types.Uintptr]
}
@@ -1855,13 +1852,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
if expr.Max != nil {
maxType = expr.Max.Type().Underlying().(*types.Basic)
max = b.getValue(expr.Max)
if max.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
if maxType.Info()&types.IsUnsigned != 0 {
max = b.CreateZExt(max, b.uintptrType, "")
} else {
max = b.CreateSExt(max, b.uintptrType, "")
}
}
max = b.extendInteger(max, maxType, b.uintptrType)
} else {
maxType = types.Typ[types.Uintptr]
}
@@ -2285,16 +2276,16 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
case token.NEQ: // !=
result := b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
return b.CreateNot(result, ""), nil
case token.LSS: // <
case token.LSS: // x < y
return b.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil
case token.LEQ: // <=
case token.LEQ: // x <= y becomes NOT (y < x)
result := b.createRuntimeCall("stringLess", []llvm.Value{y, x}, "")
return b.CreateNot(result, ""), nil
case token.GTR: // >
case token.GTR: // x > y becomes y < x
return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
case token.GEQ: // x >= y becomes NOT (x < y)
result := b.createRuntimeCall("stringLess", []llvm.Value{x, y}, "")
return b.CreateNot(result, ""), nil
case token.GEQ: // >=
return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
default:
panic("binop on string: " + op.String())
}
@@ -2811,9 +2802,8 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
// var C.add unsafe.Pointer
// Instead of a load from the global, create a bitcast of the
// function pointer itself.
globalName := b.getGlobalInfo(unop.X.(*ssa.Global)).linkName
name := globalName[:len(globalName)-len("$funcaddr")]
fn := b.getFunction(b.fn.Pkg.Members["C."+name].(*ssa.Function))
name := strings.TrimSuffix(unop.X.(*ssa.Global).Name(), "$funcaddr")
fn := b.getFunction(b.fn.Pkg.Members[name].(*ssa.Function))
if fn.IsNil() {
return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name)
}
+52 -23
View File
@@ -18,8 +18,9 @@ import (
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
type testCase struct {
file string
target string
file string
target string
scheduler string
}
// Basic tests for the compiler. Build some Go files and compare the output with
@@ -40,29 +41,36 @@ func TestCompiler(t *testing.T) {
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
}
tests := []testCase{
{"basic.go", ""},
{"pointer.go", ""},
{"slice.go", ""},
{"string.go", ""},
{"float.go", ""},
{"interface.go", ""},
{"func.go", ""},
{"pragma.go", ""},
{"goroutine.go", "wasm"},
{"goroutine.go", "cortex-m-qemu"},
{"channel.go", ""},
{"intrinsics.go", "cortex-m-qemu"},
{"intrinsics.go", "wasm"},
{"gc.go", ""},
}
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
// Determine Go minor version (e.g. 16 in go1.16.3).
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
if err != nil {
t.Fatal("could not read Go version:", err)
}
if minor >= 17 {
tests = append(tests, testCase{"go1.17.go", ""})
// Determine which tests to run, depending on the Go and LLVM versions.
tests := []testCase{
{"basic.go", "", ""},
{"pointer.go", "", ""},
{"slice.go", "", ""},
{"string.go", "", ""},
{"float.go", "", ""},
{"interface.go", "", ""},
{"func.go", "", "coroutines"},
{"pragma.go", "", ""},
{"goroutine.go", "wasm", "asyncify"},
{"goroutine.go", "wasm", "coroutines"},
{"goroutine.go", "cortex-m-qemu", "tasks"},
{"channel.go", "", ""},
{"intrinsics.go", "cortex-m-qemu", ""},
{"intrinsics.go", "wasm", ""},
{"gc.go", "", ""},
}
if llvmMajor >= 12 {
tests = append(tests, testCase{"intrinsics.go", "cortex-m-qemu", ""})
tests = append(tests, testCase{"intrinsics.go", "wasm", ""})
}
if goMinor >= 17 {
tests = append(tests, testCase{"go1.17.go", "", ""})
}
for _, tc := range tests {
@@ -70,7 +78,10 @@ func TestCompiler(t *testing.T) {
targetString := "wasm"
if tc.target != "" {
targetString = tc.target
name = tc.file + "-" + tc.target
name += "-" + tc.target
}
if tc.scheduler != "" {
name += "-" + tc.scheduler
}
t.Run(name, func(t *testing.T) {
@@ -81,6 +92,9 @@ func TestCompiler(t *testing.T) {
if err != nil {
t.Fatal("failed to load target:", err)
}
if tc.scheduler != "" {
options.Scheduler = tc.scheduler
}
config := &compileopts.Config{
Options: options,
Target: target,
@@ -94,6 +108,7 @@ func TestCompiler(t *testing.T) {
Scheduler: config.Scheduler(),
FuncImplementation: config.FuncImplementation(),
AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.Target.DefaultStackSize,
}
machine, err := NewTargetMachine(compilerConfig)
if err != nil {
@@ -142,6 +157,9 @@ func TestCompiler(t *testing.T) {
if tc.target != "" {
outFilePrefix += "-" + tc.target
}
if tc.scheduler != "" {
outFilePrefix += "-" + tc.scheduler
}
outPath := "./testdata/" + outFilePrefix + ".ll"
// Update test if needed. Do not check the result.
@@ -189,6 +207,12 @@ func fuzzyEqualIR(s1, s2 string) bool {
// stripped out.
func filterIrrelevantIRLines(lines []string) []string {
var out []string
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
if err != nil {
// Note: this should never happen and if it does, it will always happen
// for a particular build because llvm.Version is a constant.
panic(err)
}
for _, line := range lines {
line = strings.Split(line, ";")[0] // strip out comments/info
line = strings.TrimRight(line, "\r ") // drop '\r' on Windows and remove trailing spaces from comments
@@ -198,6 +222,11 @@ func filterIrrelevantIRLines(lines []string) []string {
if strings.HasPrefix(line, "source_filename = ") {
continue
}
if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
// Ignore attribute groups. These may change between LLVM versions.
// Right now test outputs are for LLVM 12.
continue
}
out = append(out, line)
}
return out
+28 -6
View File
@@ -100,7 +100,7 @@ func (b *builder) createGo(instr *ssa.Go) {
switch b.Scheduler {
case "none", "coroutines":
// There are no additional parameters needed for the goroutine start operation.
case "tasks":
case "tasks", "asyncify":
// Add the function pointer as a parameter to start the goroutine.
params = append(params, funcPtr)
default:
@@ -112,7 +112,7 @@ func (b *builder) createGo(instr *ssa.Go) {
paramBundle := b.emitPointerPack(params)
var callee, stackSize llvm.Value
switch b.Scheduler {
case "none", "tasks":
case "none", "tasks", "asyncify":
callee = b.createGoroutineStartWrapper(funcPtr, prefix, hasContext, instr.Pos())
if b.AutomaticStackSize {
// The stack size is not known until after linking. Call a dummy
@@ -124,7 +124,7 @@ func (b *builder) createGo(instr *ssa.Go) {
} else {
// The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized.
if b.Scheduler == "tasks" && b.DefaultStackSize == 0 {
if (b.Scheduler == "tasks" || b.Scheduler == "asyncify") && b.DefaultStackSize == 0 {
b.addError(instr.Pos(), "default stack size for goroutines is not set")
}
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
@@ -170,6 +170,11 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
builder := c.ctx.NewBuilder()
defer builder.Dispose()
var deadlock llvm.Value
if c.Scheduler == "asyncify" {
deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
}
if !fn.IsAFunction().IsNil() {
// See whether this wrapper has already been created. If so, return it.
name := fn.Name()
@@ -225,6 +230,12 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
// Create the call.
builder.CreateCall(fn, params, "")
if c.Scheduler == "asyncify" {
builder.CreateCall(deadlock, []llvm.Value{
llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType),
}, "")
}
} else {
// For a function pointer like this:
//
@@ -292,11 +303,22 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
// Create the call.
builder.CreateCall(fnPtr, params, "")
if c.Scheduler == "asyncify" {
builder.CreateCall(deadlock, []llvm.Value{
llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType),
}, "")
}
}
// Finish the function. Every basic block must end in a terminator, and
// because goroutines never return a value we can simply return void.
builder.CreateRetVoid()
if c.Scheduler == "asyncify" {
// The goroutine was terminated via deadlock.
builder.CreateUnreachable()
} else {
// Finish the function. Every basic block must end in a terminator, and
// because goroutines never return a value we can simply return void.
builder.CreateRetVoid()
}
// Return a ptrtoint of the wrapper, not the function itself.
return builder.CreatePtrToInt(wrapper, c.uintptrType, "")
+15 -20
View File
@@ -208,14 +208,9 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
// present in *ssa.Function, such as the link name and whether it should be
// exported.
func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
info := functionInfo{}
if strings.HasPrefix(f.Name(), "C.") {
// Created by CGo: such a name cannot be created by regular C code.
info.linkName = f.Name()[2:]
info.exported = true
} else {
info := functionInfo{
// Pick the default linkName.
info.linkName = f.RelString(nil)
linkName: f.RelString(nil),
}
// Check for //go: pragmas, which may change the link name (among others).
info.parsePragmas(f)
@@ -346,6 +341,12 @@ func (c *compilerContext) addStandardDeclaredAttributes(llvmFn llvm.Value) {
attr := c.ctx.CreateEnumAttribute(kind, 0)
llvmFn.AddFunctionAttr(attr)
}
if c.CPU != "" {
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("target-cpu", c.CPU))
}
if c.Features != "" {
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("target-features", c.Features))
}
}
// addStandardDefinedAttributes adds the set of attributes that are added to
@@ -454,20 +455,14 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
// getGlobalInfo returns some information about a specific global.
func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo {
info := globalInfo{}
if strings.HasPrefix(g.Name(), "C.") {
// Created by CGo: such a name cannot be created by regular C code.
info.linkName = g.Name()[2:]
info.extern = true
} else {
info := globalInfo{
// Pick the default linkName.
info.linkName = g.RelString(nil)
// Check for //go: pragmas, which may change the link name (among
// others).
doc := c.astComments[info.linkName]
if doc != nil {
info.parsePragmas(doc)
}
linkName: g.RelString(nil),
}
// Check for //go: pragmas, which may change the link name (among others).
doc := c.astComments[info.linkName]
if doc != nil {
info.parsePragmas(doc)
}
return info
}
+60 -5
View File
@@ -156,12 +156,12 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
// createSyscall emits instructions for the syscall.Syscall* family of
// functions, depending on the target OS/arch.
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
syscallResult, err := b.createRawSyscall(call)
if err != nil {
return syscallResult, err
}
switch b.GOOS {
case "linux", "freebsd":
case "linux":
syscallResult, err := b.createRawSyscall(call)
if err != nil {
return syscallResult, err
}
// Return values: r0, r1 uintptr, err Errno
// Pseudocode:
// var err uintptr
@@ -180,6 +180,10 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
case "darwin":
syscallResult, err := b.createRawSyscall(call)
if err != nil {
return syscallResult, err
}
// Return values: r0, r1 uintptr, err Errno
// Pseudocode:
// var err uintptr
@@ -195,6 +199,57 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
retval = b.CreateInsertValue(retval, zero, 1, "")
retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
case "windows":
// On Windows, syscall.Syscall* is basically just a function pointer
// call. This is complicated in gc because of stack switching and the
// different ABI, but easy in TinyGo: just call the function pointer.
// The signature looks like this:
// func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
// Prepare input values.
var paramTypes []llvm.Type
var params []llvm.Value
for _, val := range call.Args[2:] {
param := b.getValue(val)
params = append(params, param)
paramTypes = append(paramTypes, param.Type())
}
llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false)
fn := b.getValue(call.Args[0])
fnPtr := b.CreateIntToPtr(fn, llvm.PointerType(llvmType, 0), "")
// Prepare some functions that will be called later.
setLastError := b.mod.NamedFunction("SetLastError")
if setLastError.IsNil() {
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
}
getLastError := b.mod.NamedFunction("GetLastError")
if getLastError.IsNil() {
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
}
// Now do the actual call. Pseudocode:
// SetLastError(0)
// r1 = trap(a1, a2, a3, ...)
// err = uintptr(GetLastError())
// return r1, 0, err
// Note that SetLastError/GetLastError could be replaced with direct
// access to the thread control block, which is probably smaller and
// faster. The Go runtime does this in assembly.
b.CreateCall(setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
syscallResult := b.CreateCall(fnPtr, params, "")
errResult := b.CreateCall(getLastError, nil, "err")
if b.uintptrType != b.ctx.Int32Type() {
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
}
// Return r1, 0, err
retval := llvm.ConstNull(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
}
+5 -4
View File
@@ -6,7 +6,8 @@ target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
%"internal/task.state" = type { i8* }
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
%"internal/task.stackState" = type { i32, i32 }
%runtime.chanSelectState = type { %runtime.channel*, i8* }
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
@@ -33,12 +34,12 @@ entry:
ret void
}
; Function Attrs: argmemonly nounwind willreturn
; Function Attrs: argmemonly nofree nosync nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
; Function Attrs: argmemonly nounwind willreturn
; Function Attrs: argmemonly nofree nosync nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
; Function Attrs: nounwind
@@ -118,4 +119,4 @@ select.body: ; preds = %select.next
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*, i8*)
attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind willreturn }
attributes #1 = { argmemonly nofree nosync nounwind willreturn }
+3 -4
View File
@@ -5,7 +5,6 @@ target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
%runtime.interfaceMethodInfo = type { i8*, i32 }
%runtime.funcValue = type { i8*, i32 }
%runtime._interface = type { i32, i8* }
@main.scalar1 = hidden global i8* null, align 4
@@ -72,11 +71,11 @@ entry:
}
; Function Attrs: nounwind
define hidden %runtime.funcValue* @main.newFuncValue(i8* %context, i8* %parentHandle) unnamed_addr #0 {
define hidden { i8*, void ()* }* @main.newFuncValue(i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef, i8* null) #0
%0 = bitcast i8* %new to %runtime.funcValue*
ret %runtime.funcValue* %0
%0 = bitcast i8* %new to { i8*, void ()* }*
ret { i8*, void ()* }* %0
}
; Function Attrs: nounwind
+210
View File
@@ -0,0 +1,210 @@
; ModuleID = 'goroutine.go'
source_filename = "goroutine.go"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
%"internal/task.stackState" = type { i32, i32 }
%runtime.chanSelectState = type { %runtime.channel*, i8* }
@"main$string" = internal unnamed_addr constant [4 x i8] c"test", align 1
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
; Function Attrs: nounwind
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.regularFunction$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 8192, i8* undef, i8* null) #0
ret void
}
declare void @main.regularFunction(i32, i8*, i8*)
declare void @runtime.deadlock(i8*, i8*)
; Function Attrs: nounwind
define linkonce_odr void @"main.regularFunction$gowrapper"(i8* %0) unnamed_addr #1 {
entry:
%unpack.int = ptrtoint i8* %0 to i32
call void @main.regularFunction(i32 %unpack.int, i8* undef, i8* undef) #0
call void @runtime.deadlock(i8* undef, i8* undef) #0
unreachable
}
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.inlineFunctionGoroutine$1$gowrapper" to i32), i8* nonnull inttoptr (i32 5 to i8*), i32 8192, i8* undef, i8* null) #0
ret void
}
; Function Attrs: nounwind
define hidden void @"main.inlineFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #2 {
entry:
%unpack.int = ptrtoint i8* %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, i8* undef, i8* undef)
call void @runtime.deadlock(i8* undef, i8* undef) #0
unreachable
}
; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0
%0 = bitcast i8* %n to i32*
store i32 3, i32* %0, align 4
%1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef, i8* null) #0
%2 = bitcast i8* %1 to i32*
store i32 5, i32* %2, align 4
%3 = getelementptr inbounds i8, i8* %1, i32 4
%4 = bitcast i8* %3 to i8**
store i8* %n, i8** %4, align 4
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"main.closureFunctionGoroutine$1$gowrapper" to i32), i8* nonnull %1, i32 8192, i8* undef, i8* null) #0
%5 = load i32, i32* %0, align 4
call void @runtime.printint32(i32 %5, i8* undef, i8* null) #0
ret void
}
; Function Attrs: nounwind
define hidden void @"main.closureFunctionGoroutine$1"(i32 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%unpack.ptr = bitcast i8* %context to i32*
store i32 7, i32* %unpack.ptr, align 4
ret void
}
; Function Attrs: nounwind
define linkonce_odr void @"main.closureFunctionGoroutine$1$gowrapper"(i8* %0) unnamed_addr #3 {
entry:
%1 = bitcast i8* %0 to i32*
%2 = load i32, i32* %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %2, i8* %5, i8* undef)
call void @runtime.deadlock(i8* undef, i8* undef) #0
unreachable
}
declare void @runtime.printint32(i32, i8*, i8*)
; Function Attrs: nounwind
define hidden void @main.funcGoroutine(i8* %fn.context, void ()* %fn.funcptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%0 = call i8* @runtime.alloc(i32 12, i8* null, i8* undef, i8* null) #0
%1 = bitcast i8* %0 to i32*
store i32 5, i32* %1, align 4
%2 = getelementptr inbounds i8, i8* %0, i32 4
%3 = bitcast i8* %2 to i8**
store i8* %fn.context, i8** %3, align 4
%4 = getelementptr inbounds i8, i8* %0, i32 8
%5 = bitcast i8* %4 to void ()**
store void ()* %fn.funcptr, void ()** %5, align 4
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @main.funcGoroutine.gowrapper to i32), i8* nonnull %0, i32 8192, i8* undef, i8* null) #0
ret void
}
; Function Attrs: nounwind
define linkonce_odr void @main.funcGoroutine.gowrapper(i8* %0) unnamed_addr #4 {
entry:
%1 = bitcast i8* %0 to i32*
%2 = load i32, i32* %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
%6 = getelementptr inbounds i8, i8* %0, i32 8
%7 = bitcast i8* %6 to void (i32, i8*, i8*)**
%8 = load void (i32, i8*, i8*)*, void (i32, i8*, i8*)** %7, align 4
call void %8(i32 %2, i8* %5, i8* undef) #0
call void @runtime.deadlock(i8* undef, i8* undef) #0
unreachable
}
; Function Attrs: nounwind
define hidden void @main.recoverBuiltinGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(i8* %dst.data, i32 %dst.len, i32 %dst.cap, i8* %src.data, i32 %src.len, i32 %src.cap, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%copy.n = call i32 @runtime.sliceCopy(i8* %dst.data, i8* %src.data, i32 %dst.len, i32 %src.len, i32 1, i8* undef, i8* null) #0
ret void
}
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
call void @runtime.chanClose(%runtime.channel* %ch, i8* undef, i8* null) #0
ret void
}
declare void @runtime.chanClose(%runtime.channel* dereferenceable_or_null(32), i8*, i8*)
; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef, i8* null) #0
%1 = bitcast i8* %0 to i8**
store i8* %itf.value, i8** %1, align 4
%2 = getelementptr inbounds i8, i8* %0, i32 4
%.repack = bitcast i8* %2 to i8**
store i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"main$string", i32 0, i32 0), i8** %.repack, align 4
%.repack1 = getelementptr inbounds i8, i8* %0, i32 8
%3 = bitcast i8* %.repack1 to i32*
store i32 4, i32* %3, align 4
%4 = getelementptr inbounds i8, i8* %0, i32 12
%5 = bitcast i8* %4 to i32*
store i32 %itf.typecode, i32* %5, align 4
call void @"internal/task.start"(i32 ptrtoint (void (i8*)* @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), i8* nonnull %0, i32 8192, i8* undef, i8* null) #0
ret void
}
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8*, i8*, i32, i32, i8*, i8*) #5
; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(i8* %0) unnamed_addr #6 {
entry:
%1 = bitcast i8* %0 to i8**
%2 = load i8*, i8** %1, align 4
%3 = getelementptr inbounds i8, i8* %0, i32 4
%4 = bitcast i8* %3 to i8**
%5 = load i8*, i8** %4, align 4
%6 = getelementptr inbounds i8, i8* %0, i32 8
%7 = bitcast i8* %6 to i32*
%8 = load i32, i32* %7, align 4
%9 = getelementptr inbounds i8, i8* %0, i32 12
%10 = bitcast i8* %9 to i32*
%11 = load i32, i32* %10, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(i8* %2, i8* %5, i32 %8, i32 %11, i8* undef, i8* undef) #0
call void @runtime.deadlock(i8* undef, i8* undef) #0
unreachable
}
attributes #0 = { nounwind }
attributes #1 = { nounwind "tinygo-gowrapper"="main.regularFunction" }
attributes #2 = { nounwind "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #3 = { nounwind "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #4 = { nounwind "tinygo-gowrapper" }
attributes #5 = { "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #6 = { nounwind "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
+3 -3
View File
@@ -18,7 +18,7 @@ entry:
ret double %0
}
; Function Attrs: nounwind readnone speculatable willreturn
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
declare double @llvm.sqrt.f64(double) #1
; Function Attrs: nounwind
@@ -28,8 +28,8 @@ entry:
ret double %0
}
; Function Attrs: nounwind readnone speculatable willreturn
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
declare double @llvm.trunc.f64(double) #1
attributes #0 = { nounwind }
attributes #1 = { nounwind readnone speculatable willreturn }
attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
+5
View File
@@ -27,3 +27,8 @@ func stringCompareUnequal(s1, s2 string) bool {
func stringCompareLarger(s1, s2 string) bool {
return s1 > s2
}
func stringLookup(s string, x uint8) byte {
// Test that x is correctly extended to an uint before comparison.
return s[x]
}
+19 -3
View File
@@ -71,11 +71,27 @@ entry:
; Function Attrs: nounwind
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%0 = call i1 @runtime.stringLess(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null) #0
%1 = xor i1 %0, true
ret i1 %1
%0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef, i8* null) #0
ret i1 %0
}
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*, i8*)
; Function Attrs: nounwind
define hidden i8 @main.stringLookup(i8* %s.data, i32 %s.len, i8 %x, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%0 = zext i8 %x to i32
%.not = icmp ult i32 %0, %s.len
br i1 %.not, label %lookup.next, label %lookup.throw
lookup.throw: ; preds = %entry
call void @runtime.lookupPanic(i8* undef, i8* null) #0
unreachable
lookup.next: ; preds = %entry
%1 = getelementptr inbounds i8, i8* %s.data, i32 %0
%2 = load i8, i8* %1, align 1
ret i8 %2
}
attributes #0 = { nounwind }
+3 -2
View File
@@ -3,7 +3,7 @@ module github.com/tinygo-org/tinygo
go 1.15
require (
github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d // indirect
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a // indirect
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693
github.com/chromedp/chromedp v0.6.4
@@ -11,7 +11,8 @@ require (
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
github.com/mattn/go-colorable v0.1.8
go.bug.st/serial v1.1.3
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
tinygo.org/x/go-llvm v0.0.0-20210907125547-fd2d62ea06be
)
+5 -6
View File
@@ -1,5 +1,5 @@
github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d h1:JeuI5/546naK5hpOIX+Lq5xE8rvt7uwiTp6iL+pLQgk=
github.com/aykevl/go-wasm v0.0.2-0.20211030161413-11881cb9032d/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a h1:QPU7APo6y/6VkCDq6HU3WWIUzER8iywSac23+1UQv60=
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a/go.mod h1:7sXyiaA0WtSogCu67R2252fQpVmJMh9JWJ9ddtGkpWw=
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/chromedp/cdproto v0.0.0-20210113043257-dabd2f2e7693 h1:11eq/RkpaotwdF6b1TRMcdgQUPNmyFEJOB7zLvh0O/Y=
@@ -45,6 +45,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -68,7 +69,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c h1:vn9IPshzYmzZis10UEVrsIBRv9FpykADw6M3/tHHROg=
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20210907125547-fd2d62ea06be h1:syIpWbi/yESuoyijF2nhRdgX4422sNfmij+o73B3+vU=
tinygo.org/x/go-llvm v0.0.0-20210907125547-fd2d62ea06be/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
+110
View File
@@ -3,12 +3,15 @@
package goenv
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strings"
)
// Keys is a slice of all available environment variable keys.
@@ -22,6 +25,12 @@ var Keys = []string{
"TINYGOROOT",
}
func init() {
if Get("GOARCH") == "arm" {
Keys = append(Keys, "GOARM")
}
}
// TINYGOROOT is the path to the final location for checking tinygo files. If
// unset (by a -X ldflag), then sourceDir() will fallback to the original build
// directory.
@@ -41,6 +50,20 @@ func Get(name string) string {
return dir
}
return runtime.GOARCH
case "GOARM":
if goarm := os.Getenv("GOARM"); goarm != "" {
return goarm
}
if goos := Get("GOOS"); goos == "windows" || goos == "android" {
// Assume Windows and Android are running on modern CPU cores.
// This matches upstream Go.
return "7"
}
// Default to ARMv6 on other devices.
// The difference between ARMv5 and ARMv6 is big, much bigger than the
// difference between ARMv6 and ARMv7. ARMv6 binaries are much smaller,
// especially when floating point instructions are involved.
return "6"
case "GOROOT":
return getGoroot()
case "GOPATH":
@@ -67,11 +90,98 @@ func Get(name string) string {
return "1"
case "TINYGOROOT":
return sourceDir()
case "WASMOPT":
if path := os.Getenv("WASMOPT"); path != "" {
err := wasmOptCheckVersion(path)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot use %q as wasm-opt (from WASMOPT environment variable): %s", path, err.Error())
os.Exit(1)
}
return path
}
return findWasmOpt()
default:
return ""
}
}
// Find wasm-opt, or exit with an error.
func findWasmOpt() string {
tinygoroot := sourceDir()
searchPaths := []string{
tinygoroot + "/bin/wasm-opt",
tinygoroot + "/build/wasm-opt",
}
var paths []string
for _, path := range searchPaths {
if runtime.GOOS == "windows" {
path += ".exe"
}
_, err := os.Stat(path)
if err != nil && os.IsNotExist(err) {
continue
}
paths = append(paths, path)
}
if path, err := exec.LookPath("wasm-opt"); err == nil {
paths = append(paths, path)
}
if len(paths) == 0 {
fmt.Fprintln(os.Stderr, "error: could not find wasm-opt, set the WASMOPT environment variable to override")
os.Exit(1)
}
errs := make([]error, len(paths))
for i, path := range paths {
err := wasmOptCheckVersion(path)
if err == nil {
return path
}
errs[i] = err
}
fmt.Fprintln(os.Stderr, "no usable wasm-opt found, update or run \"make binaryen\"")
for i, path := range paths {
fmt.Fprintf(os.Stderr, "\t%s: %s\n", path, errs[i].Error())
}
os.Exit(1)
panic("unreachable")
}
// wasmOptCheckVersion checks if a copy of wasm-opt is usable.
func wasmOptCheckVersion(path string) error {
cmd := exec.Command(path, "--version")
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return err
}
str := buf.String()
if strings.Contains(str, "(") {
// The git tag may be placed in parentheses after the main version string.
str = strings.Split(str, "(")[0]
}
str = strings.TrimSpace(str)
var ver uint
_, err = fmt.Sscanf(str, "wasm-opt version %d", &ver)
if err != nil || ver < 102 {
return errors.New("incompatible wasm-opt (need 102 or newer)")
}
return nil
}
// Return the TINYGOROOT, or exit with an error.
func sourceDir() string {
// Use $TINYGOROOT as root, if available.
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo.
// Update this value before release of new version of software.
const Version = "0.21.0-dev"
const Version = "0.22.0-dev"
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
+6
View File
@@ -145,6 +145,9 @@ func Run(mod llvm.Module, debug bool) error {
if obj.buffer == nil {
continue
}
if obj.constant {
continue // constant buffers can't have been modified
}
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
if err == errInvalidPtrToIntSize {
// This can happen when a previous interp run did not have the
@@ -248,6 +251,9 @@ func RunFunc(fn llvm.Value, debug bool) error {
if obj.buffer == nil {
continue
}
if obj.constant {
continue // constant, so can't have been modified
}
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
if err != nil {
return err
+1
View File
@@ -71,6 +71,7 @@ func runTest(t *testing.T, pathPrefix string) {
defer pm.Dispose()
pm.AddGlobalOptimizerPass()
pm.AddDeadStoreEliminationPass()
pm.AddAggressiveDCEPass()
pm.Run(mod)
// Read the expected output IR.
+5
View File
@@ -941,6 +941,7 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
agg := operands[0]
for i := 0; i < len(indices)-1; i++ {
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg")
mem.instructions = append(mem.instructions, agg)
}
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
case llvm.InsertValue:
@@ -953,11 +954,15 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
for i := 0; i < len(indices)-1; i++ {
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i))
aggregates = append(aggregates, agg)
mem.instructions = append(mem.instructions, agg)
}
result = operands[1]
for i := len(indices) - 1; i >= 0; i-- {
agg := aggregates[i]
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
if i != 0 { // don't add last result to mem.instructions as it will be done at the end already
mem.instructions = append(mem.instructions, result)
}
}
case llvm.Add:
+6 -1
View File
@@ -41,6 +41,7 @@ type object struct {
globalName string // name, if not yet created (not guaranteed to be the final name)
buffer value // buffer with value as given by interp, nil if external
size uint32 // must match buffer.len(), if available
constant bool // true if this is a constant global
marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
}
@@ -223,7 +224,7 @@ func (mv *memoryView) hasExternalLoadOrStore(v pointerValue) bool {
// possible for the interpreter to read from the object.
func (mv *memoryView) hasExternalStore(v pointerValue) bool {
obj := mv.get(v.index())
return obj.marked >= 2
return obj.marked >= 2 && !obj.constant
}
// get returns an object that can only be read from, as it may return an object
@@ -263,6 +264,9 @@ func (mv *memoryView) put(index uint32, obj object) {
if checks && mv.get(index).buffer.len(mv.r) != obj.buffer.len(mv.r) {
panic("put() with a differently-sized object")
}
if checks && obj.constant {
panic("interp: store to a constant")
}
mv.objects[index] = obj
}
@@ -1138,6 +1142,7 @@ func (r *runner) getValue(llvmValue llvm.Value) value {
obj.size = uint32(r.targetData.TypeAllocSize(llvmValue.Type().ElementType()))
if initializer := llvmValue.Initializer(); !initializer.IsNil() {
obj.buffer = r.getValue(initializer)
obj.constant = llvmValue.IsGlobalConstant()
}
} else if !llvmValue.IsAFunction().IsNil() {
// OK
+8
View File
@@ -6,6 +6,7 @@ target triple = "x86_64--linux"
@main.nonConst2 = global i64 0
@main.someArray = global [8 x {i16, i32}] zeroinitializer
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exportedConst = constant i64 42
@main.exposedValue1 = global i16 0
@main.exposedValue2 = global i16 0
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
@@ -62,6 +63,11 @@ entry:
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
store i16 5, i16* @main.exposedValue1
; Test that marking a constant as external still allows loading from it.
call void @readExternal(i32* bitcast (i64* @main.exportedConst to i32*))
%constLoad = load i64, i64 * @main.exportedConst
call void @runtime.printint64(i64 %constLoad)
; Test that this even propagates through functions.
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2
@@ -96,6 +102,8 @@ declare i64 @someValue()
declare void @modifyExternal(i32*)
declare void @readExternal(i32*)
; This function will modify an external value. By passing this function as a
; function pointer to an external function, @main.exposedValue2 should be
; marked as external.
+5
View File
@@ -5,6 +5,7 @@ target triple = "x86_64--linux"
@main.nonConst2 = local_unnamed_addr global i64 0
@main.someArray = global [8 x { i16, i32 }] zeroinitializer
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
@main.exportedConst = constant i64 42
@main.exposedValue1 = global i16 0
@main.exposedValue2 = local_unnamed_addr global i16 0
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
@@ -24,6 +25,8 @@ entry:
call void @modifyExternal(i32* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ([8 x { i16, i32 }]* @main.someArray to i8*), i32 28) to i32*))
call void @modifyExternal(i32* bitcast ([1 x i16*]* @main.exportedValue to i32*))
store i16 5, i16* @main.exposedValue1, align 2
call void @readExternal(i32* bitcast (i64* @main.exportedConst to i32*))
call void @runtime.printint64(i64 42)
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2, align 2
call void @modifyExternal(i32* bitcast (void ()* @hasInlineAsm to i32*))
@@ -54,6 +57,8 @@ declare i64 @someValue() local_unnamed_addr
declare void @modifyExternal(i32*) local_unnamed_addr
declare void @readExternal(i32*) local_unnamed_addr
define void @willModifyGlobal() {
entry:
store i16 8, i16* @main.exposedValue2, align 2
+11
View File
@@ -5,9 +5,12 @@ declare void @externalCall(i64)
@foo.knownAtRuntime = global i64 0
@bar.knownAtRuntime = global i64 0
@baz.someGlobal = external global [3 x {i64, i32}]
@baz.someInt = global i32 0
define void @runtime.initAll() unnamed_addr {
entry:
call void @baz.init(i8* undef, i8* undef)
call void @foo.init(i8* undef, i8* undef)
call void @bar.init(i8* undef, i8* undef)
call void @main.init(i8* undef, i8* undef)
@@ -25,6 +28,14 @@ define internal void @bar.init(i8* %context, i8* %parentHandle) unnamed_addr {
ret void
}
define internal void @baz.init(i8* %context, i8* %parentHandle) unnamed_addr {
; Test extractvalue/insertvalue with more than one index.
%val = load [3 x {i64, i32}], [3 x {i64, i32}]* @baz.someGlobal
%part = extractvalue [3 x {i64, i32}] %val, 0, 1
%val2 = insertvalue [3 x {i64, i32}] %val, i32 5, 2, 1
unreachable ; trigger revert
}
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
call void @externalCall(i64 3)
+7
View File
@@ -3,11 +3,14 @@ target triple = "x86_64--linux"
@foo.knownAtRuntime = local_unnamed_addr global i64 0
@bar.knownAtRuntime = local_unnamed_addr global i64 0
@baz.someGlobal = external local_unnamed_addr global [3 x { i64, i32 }]
@baz.someInt = local_unnamed_addr global i32 0
declare void @externalCall(i64) local_unnamed_addr
define void @runtime.initAll() unnamed_addr {
entry:
call fastcc void @baz.init(i8* undef, i8* undef)
call fastcc void @foo.init(i8* undef, i8* undef)
%val = load i64, i64* @foo.knownAtRuntime, align 8
store i64 %val, i64* @bar.knownAtRuntime, align 8
@@ -19,3 +22,7 @@ define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_a
store i64 5, i64* @foo.knownAtRuntime, align 8
unreachable
}
define internal fastcc void @baz.init(i8* %context, i8* %parentHandle) unnamed_addr {
unreachable
}
Submodule
+1
Submodule lib/binaryen added at 96f7acf09a
Submodule
+1
Submodule lib/mingw-w64 added at acc9b9d9eb
+1
View File
@@ -228,6 +228,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
"internal/bytealg/": false,
"internal/reflectlite/": false,
"internal/task/": false,
"internal/itoa/": false, // TODO: Remove when we drop support for go 1.16
"machine/": false,
"net/": true,
"os/": true,
+76 -35
View File
@@ -177,7 +177,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
// Test runs the tests in the given package. Returns whether the test passed and
// possibly an error if the test failed to run.
func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVerbose, testShort bool, outpath string) (bool, error) {
func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVerbose, testShort bool, testRunRegexp string, outpath string) (bool, error) {
options.TestConfig.CompileTestBinary = true
config, err := builder.NewConfig(options)
if err != nil {
@@ -203,7 +203,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVer
// Run the test.
start := time.Now()
var err error
passed, err = runPackageTest(config, result, testVerbose, testShort)
passed, err = runPackageTest(config, result, testVerbose, testShort, testRunRegexp)
if err != nil {
return err
}
@@ -229,7 +229,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVer
// runPackageTest runs a test binary that was previously built. The return
// values are whether the test passed and any errors encountered while trying to
// run the binary.
func runPackageTest(config *compileopts.Config, result builder.BuildResult, testVerbose, testShort bool) (bool, error) {
func runPackageTest(config *compileopts.Config, result builder.BuildResult, testVerbose, testShort bool, testRunRegexp string) (bool, error) {
var cmd *exec.Cmd
if len(config.Target.Emulator) == 0 {
// Run directly.
@@ -240,12 +240,28 @@ func runPackageTest(config *compileopts.Config, result builder.BuildResult, test
if testShort {
flags = append(flags, "-test.short")
}
if testRunRegexp != "" {
flags = append(flags, "-test.run="+testRunRegexp)
}
cmd = executeCommand(config.Options, result.Binary, flags...)
cmd.Dir = result.MainDir
} else {
// Run in an emulator.
// TODO: pass the -test.v flag if needed.
args := append(config.Target.Emulator[1:], result.Binary)
if config.Target.Emulator[0] == "wasmtime" {
// allow reading from current directory: --dir=.
// mark end of wasmtime arguments and start of program ones: --
args = append(args, "--dir=.", "--")
if testVerbose {
args = append(args, "-test.v")
}
if testShort {
args = append(args, "-test.short")
}
if testRunRegexp != "" {
args = append(args, "-test.run="+testRunRegexp)
}
}
cmd = executeCommand(config.Options, config.Target.Emulator[0], args...)
}
cmd.Stdout = os.Stdout
@@ -940,22 +956,39 @@ func getBMPPorts() (gdbPort, uartPort string, err error) {
}
}
func usage() {
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
fmt.Fprintln(os.Stderr, "version:", goenv.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")
fmt.Fprintln(os.Stderr, " run: compile and run immediately")
fmt.Fprintln(os.Stderr, " test: test packages")
fmt.Fprintln(os.Stderr, " flash: compile and flash to the device")
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
fmt.Fprintln(os.Stderr, " list: run go list using the TinyGo root")
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
fmt.Fprintln(os.Stderr, " help: print this help text")
fmt.Fprintln(os.Stderr, "\nflags:")
flag.PrintDefaults()
func usage(command string) {
version := goenv.Version
if strings.HasSuffix(version, "-dev") && gitSha1 != "" {
version += "-" + gitSha1
}
switch command {
default:
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> [arguments]\n", os.Args[0])
fmt.Fprintln(os.Stderr, "\ncommands:")
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
fmt.Fprintln(os.Stderr, " run: compile and run immediately")
fmt.Fprintln(os.Stderr, " test: test packages")
fmt.Fprintln(os.Stderr, " flash: compile and flash to the device")
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
fmt.Fprintln(os.Stderr, " lldb: run/flash and immediately enter LLDB")
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
fmt.Fprintln(os.Stderr, " list: run go list using the TinyGo root")
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
fmt.Fprintln(os.Stderr, " targets: list targets")
fmt.Fprintln(os.Stderr, " info: show info for specified target")
fmt.Fprintln(os.Stderr, " version: show version")
fmt.Fprintln(os.Stderr, " help: print this help text")
if flag.Parsed() {
fmt.Fprintln(os.Stderr, "\nflags:")
flag.PrintDefaults()
}
fmt.Fprintln(os.Stderr, "\nfor more details, see https://tinygo.org/docs/reference/usage/")
}
}
// try to make the path relative to the current working directory. If any error
@@ -1099,7 +1132,7 @@ func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
usage()
usage("")
os.Exit(1)
}
command := os.Args[1]
@@ -1107,7 +1140,7 @@ func main() {
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)")
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
scheduler := flag.String("scheduler", "", "which scheduler to use (none, coroutines, tasks)")
scheduler := flag.String("scheduler", "", "which scheduler to use (none, coroutines, tasks, asyncify)")
serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
printIR := flag.Bool("printir", false, "print LLVM IR")
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
@@ -1140,10 +1173,12 @@ func main() {
flag.StringVar(&outpath, "o", "", "output filename")
}
var testCompileOnlyFlag, testVerboseFlag, testShortFlag *bool
var testRunRegexp *string
if command == "help" || command == "test" {
testCompileOnlyFlag = flag.Bool("c", false, "compile the test binary but do not run it")
testVerboseFlag = flag.Bool("v", false, "verbose: print additional output")
testShortFlag = flag.Bool("short", false, "short: run smaller test suite to save time")
testRunRegexp = flag.String("run", "", "run: regexp of tests to run")
}
// Early command processing, before commands are interpreted by the Go flag
@@ -1182,6 +1217,7 @@ func main() {
options := &compileopts.Options{
GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
Target: *target,
Opt: *opt,
GC: *gc,
@@ -1210,7 +1246,7 @@ func main() {
err = options.Verify()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
usage()
usage(command)
os.Exit(1)
}
@@ -1232,7 +1268,7 @@ func main() {
case "build":
if outpath == "" {
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
usage()
usage(command)
os.Exit(1)
}
pkgName := "."
@@ -1240,7 +1276,7 @@ func main() {
pkgName = filepath.ToSlash(flag.Arg(0))
} else if flag.NArg() > 1 {
fmt.Fprintln(os.Stderr, "build only accepts a single positional argument: package name, but multiple were specified")
usage()
usage(command)
os.Exit(1)
}
if options.Target == "" && filepath.Ext(outpath) == ".wasm" {
@@ -1253,7 +1289,7 @@ func main() {
// Note: this command is only meant to be used while making a release!
if outpath == "" {
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
usage()
usage(command)
os.Exit(1)
}
if *target == "" {
@@ -1261,7 +1297,7 @@ func main() {
}
if flag.NArg() != 1 {
fmt.Fprintf(os.Stderr, "Build-library only accepts exactly one library name as argument, %d given\n", flag.NArg())
usage()
usage(command)
os.Exit(1)
}
var lib *builder.Library
@@ -1299,7 +1335,7 @@ func main() {
} else {
if !options.Debug {
fmt.Fprintln(os.Stderr, "Debug disabled while running debugger?")
usage()
usage(command)
os.Exit(1)
}
err := Debug(command, pkgName, *ocdOutput, options)
@@ -1308,7 +1344,7 @@ func main() {
case "run":
if flag.NArg() != 1 {
fmt.Fprintln(os.Stderr, "No package specified.")
usage()
usage(command)
os.Exit(1)
}
pkgName := filepath.ToSlash(flag.Arg(0))
@@ -1325,7 +1361,7 @@ func main() {
allTestsPassed := true
for _, pkgName := range pkgNames {
// TODO: parallelize building the test binaries
passed, err := Test(pkgName, options, *testCompileOnlyFlag, *testVerboseFlag, *testShortFlag, outpath)
passed, err := Test(pkgName, options, *testCompileOnlyFlag, *testVerboseFlag, *testShortFlag, *testRunRegexp, outpath)
handleCompilerError(err)
if !passed {
allTestsPassed = false
@@ -1369,13 +1405,13 @@ func main() {
options.Target = flag.Arg(0)
} else if flag.NArg() > 1 {
fmt.Fprintln(os.Stderr, "only one target name is accepted")
usage()
usage(command)
os.Exit(1)
}
config, err := builder.NewConfig(options)
if err != nil {
fmt.Fprintln(os.Stderr, err)
usage()
usage(command)
os.Exit(1)
}
config.GoMinorVersion = 0 // this avoids creating the list of Go1.x build tags.
@@ -1391,6 +1427,7 @@ func main() {
fmt.Printf("LLVM triple: %s\n", config.Triple())
fmt.Printf("GOOS: %s\n", config.GOOS())
fmt.Printf("GOARCH: %s\n", config.GOARCH())
fmt.Printf("GOARM: %s\n", config.GOARM())
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
fmt.Printf("garbage collector: %s\n", config.GC())
fmt.Printf("scheduler: %s\n", config.Scheduler())
@@ -1399,7 +1436,7 @@ func main() {
config, err := builder.NewConfig(options)
if err != nil {
fmt.Fprintln(os.Stderr, err)
usage()
usage(command)
os.Exit(1)
}
var extraArgs []string
@@ -1435,7 +1472,11 @@ func main() {
os.Exit(1)
}
case "help":
usage()
command := ""
if flag.NArg() >= 1 {
command = flag.Arg(0)
}
usage(command)
case "version":
goversion := "<unknown>"
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
@@ -1460,7 +1501,7 @@ func main() {
}
default:
fmt.Fprintln(os.Stderr, "Unknown command:", command)
usage()
usage("")
os.Exit(1)
}
}
+69 -48
View File
@@ -71,52 +71,12 @@ func TestCompiler(t *testing.T) {
return
}
if runtime.GOOS != "windows" {
t.Run("Host", func(t *testing.T) {
runPlatTests(optionsFromTarget(""), tests, t)
})
}
if testing.Short() {
return
}
t.Run("EmulatedCortexM3", func(t *testing.T) {
runPlatTests(optionsFromTarget("cortex-m-qemu"), tests, t)
t.Run("Host", func(t *testing.T) {
runPlatTests(optionsFromTarget(""), tests, t)
})
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
// Note: running only on Windows and macOS because Linux (as of 2020)
// usually has an outdated QEMU version that doesn't support RISC-V yet.
t.Run("EmulatedRISCV", func(t *testing.T) {
runPlatTests(optionsFromTarget("riscv-qemu"), tests, t)
})
}
if runtime.GOOS == "linux" {
t.Run("X86Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux", "386"), tests, t)
})
t.Run("ARMLinux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux", "arm"), tests, t)
})
t.Run("ARM64Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux", "arm64"), tests, t)
})
t.Run("WebAssembly", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasm"), tests, t)
})
t.Run("WASI", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasi"), tests, t)
})
}
// Test a few build options.
t.Run("build-options", func(t *testing.T) {
if runtime.GOOS == "windows" {
// These tests assume a host that is supported by TinyGo.
t.Skip("can't test build options on Windows")
}
t.Parallel()
// Test with few optimizations enabled (no inlining, etc).
@@ -125,6 +85,7 @@ func TestCompiler(t *testing.T) {
runTestWithConfig("stdlib.go", t, compileopts.Options{
GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
Opt: "1",
}, nil, nil)
})
@@ -136,6 +97,7 @@ func TestCompiler(t *testing.T) {
runTestWithConfig("print.go", t, compileopts.Options{
GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
Opt: "0",
}, nil, nil)
})
@@ -145,6 +107,7 @@ func TestCompiler(t *testing.T) {
runTestWithConfig("ldflags.go", t, compileopts.Options{
GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
GlobalValues: map[string]map[string]string{
"main": {
"someGlobal": "foobar",
@@ -153,6 +116,38 @@ func TestCompiler(t *testing.T) {
}, nil, nil)
})
})
if testing.Short() {
// Don't test other targets when the -short flag is used. Only test the
// host system.
return
}
t.Run("EmulatedCortexM3", func(t *testing.T) {
runPlatTests(optionsFromTarget("cortex-m-qemu"), tests, t)
})
t.Run("EmulatedRISCV", func(t *testing.T) {
runPlatTests(optionsFromTarget("riscv-qemu"), tests, t)
})
if runtime.GOOS == "linux" {
t.Run("X86Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/386"), tests, t)
})
t.Run("ARMLinux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/arm/6"), tests, t)
})
t.Run("ARM64Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/arm64"), tests, t)
})
t.Run("WebAssembly", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasm"), tests, t)
})
t.Run("WASI", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasi"), tests, t)
})
}
}
func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
@@ -169,6 +164,14 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
t.Parallel()
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
})
if options.Target == "wasi" || options.Target == "wasm" {
t.Run("alias.go-scheduler-none", func(t *testing.T) {
t.Parallel()
options := compileopts.Options(options)
options.Scheduler = "none"
runTest("alias.go", options, t, nil, nil)
})
}
if options.Target == "" || options.Target == "wasi" {
t.Run("filesystem.go", func(t *testing.T) {
t.Parallel()
@@ -200,15 +203,24 @@ func optionsFromTarget(target string) compileopts.Options {
// GOOS/GOARCH are only used if target == ""
GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
Target: target,
}
}
func optionsFromOSARCH(goos, goarch string) compileopts.Options {
return compileopts.Options{
GOOS: goos,
GOARCH: goarch,
// optionsFromOSARCH returns a set of options based on the "osarch" string. This
// string is in the form of "os/arch/subarch", with the subarch only sometimes
// being necessary. Examples are "darwin/amd64" or "linux/arm/7".
func optionsFromOSARCH(osarch string) compileopts.Options {
parts := strings.Split(osarch, "/")
options := compileopts.Options{
GOOS: parts[0],
GOARCH: parts[1],
}
if options.GOARCH == "arm" {
options.GOARM = parts[2]
}
return options
}
func runTest(name string, options compileopts.Options, t *testing.T, cmdArgs, environmentVars []string) {
@@ -278,6 +290,9 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
// Build the test binary.
binary := filepath.Join(tmpdir, "test")
if spec.GOOS == "windows" {
binary += ".exe"
}
err = runBuild("./"+path, binary, &options)
if err != nil {
printCompilerError(t.Log, err)
@@ -319,7 +334,13 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
}
go func() {
// Terminate the process if it runs too long.
timer := time.NewTimer(10 * time.Second)
maxDuration := 10 * time.Second
if runtime.GOOS == "windows" {
// For some reason, tests on Windows can take around
// 30s to complete. TODO: investigate why and fix this.
maxDuration = 40 * time.Second
}
timer := time.NewTimer(maxDuration)
select {
case <-runComplete:
timer.Stop()
@@ -349,7 +370,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
t.Log("failed to run:", err)
fail = true
} else if !bytes.Equal(expected, actual) {
t.Log("output did not match")
t.Logf("output did not match (expected %d bytes, got %d bytes):", len(expected), len(actual))
fail = true
}
+218
View File
@@ -0,0 +1,218 @@
package main
import (
"bufio"
"bytes"
"crypto/md5"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"golang.org/x/sync/errgroup"
)
func main() {
threads := runtime.NumCPU()
if j, ok := os.LookupEnv(`RUN_SMOKETEST_JOBS`); ok {
n, err := strconv.ParseInt(j, 0, 0)
if err == nil {
threads = int(n)
}
}
flag.IntVar(&threads, "threads", threads, "threads of make smoketest")
flag.Parse()
if len(os.Args) < 2 {
fmt.Printf("usage: %s make smoketest\n", filepath.Base(os.Args[0]))
os.Exit(1)
}
commands, err := getMakeCommands(flag.Args())
if err != nil {
log.Fatalf(err.Error())
}
err = run(commands, threads)
if err != nil {
log.Fatalf(err.Error())
}
}
func getMakeCommands(args []string) ([]string, error) {
var buf bytes.Buffer
cmd := exec.Command(args[0], args[1:]...)
cmd.Args = append(cmd.Args, "-n")
cmd.Stdout = &buf
cmd.Stderr = &buf
err := cmd.Run()
if err != nil {
return nil, err
}
commands := []string{}
scanner := bufio.NewScanner(&buf)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if filepath.Base(fields[0]) == "tinygo" && (fields[1] == "build" || fields[1] == "version") {
commands = append(commands, line)
} else if strings.HasPrefix(line, "#") {
commands = append(commands, line)
}
}
return commands, nil
}
func run(commands []string, threads int) error {
ch := make(chan string, 1000)
o := NewOchan(ch, 100)
go func() {
limit := make(chan struct{}, threads)
var eg errgroup.Group
for _, command := range commands {
command := command
select {
case limit <- struct{}{}:
och := o.GetCh()
eg.Go(func() error {
var err error
if runtime.GOOS == `windows` {
command = convertToWindowsPath(command)
}
fields := strings.Fields(command)
if filepath.Base(fields[0]) == "tinygo" && fields[1] == "build" {
err = buildAndmd5sum(command, och)
} else if strings.HasPrefix(command, "#") {
och <- fmt.Sprintf("%s\n", command)
} else {
err = runCommand(command, och)
}
close(och)
<-limit
return err
})
}
}
if err := eg.Wait(); err != nil {
log.Fatal(err)
}
o.Wait()
close(ch)
}()
for s := range ch {
fmt.Print(s)
}
return nil
}
func runCommand(command string, ch chan string) error {
var buf bytes.Buffer
fmt.Fprintf(&buf, "%s\n", command)
args := strings.Fields(command)
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = &buf
cmd.Stderr = &buf
err := cmd.Run()
if err != nil {
ch <- buf.String()
return err
}
ch <- buf.String()
return nil
}
func buildAndmd5sum(command string, ch chan string) error {
var buf bytes.Buffer
fmt.Fprintf(&buf, "%s\n", command)
args := strings.Fields(command)
output := ""
tmpOutput := ""
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
return err
}
for i := range args {
if args[i] == "-o" {
output = args[i+1]
ext := filepath.Ext(output)
tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext))
args[i+1] = tmpOutput
} else if strings.HasPrefix(args[i], "-o=") {
output = args[i][3:]
ext := filepath.Ext(output)
tmpOutput = filepath.Join(tmpdir, fmt.Sprintf("%s.%s", filepath.Base(output), ext))
args[i] = fmt.Sprintf("-o=%s", tmpOutput)
}
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
if err != nil {
ch <- buf.String()
return err
}
md5str, err := calcMD5(tmpOutput)
if err != nil {
ch <- buf.String()
return err
}
fmt.Fprintf(&buf, "%s %s\n", md5str, output)
ch <- buf.String()
return nil
}
func calcMD5(f string) (string, error) {
r, err := os.Open(f)
if err != nil {
return "", err
}
defer r.Close()
h := md5.New()
if _, err := io.Copy(h, r); err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
func convertToWindowsPath(command string) string {
if !strings.HasPrefix(command, `/`) {
return command
}
command = fmt.Sprintf("%c:%s", command[1], command[2:])
return command
}
+71
View File
@@ -0,0 +1,71 @@
package main
import (
"sync"
)
// An Ochan is a structure for controlling the output order of channels.
type Ochan struct {
out chan string
in chan chan string
done chan struct{}
wg sync.WaitGroup
size int
}
// NewOchan returns a new Ochan struct with specified buffer capacity.
func NewOchan(out chan string, size int) *Ochan {
o := &Ochan{
out: out,
in: make(chan chan string, size),
done: make(chan struct{}, 1),
wg: sync.WaitGroup{},
size: size,
}
go func(o *Ochan) {
for {
select {
case ch, ok := <-o.in:
if !ok {
return
}
for s := range ch {
o.out <- s
}
o.wg.Done()
case <-o.done:
return
}
}
}(o)
return o
}
// GetCh returns a next input channel. The input channel must be explicitly
// closed after use.
func (o *Ochan) GetCh() chan string {
ch := make(chan string, o.size)
o.in <- ch
o.wg.Add(1)
return ch
}
// SetSize sets the capacity of the channel returned by GetCh.
func (o *Ochan) SetSize(size int) {
o.size = size
}
// Wait blocks until it retrieves data from all input channel. All input
// channels must be closed before calling this function.
func (o *Ochan) Wait() error {
o.wg.Wait()
return nil
}
// Close closed ochan's goroutine.
func (o *Ochan) Close() {
close(o.done)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// +build darwin freebsd tinygo.wasm
// +build darwin tinygo.wasm
// This implementation of crypto/rand uses the arc4random_buf function
// (available on both MacOS and WASI) to generate random numbers.
+42
View File
@@ -0,0 +1,42 @@
package rand
import "errors"
func init() {
Reader = &reader{}
}
type reader struct {
}
var errRandom = errors.New("failed to obtain random data from rand_s")
func (r *reader) Read(b []byte) (n int, err error) {
if len(b) == 0 {
return
}
var randomByte uint32
for i := range b {
// Call rand_s every four bytes because it's a C int (always 32-bit in
// Windows).
if i%4 == 0 {
errCode := libc_rand_s(&randomByte)
if errCode != 0 {
// According to the documentation, it can return an error.
return n, errRandom
}
} else {
randomByte >>= 8
}
b[i] = byte(randomByte)
}
return len(b), nil
}
// Cryptographically secure random number generator.
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s?view=msvc-170
// errno_t rand_s(unsigned int* randomValue);
//export rand_s
func libc_rand_s(randomValue *uint32) int32
+1
View File
@@ -2,6 +2,7 @@
// Hardfault aliases for definitions that have inconsistent naming (which are
// auto-generated by gen-device-svd.go) among devices in package nxp.
//go:build nxp && !mimxrt1062
// +build nxp,!mimxrt1062
package nxp
+69 -164
View File
@@ -2,12 +2,12 @@
// Type definitions, fields, and constants associated with various clocks and
// peripherals of the NXP MIMXRT1062.
//go:build nxp && mimxrt1062
// +build nxp,mimxrt1062
package nxp
import (
"device/arm"
"runtime/volatile"
"unsafe"
)
@@ -378,6 +378,30 @@ func (clk Clock) setCcm(value uint32) {
}
}
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// PLL configuration for ARM
type ClockConfigArmPll struct {
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
@@ -448,178 +472,59 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
setSysPfd(pfd...)
}
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
// PHY configuration for USB HS
type ClockConfigUsbPhy struct {
Instance uint8 // USB PHY number (1 or 2)
XtalFreq uint32 // External reference clock frequency (Hz)
DCal uint32 // Decode to trim nominal 17.78mA current source
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
PllConfig ClockConfigUsbPll
}
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
// the USB +3V regulator (PMU), for use as either USB host or device.
func (cfg ClockConfigUsbPhy) Configure() {
var (
usb *USB_Type
phy *USBPHY_Type
chrgDetectReg *volatile.Register32
chrgDetectMsk uint32
)
// Select appropriate peripherals based on receiver Instance
switch cfg.Instance {
case 1:
usb = USB1 // Select USB1 HS PHY/PLL
phy = USBPHY1 //
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
case 2:
usb = USB2 // Select USB2 HS PHY/PLL
phy = USBPHY2 //
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
default:
panic("nxp: invalid USB PHY")
}
// Configure and enable USB PLL clocks
cfg.PllConfig.Configure()
// Release PHY from reset
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
// Enable power to USB PHY
phy.PWD.Set(0)
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
// enable support for low-speed device connection, direct and indirect (hub)
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
// Enable USB HS clocks gate
ClockIpUsbOh3.Enable(true)
// Reset USB peripheral
usb.USBCMD.SetBits(USB_USBCMD_RST)
// Add a delay after RST to ensure there is a USB D+ pullup sequence
nopDelay(400000)
// Enable USB LDO
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
// check whether we are connected to USB charger
chrgDetectReg.Set(chrgDetectMsk)
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
phy.TX.Set((phy.TX.Get() &
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
}
// PLL configuration for USB
type ClockConfigUsbPll struct {
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
Src uint8 // Pll clock source, reference _clock_pll_clk_src
}
func (cfg ClockConfigUsbPll) Configure() {
// select USB peripheral registers based on receiver's Instance
switch cfg.Instance {
case 1: // USB1 PLL
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
// PLL already configured, enable USB clocks
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
} else {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS | src)
// reconfigure PLL
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
}
// disable bypass
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
// update PFDs (if provided)
if nil != cfg.Pfd {
setUsb1Pfd(cfg.Pfd...)
}
switch cfg.Instance {
case 1:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB1_SET.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
}
case 2: // USB2 PLL
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
// PLL already configured, enable USB clocks
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
} else {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS | src)
// reconfigure PLL
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
// disable bypass
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
// update PFDs after update
setUsb1Pfd(pfd...)
case 2:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
default:
panic("nxp: invalid USB PLL")
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// We cannot use the sleep timer from this context (import cycle), but we need
// an approximate method to spin CPU cycles for short periods of time.
// go:inline
func nopDelay(cycles uint32) {
for i := uint32(0); i < cycles; i++ {
arm.Asm(`nop`)
}
}
+1
View File
@@ -2,6 +2,7 @@
// Hardfault aliases for definitions that have inconsistent naming (which are
// auto-generated by gen-device-svd.go) among devices in package nxp.
//go:build nxp && mimxrt1062
// +build nxp,mimxrt1062
package nxp
-80
View File
@@ -278,83 +278,3 @@ func enableDcache(enable bool) {
}
}
}
// FlushDcache flushes data from cache to memory
//
// Normally FlushDcache is used when metadata written to memory will be used by
// a DMA or a bus-controller peripheral. Any data in the cache is written to
// memory. A copy remains in the cache, so this is typically used with special
// fields you will want to quickly access in the future. For data transmission,
// use FlushDeleteDcache.
//go:inline
func FlushDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCCMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
// DeleteDcache deletes data from the cache, without touching memory.
//
// Normally DeleteDcache is used before receiving data via DMA or from
// bus-controller peripherals which write to memory. You want to delete anything
// the cache may have stored, so your next read is certain to access the
// physical memory.
//go:inline
func DeleteDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCIMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
// FlushDeleteDcache flushes data from cache to memory, and delete it from the
// cache
//
// Normally FlushDeleteDcache is used when transmitting data via DMA or
// bus-controller peripherals which read from memory. You want any cached data
// written to memory, and then removed from the cache, because you no longer
// need to access the data after transmission.
//go:inline
func FlushDeleteDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCCIMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
+1
View File
@@ -1,6 +1,7 @@
// Hand created file. DO NOT DELETE.
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
//go:build sam && atsamd51
// +build sam,atsamd51
// These are the supported pchctrl function numberings on the atsamd51x
+1
View File
@@ -1,6 +1,7 @@
// Hand created file. DO NOT DELETE.
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
//go:build sam && atsame5x
// +build sam,atsame5x
// These are the supported pchctrl function numberings on the atsamd51x
View File
-251
View File
@@ -1,251 +0,0 @@
package main
import (
"machine"
"machine/usb"
"time"
)
var keyboard = machine.HID0.Keyboard()
func main() {
println("USB HID keyboard demo")
for {
time.Sleep(5 * time.Second)
// Open a new text editor
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeySpace)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("kate"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(5 * time.Second)
// Use the io.Writer interface
keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n"))
time.Sleep(2 * time.Second)
// Or manually specify keycodes and Unicode codepoints
testKeys([]Key{
// Print alphabet out-of-order
{Press: usb.KeyX},
{Press: usb.KeyY},
{Press: usb.KeyZ},
{Press: usb.KeyG},
{Press: usb.KeyH},
{Press: usb.KeyI},
{Press: usb.KeyJ},
{Press: usb.KeyK},
{Press: usb.KeyL},
{Press: usb.KeyM},
{Press: usb.KeyN},
{Press: usb.KeyO},
{Press: usb.KeyP},
{Press: usb.KeyQ},
{Press: usb.KeyR},
{Press: usb.KeyS},
{Press: usb.KeyT},
{Press: usb.KeyA},
{Press: usb.KeyB},
{Press: usb.KeyC},
{Press: usb.KeyD},
{Press: usb.KeyE},
{Press: usb.KeyF},
{Press: usb.KeyU},
{Press: usb.KeyV},
{Press: usb.KeyW},
// Pause 1 second
{Time: time.Second},
// Move cursor left x3
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
// Pause 1 second
{Time: time.Second},
// Highlight 6 symbols to the left
{Down: usb.KeyModifierShift},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Up: usb.KeyModifierShift},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-X to cut
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Move to beginning of line
{Press: usb.KeyHome},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-V to paste
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Highlight 3 symbols to the right
{Down: usb.KeyModifierShift},
{Press: usb.KeyRight},
{Press: usb.KeyRight},
{Press: usb.KeyRight},
{Up: usb.KeyModifierShift},
// Use Ctrl-X to cut
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Move to end of line
{Press: usb.KeyEnd},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-V to paste
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Newline
{Press: usb.KeyEnter},
{Press: usb.KeyEnter},
}, 150*time.Millisecond)
// Highlight all text and delete
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyA)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(time.Second)
keyboard.Press(usb.KeyDelete)
time.Sleep(time.Second)
// Close window
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyQ)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(2 * time.Second)
// Confirm discard file
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeyD)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(5 * time.Second)
// Open a new terminal
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeySpace)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("konsole"))
keyboard.Press(usb.KeyEnter)
time.Sleep(5 * time.Second)
// Open serial connection
keyboard.Write([]byte("screen /dev/ttyACM0 115200"))
time.Sleep(2 * time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(4 * time.Second)
// Write to UART
keyboard.Write([]byte("hello!"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("NO U"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
// Close serial connection
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyX)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(time.Second)
keyboard.Press(usb.KeyBackslash)
time.Sleep(time.Second)
keyboard.Press(usb.KeyY)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
// Close terminal
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyD)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(25 * time.Second)
}
}
type Key struct {
Press usb.Keycode
Down usb.Keycode
Up usb.Keycode
Time time.Duration
}
func testKeys(key []Key, delay time.Duration) {
for _, k := range key {
if 0 != k.Down {
keyboard.Down(k.Down)
}
if 0 != k.Press {
keyboard.Press(k.Press)
}
if 0 != k.Up {
keyboard.Up(k.Up)
}
if 0 != k.Time {
time.Sleep(k.Time)
} else {
time.Sleep(delay)
}
}
}
func testInternationalLayout() {
// International keyboard layouts also supported
keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Numbers: 0123456789\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬­®¯°±\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Cedilla: Çç\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Ring Above: Åå\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("AE: Ææ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Thorn: Þþ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Sharp S: ß\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("O-Stroke: Øø\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Eth: Ðð\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Euro: €\n"))
}
-37
View File
@@ -1,37 +0,0 @@
// This is a echo console running on the device UART.
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
package main
import (
"machine"
"time"
)
func main() {
uart := machine.UART0
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
input := make([]byte, 4096)
i := 0
for {
if uart.Buffered() > 0 {
data, _ := uart.ReadByte()
switch data {
case 13:
// return key
uart.Write([]byte("\r\n"))
uart.Write([]byte("You typed: "))
uart.Write(input[:i])
uart.Write([]byte("\r\n"))
i = 0
default:
// just echo the character
uart.WriteByte(data)
input[i] = data
i++
}
}
time.Sleep(10 * time.Millisecond)
}
}
+2
View File
@@ -0,0 +1,2 @@
internal/itoa is new to go as of 1.17.
This directory should be removed when tinygo drops support for go 1.16.
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Simple conversions to avoid depending on strconv.
package itoa
// Itoa converts val to a decimal string.
func Itoa(val int) string {
if val < 0 {
return "-" + Uitoa(uint(-val))
}
return Uitoa(uint(val))
}
// Uitoa converts val to a decimal string.
func Uitoa(val uint) string {
if val == 0 { // avoid string allocation
return "0"
}
var buf [20]byte // big enough for 64bit value base 10
i := len(buf) - 1
for val >= 10 {
q := val / 10
buf[i] = byte('0' + val - q*10)
i--
val = q
}
// val < 10
buf[i] = byte('0' + val)
return string(buf[i:])
}
+40
View File
@@ -0,0 +1,40 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package itoa_test
import (
"fmt"
"internal/itoa"
"math"
"testing"
)
var (
minInt64 int64 = math.MinInt64
maxInt64 int64 = math.MaxInt64
maxUint64 uint64 = math.MaxUint64
)
func TestItoa(t *testing.T) {
tests := []int{int(minInt64), math.MinInt32, -999, -100, -1, 0, 1, 100, 999, math.MaxInt32, int(maxInt64)}
for _, tt := range tests {
got := itoa.Itoa(tt)
want := fmt.Sprint(tt)
if want != got {
t.Fatalf("Itoa(%d) = %s, want %s", tt, got, want)
}
}
}
func TestUitoa(t *testing.T) {
tests := []uint{0, 1, 100, 999, math.MaxUint32, uint(maxUint64)}
for _, tt := range tests {
got := itoa.Uitoa(tt)
want := fmt.Sprint(tt)
if want != got {
t.Fatalf("Uitoa(%d) = %s, want %s", tt, got, want)
}
}
}
+127
View File
@@ -0,0 +1,127 @@
//go:build scheduler.asyncify
// +build scheduler.asyncify
package task
import (
"unsafe"
)
// Stack canary, to detect a stack overflow. The number is a random number
// generated by random.org. The bit fiddling dance is necessary because
// otherwise Go wouldn't allow the cast to a smaller integer size.
const stackCanary = uintptr(uint64(0x670c1333b83bf575) & uint64(^uintptr(0)))
//go:linkname runtimePanic runtime.runtimePanic
func runtimePanic(str string)
// state is a structure which holds a reference to the state of the task.
// When the task is suspended, the stack pointers are saved here.
type state struct {
// entry is the entry function of the task.
// This is needed every time the function is invoked so that asyncify knows what to rewind.
entry uintptr
// args are a pointer to a struct holding the arguments of the function.
args unsafe.Pointer
// stackState is the state of the stack while unwound.
stackState
launched bool
}
// stackState is the saved state of a stack while unwound.
// The stack is arranged with asyncify at the bottom, C stack at the top, and a gap of available stack space between the two.
type stackState struct {
// asyncify is the stack pointer of the asyncify stack.
// This starts from the bottom and grows upwards.
asyncifysp uintptr
// asyncify is stack pointer of the C stack.
// This starts from the top and grows downwards.
csp uintptr
}
// start creates and starts a new goroutine with the given function and arguments.
// The new goroutine is immediately started.
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
t := &Task{}
t.state.initialize(fn, args, stackSize)
runqueuePushBack(t)
}
//export tinygo_launch
func (*state) launch()
//go:linkname align runtime.align
func align(p uintptr) uintptr
// initialize the state and prepare to call the specified function with the specified argument bundle.
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
// Save the entry call.
s.entry = fn
s.args = args
// Create a stack.
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
// Calculate stack base addresses.
s.asyncifysp = uintptr(unsafe.Pointer(&stack[0]))
s.csp = uintptr(unsafe.Pointer(&stack[0])) + uintptr(len(stack))*unsafe.Sizeof(uintptr(0))
stack[0] = stackCanary
}
//go:linkname runqueuePushBack runtime.runqueuePushBack
func runqueuePushBack(*Task)
// currentTask is the current running task, or nil if currently in the scheduler.
var currentTask *Task
// Current returns the current active task.
func Current() *Task {
return currentTask
}
// Pause suspends the current task and returns to the scheduler.
// This function may only be called when running on a goroutine stack, not when running on the system stack.
func Pause() {
// This is mildly unsafe but this is also the only place we can do this.
if *(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) != stackCanary {
runtimePanic("stack overflow")
}
currentTask.state.unwind()
*(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) = stackCanary
}
//export tinygo_unwind
func (*stackState) unwind()
// Resume the task until it pauses or completes.
// This may only be called from the scheduler.
func (t *Task) Resume() {
// The current task must be saved and restored because this can nest on WASM with JS.
prevTask := currentTask
currentTask = t
if !t.state.launched {
t.state.launch()
t.state.launched = true
} else {
t.state.rewind()
}
currentTask = prevTask
if t.state.asyncifysp > t.state.csp {
runtimePanic("stack overflow")
}
}
//export tinygo_rewind
func (*state) rewind()
// OnSystemStack returns whether the caller is running on the system stack.
func OnSystemStack() bool {
// If there is not an active goroutine, then this must be running on the system stack.
return Current() == nil
}
+102
View File
@@ -0,0 +1,102 @@
.globaltype __stack_pointer, i32
.global tinygo_unwind
.hidden tinygo_unwind
.type tinygo_unwind,@function
tinygo_unwind: // func (state *stackState) unwind()
.functype tinygo_unwind (i32) -> ()
// Check if we are rewinding.
i32.const 0
i32.load8_u tinygo_rewinding
if // if tinygo_rewinding {
// Stop rewinding.
call stop_rewind
i32.const 0
i32.const 0
i32.store8 tinygo_rewinding // tinygo_rewinding = false;
else
// Save the C stack pointer (destination structure pointer is in local 0).
local.get 0
global.get __stack_pointer
i32.store 4 // state.csp = getCurrentStackPointer()
// Ask asyncify to unwind.
// When resuming, asyncify will return this function with tinygo_rewinding set to true.
local.get 0
call start_unwind // asyncify.start_unwind(state)
end_if
return
end_function
.global tinygo_launch
.hidden tinygo_launch
.type tinygo_launch,@function
tinygo_launch: // func (state *state) launch()
.functype tinygo_launch (i32) -> ()
// Switch to the goroutine's C stack.
global.get __stack_pointer // prev := getCurrentStackPointer()
local.get 0
i32.load 12
global.set __stack_pointer // setStackPointer(state.csp)
// Get the argument pack and entry pointer.
local.get 0
i32.load 4 // args := state.args
local.get 0
i32.load 0 // fn := state.entry
// Launch the entry function.
call_indirect (i32) -> () // fn(args)
// Stop unwinding.
call stop_unwind
// Restore the C stack.
global.set __stack_pointer // setStackPointer(prev)
return
end_function
.global tinygo_rewind
.hidden tinygo_rewind
.type tinygo_rewind,@function
tinygo_rewind: // func (state *state) rewind()
.functype tinygo_rewind (i32) -> ()
// Switch to the goroutine's C stack.
global.get __stack_pointer // prev := getCurrentStackPointer()
local.get 0
i32.load 12
global.set __stack_pointer // setStackPointer(state.csp)
// Get the argument pack and entry pointer.
local.get 0
i32.load 4 // args := state.args
local.get 0
i32.load 0 // fn := state.entry
// Prepare to rewind.
i32.const 0
i32.const 1
i32.store8 tinygo_rewinding // tinygo_rewinding = true;
local.get 0
i32.const 8
i32.add
call start_rewind // asyncify.start_rewind(&state.stackState)
// Launch the entry function.
// This will actually rewind the call stack.
call_indirect (i32) -> () // fn(args)
// Stop unwinding.
call stop_unwind
// Restore the C stack.
global.set __stack_pointer // setStackPointer(prev)
return
end_function
.functype start_unwind (i32) -> ()
.import_module start_unwind, asyncify
.functype stop_unwind () -> ()
.import_module stop_unwind, asyncify
.functype start_rewind (i32) -> ()
.import_module start_rewind, asyncify
.functype stop_rewind () -> ()
.import_module stop_rewind, asyncify
.hidden tinygo_rewinding # @tinygo_rewinding
.type tinygo_rewinding,@object
.section .bss.tinygo_rewinding,"",@
.globl tinygo_rewinding
tinygo_rewinding:
.int8 0 # 0x0
.size tinygo_rewinding, 1
+1 -1
View File
@@ -1,4 +1,4 @@
// +build scheduler.tasks,amd64
// +build scheduler.tasks,amd64,!windows
package task
@@ -0,0 +1,57 @@
// Windows on amd64 has a slightly different ABI than other (*nix) systems.
// Therefore, assembly functions need to be tweaked slightly.
.section .text.tinygo_startTask,"ax"
.global tinygo_startTask
tinygo_startTask:
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, r12 contain the pc of the to-be-started function and
// r13 contain the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
movq %r13, %rcx
// Branch to the "goroutine start" function.
callq *%r12
// After return, exit this goroutine. This is a tail call.
jmp tinygo_pause
.global tinygo_swapTask
.section .text.tinygo_swapTask,"ax"
tinygo_swapTask:
// This function gets the following parameters:
// %rcx = newStack uintptr
// %rdx = oldStack *uintptr
// Save all callee-saved registers:
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rsi
pushq %rdi
pushq %rbp
pushq %rbx
// Save the current stack pointer in oldStack.
movq %rsp, (%rdx)
// Switch to the new stack pointer.
movq %rcx, %rsp
// Load saved register from the new stack.
popq %rbx
popq %rbp
popq %rdi
popq %rsi
popq %r12
popq %r13
popq %r14
popq %r15
// Return into the new task, as if tinygo_swapTask was a regular call.
ret
@@ -0,0 +1,66 @@
// +build scheduler.tasks,amd64,windows
package task
// This is almost the same as task_stack_amd64.go, but with the extra rdi and
// rsi registers saved: Windows has a slightly different calling convention.
import "unsafe"
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see task_stack_amd64_windows.S that relies on
// the exact layout of this struct.
type calleeSavedRegs struct {
rbx uintptr
rbp uintptr
rdi uintptr
rsi uintptr
r12 uintptr
r13 uintptr
r14 uintptr
r15 uintptr
pc uintptr
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
// Start the function at tinygo_startTask (defined in
// src/internal/task/task_stack_amd64_windows.S). This assembly code calls a
// function (passed in r12) with a single argument (passed in r13). After
// the function returns, it calls Pause().
r.pc = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in r12.
// This function is a compiler-generated wrapper which loads arguments out
// of a struct pointer. See createGoroutineStartWrapper (defined in
// compiler/goroutine.go) for more information.
r.r12 = fn
// Pass the pointer to the arguments struct in r13.
r.r13 = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
+59
View File
@@ -0,0 +1,59 @@
//go:build lorae5
// +build lorae5
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
// We assume a LED is connected on PB5
LED = PB5 // Default LED
)
// SubGhz (SPI3)
const (
SPI0_NSS_PIN = PA4
SPI0_SCK_PIN = PA5
SPI0_SDO_PIN = PA6
SPI0_SDI_PIN = PA7
)
// UARTS
const (
//MCU USART1
UART1_TX_PIN = PB6
UART1_RX_PIN = PB7
//MCU USART2
UART2_TX_PIN = PA2
UART2_RX_PIN = PA3
// DEFAULT USART
UART_TX_PIN = UART1_TX_PIN
UART_RX_PIN = UART1_RX_PIN
)
var (
// Console UART
UART0 = &_UART0
_UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART1,
TxAltFuncSelector: AF7_USART1_2,
RxAltFuncSelector: AF7_USART1_2,
}
DefaultUART = UART0
// SPI
SPI3 = SPI{
Bus: stm32.SPI3,
}
)
func init() {
// Enable UARTs Interrupts
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART0.handleInterrupt)
}
+130
View File
@@ -0,0 +1,130 @@
// +build m5stack
package machine
const (
// GND | ADC G35
// GND | ADC G36
// GND | RST EN
// G23 MOSI | DAC/SPK G25
// G19 MISO | DAC G26
// G18 SCK | 3.3V
// G3 RXD1 | TXD1 G1
// G16 RXD2 | TXD2 G17
// G21 SDA | DCL G22
// G2 GPIO | GPIO G5
// G12 IIS_SK | IIS_WS G13
// G15 IIS_OUT | IIS_MK G0
// HPWR | IIS_IN G34
// HPWR | 5V
// HPWR | BATTERY
IO0 Pin = 0
IO1 Pin = 1
IO2 Pin = 2
IO3 Pin = 3
IO4 Pin = 4
IO5 Pin = 5
IO6 Pin = 6
IO7 Pin = 7
IO8 Pin = 8
IO9 Pin = 9
IO10 Pin = 10
IO11 Pin = 11
IO12 Pin = 12
IO13 Pin = 13
IO14 Pin = 14
IO15 Pin = 15
IO16 Pin = 16
IO17 Pin = 17
IO18 Pin = 18
IO19 Pin = 19
IO20 Pin = 20
IO21 Pin = 21
IO22 Pin = 22
IO23 Pin = 23
IO24 Pin = 24
IO25 Pin = 25
IO26 Pin = 26
IO27 Pin = 27
IO28 Pin = 28
IO29 Pin = 29
IO30 Pin = 30
IO31 Pin = 31
IO32 Pin = 32
IO33 Pin = 33
IO34 Pin = 34
IO35 Pin = 35
IO36 Pin = 36
IO37 Pin = 37
IO38 Pin = 38
IO39 Pin = 39
)
const (
// Buttons
BUTTON_A = IO39
BUTTON_B = IO38
BUTTON_C = IO37
BUTTON = BUTTON_A
// Speaker
SPEAKER_PIN = IO25
)
// SPI pins
const (
SPI0_SCK_PIN = IO18
SPI0_SDO_PIN = IO23
SPI0_SDI_PIN = IO19
SPI0_CS0_PIN = IO14
// LCD (ILI9342C)
LCD_SCK_PIN = SPI0_SCK_PIN
LCD_SDO_PIN = SPI0_SDO_PIN
LCD_SDI_PIN = SPI0_SDI_PIN // NoPin ?
LCD_SS_PIN = SPI0_CS0_PIN
LCD_DC_PIN = IO27
LCD_RST_PIN = IO33
LCD_BL_PIN = IO32
// SD CARD
SDCARD_SCK_PIN = SPI0_SCK_PIN
SDCARD_SDO_PIN = SPI0_SDO_PIN
SDCARD_SDI_PIN = SPI0_SDI_PIN
SDCARD_SS_PIN = IO4
)
// I2C pins
const (
SDA0_PIN = IO21
SCL0_PIN = IO22
SDA_PIN = SDA0_PIN
SCL_PIN = SCL0_PIN
)
// ADC pins
const (
ADC1 Pin = IO35
ADC2 Pin = IO36
)
// DAC pins
const (
DAC1 Pin = IO25
DAC2 Pin = IO26
)
// UART pins
const (
// UART0 (CP2104)
UART0_TX_PIN = IO1
UART0_RX_PIN = IO3
UART1_TX_PIN = IO17
UART1_RX_PIN = IO16
UART_TX_PIN = UART0_TX_PIN
UART_RX_PIN = UART0_RX_PIN
)
@@ -1,53 +0,0 @@
// +build sam,atsamd51,matrixportal_m4
package machine
import (
"device/sam"
"runtime/interrupt"
)
// UART on the MatrixPortal M4
var (
Serial = UART1
UART1 = &_UART1
_UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM1_USART_INT,
SERCOM: 1,
}
UART2 = &_UART2
_UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM4_USART_INT,
SERCOM: 4,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1_1, _UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM4_1, _UART2.handleInterrupt)
}
// I2C on the MatrixPortal M4
var (
I2C0 = &I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
)
// SPI on the MatrixPortal M4
var (
SPI0 = SPI{
Bus: sam.SERCOM3_SPIM,
SERCOM: 3, // BUG: SDO on SERCOM1!
}
NINA_SPI = SPI0
SPI1 = SPI{
Bus: sam.SERCOM0_SPIM,
SERCOM: 0,
}
)
+41 -6
View File
@@ -1,9 +1,14 @@
//go:build nano_33_ble
// +build nano_33_ble
// This contains the pin mappings for the Arduino Nano 33 BLE [Sense] boards.
// - https://store.arduino.cc/arduino-nano-33-ble
// - https://store.arduino.cc/arduino-nano-33-ble-sense
//
// Flashing the board requires special version of bossac.
// ----------------------------------------------------------------------------
// Flashing
//
// Special version of bossac is required.
// This executable can be obtained two ways:
// 1) In Arduino IDE, install support for the board ("Arduino Mbed OS Nano Boards")
// Search for "tools/bossac/1.9.1-arduino2/bossac" in Arduino IDEs directory
@@ -16,9 +21,14 @@
// It is possible to replace original bossac with this new one (this only adds support for nrf chip).
// In that case make "bossac_arduino2" symlink on it, for the board target to be able to find it.
//
// For more information, see:
// - https://store.arduino.cc/arduino-nano-33-ble
// - https://store.arduino.cc/arduino-nano-33-ble-sense
// ----------------------------------------------------------------------------
// Bluetooth
//
// SoftDevice (s140v7) must be flashed first to enable use of bluetooth on this board.
// See https://github.com/tinygo-org/bluetooth
//
// SoftDevice overwrites original bootloader and flashing method described above is not avalable anymore.
// Instead, please use debug probe and flash your code with "nano-33-ble-s140v7" target.
//
package machine
@@ -62,6 +72,7 @@ const (
LED_RED = P0_24
LED_GREEN = P0_16
LED_BLUE = P0_06
LED_PWR = P1_09
)
// UART0 pins
@@ -72,8 +83,19 @@ const (
// I2C pins
const (
SDA_PIN = P0_31
SCL_PIN = P0_02
// Defaults to internal
SDA_PIN = SDA1_PIN
SCL_PIN = SCL1_PIN
// I2C0 (external) pins
SDA0_PIN = P0_31
SCL0_PIN = P0_02
// I2C1 (internal) pins
SDA1_PIN = P0_14
SCL1_PIN = P0_15
I2C_PULLUP = P1_00 // Set high for I2C to work
)
// SPI pins
@@ -83,6 +105,19 @@ const (
SPI0_SDI_PIN = P1_08
)
// Peripherals
const (
APDS_INT = P0_19 // Proximity (APDS9960) interrupt pin
LSM_PWR = P0_22 // IMU (LSM9DS1) power
LSP_PWR = P0_22 // Pressure (LSP22) power
HTS_PWR = P0_22 // Humidity (HTS221) power
MIC_PWR = P0_17 // Microphone (MP34DT06JTR) power
MIC_CLK = P0_26
MIC_DIN = P0_25
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Nano 33 BLE"
+72
View File
@@ -0,0 +1,72 @@
//go:build nucleowl55jc
// +build nucleowl55jc
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
LED_BLUE = PB15
LED_GREEN = PB9
LED_RED = PB11
LED = LED_RED
BTN1 = PA0
BTN2 = PA1
BTN3 = PC6
BUTTON = BTN1
// SubGhz (SPI3)
SPI0_NSS_PIN = PA4
SPI0_SCK_PIN = PA5
SPI0_SDO_PIN = PA6
SPI0_SDI_PIN = PA7
//MCU USART1
UART1_TX_PIN = PB6
UART1_RX_PIN = PB7
//MCU USART2
UART2_RX_PIN = PA3
UART2_TX_PIN = PA2
// DEFAULT USART
UART_RX_PIN = UART2_RX_PIN
UART_TX_PIN = UART2_TX_PIN
)
var (
// STM32 UART2 is connected to the embedded STLINKV3 Virtual Com Port
UART0 = &_UART0
_UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
TxAltFuncSelector: 7,
RxAltFuncSelector: 7,
}
// UART1 is free
UART1 = &_UART1
_UART1 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART1,
TxAltFuncSelector: 7,
RxAltFuncSelector: 7,
}
DefaultUART = UART0
// SPI
SPI3 = SPI{
Bus: stm32.SPI3,
}
)
func init() {
// Enable UARTs Interrupts
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART0.handleInterrupt)
UART1.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART1.handleInterrupt)
}
+15 -25
View File
@@ -4,7 +4,6 @@ package machine
import (
"device/nxp"
"machine/usb"
"runtime/interrupt"
)
@@ -56,21 +55,21 @@ const (
// Analog pins
const (
// = Pin // Dig [Pad] {ADC1/ADC2}
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
// = Pin // Dig | [Pad] {ADC1/ADC2}
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
)
// Default peripheral pins
@@ -105,15 +104,6 @@ func init() {
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
}
// #=====================================================#
// | USB |
// #=====================================================#
var (
// UART0 = usb.UART{Port: 0}
HID0 = usb.HID{Port: 0}
UART0 = &UART1
)
// #=====================================================#
// | UART |
// #===========#===========#=============#===============#
+2 -1
View File
@@ -1,4 +1,5 @@
// +build atmega nrf sam stm32 fe310 k210 rp2040
//go:build atmega || nrf || sam || (stm32 && !stm32wle5) || fe310 || k210 || rp2040
// +build atmega nrf sam stm32,!stm32wle5 fe310 k210 rp2040
package machine
+7
View File
@@ -10,6 +10,13 @@ var (
ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt")
)
// Device is the running program's chip name, such as "ATSAMD51J19A" or
// "nrf52840". It is not the same as the CPU name.
//
// The constant is some hardcoded default value if the program does not target a
// particular chip but instead runs in WebAssembly for example.
const Device = deviceName
// PinMode sets the direction and pull mode of the pin. For example, PinOutput
// sets the pin as an output and PinInputPullup sets the pin as an input with a
// pull-up.
+2
View File
@@ -16,6 +16,8 @@ import (
"unsafe"
)
const deviceName = sam.Device
const (
PinAnalog PinMode = 1
PinSERCOM PinMode = 2
+2
View File
@@ -16,6 +16,8 @@ import (
"unsafe"
)
const deviceName = sam.Device
func CPUFrequency() uint32 {
return 120000000
}
+2
View File
@@ -8,6 +8,8 @@ import (
"unsafe"
)
const deviceName = avr.DEVICE
const (
PinInput PinMode = iota
PinInputPullup
+2
View File
@@ -9,6 +9,8 @@ import (
"unsafe"
)
const deviceName = esp.Device
const peripheralClock = 80000000 // 80MHz
// CPUFrequency returns the current CPU frequency of the chip.

Some files were not shown because too many files have changed in this diff Show More