Compare commits

...

86 Commits

Author SHA1 Message Date
Ayke van Laethem 731532cd2b all: release 0.30.0
This is a small release just in time for GopherCon US.

Some of the big changes of this release are:

  - switch to LLVM 16
  - fixes for two separate hard-to-reproduce compiler crashes
  - added support for the Adafruit Gemma M0
2023-09-21 08:03:16 +02:00
Kenneth Bell adadbadec3 interp: improve unknown opcode handling 2023-09-21 01:18:05 +02:00
Kenneth Bell 58fafaeb5c build: #3893 do not return LLVM structs from Build 2023-09-21 01:18:05 +02:00
Dan Kegel 13a8eae0d4 build: build Go SSA serially [issue 3895]
From
https://github.com/tinygo-org/tinygo/pull/3915#issuecomment-1724405109

According to Ayke, it slows down the build but seems to reliably fix https://github.com/tinygo-org/tinygo/issues/3895
2023-09-20 23:22:18 +02:00
Ayke van Laethem 42da7654ec compiler: don't use types in the global context
This usually works by chance, but leads to crashes. So we should never
ever do this.

I'm pretty sure this is the crash behind this issue: https://github.com/tinygo-org/tinygo/issues/3894

It may also have caused this crash: https://github.com/tinygo-org/tinygo/issues/3874

I have a suspicion this is also behind the rather crash-prone CircleCI
jobs, that we haven't been able to find the source of. But we'll find
out soon enough once this fix is merged.

To avoid hitting this issue again in the future, I've created a PR to
remove these dangerous functions altogether from the go-llvm API:
https://github.com/tinygo-org/go-llvm/pull/54
2023-09-19 09:21:51 +02:00
Ayke van Laethem 8698a7e496 all: default to LLVM 16
So that `go install` works on MacOS with Homebrew (and on Linux with an
up-to-date distro).
2023-09-18 21:58:02 +02:00
Ayke van Laethem 1d7543e2bf all: switch to LLVM 16
This commit adds support for LLVM 16 and switches to it by default. That
means three LLVM versions are supported at the same time: LLVM 14, 15,
and 16.

This commit includes work by QuLogic:

  * Part of this work was based on a PR by QuLogic:
    https://github.com/tinygo-org/tinygo/pull/3649
    But I also had parts of this already implemented in an old branch I
    already made for LLVM 16.
  * QuLogic also provided a CGo fix here, which is also incorporated in
    this commit:
    https://github.com/tinygo-org/tinygo/pull/3869

The difference with the original PR by QuLogic is that this commit is
more complete:
  * It switches to LLVM 16 by default.
  * It updates some things to also make it work with a self-built LLVM.
  * It fixes the CGo bug in a slightly different way, and also fixes
    another one not included in the original PR.
  * It does not keep compiler tests passing on older LLVM versions. I
    have found this to be quite burdensome and therefore don't generally
    do this - the smoke tests should hopefully catch most regressions.
2023-09-18 21:58:02 +02:00
deadprogram ff32fbbb4f targets: increase default stack size to 32k for wasi/wasm targets
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-17 14:24:21 +02:00
sago35 3ad2530ee7 atsamd21, atsamd51: add support for USB INTERRUPT OUT 2023-09-16 09:30:05 +02:00
deadprogram c4de195f04 machine/rp2040: always use the USB device enum fix, even in chips that supposedly have the HW fix.
Additionally, correct the amount of time spent waiting after USB reset, based on advice from @sago35

Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-12 08:41:58 +02:00
Timothy Rule 75aca0f5ee Board support for Adafruit Gemma M0 (#3903)
machine, targets: Board support for Adafruit Gemma M0
2023-09-11 11:11:26 +02:00
Elliott Sales de Andrade bb9a9beed5 Update golang.org/x/tools to v0.12.0 2023-09-10 23:14:58 +02:00
Elliott Sales de Andrade 4042c1d618 Update tools to 0.9.0
This requires updating test data, due to the change noted in the
previous commit.
2023-09-10 23:14:58 +02:00
Elliott Sales de Andrade bf73516259 compiler: Handle nil array and struct constants
This is necessary for tools 0.9.0, which started lifting those since
https://github.com/golang/tools/commit/71ea8f168c160da91116b4ddbd577a8fc95aa334
2023-09-10 23:14:58 +02:00
deadprogram 43d282174f targets: add GoBadge target as alias for PyBadge :)
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-10 15:52:55 +02:00
Damian Gryski 0042bf62a5 compiler,reflect: add support for [...]T -> []T in reflect 2023-09-10 13:05:18 +02:00
Ayke van Laethem f11731ff35 interp: don't copy unknown values in runtime.sliceCopy
This was bug https://github.com/tinygo-org/tinygo/issues/3890.
See the diff for details. Essentially, it means that `copy` in the
interpreter was copying data that wasn't known yet, or copying data into
a slice that could be read externally after the interp pass.
2023-09-10 11:30:09 +02:00
sago35 e9d8a9bc0b goenv: update to new v0.30.0 development version 2023-09-08 17:41:31 +02:00
deadprogram 9d6eb1ff06 machine/usb/adc/midi: improve implementation to include several new messages
such as program changes and pitch bend. Also add error handling for invalid
parameter values such as MIDI channel. This however makes a somewhat breaking
change to the current implementation, in that we now use the typical MIDI user
system of counting MIDI channels from 1-16 instead of from 0-15 as the lower
level USB-MIDI API itself expects.

Also add constant values for continuous controller messages, rename SendCC
function, and add SysEx function.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-07 08:41:57 +02:00
Ayke van Laethem 4643401a1d runtime: refactor markGlobals to findGlobals
Instead of markGlobals calling markRoots unconditionally (which doesn't
make sense for -gc=none and -gc=leaking), provide markRoots as a
callback function.

This is in preparation for -gc=boehm, where the previous design is even
more awkward and a callback makes far more sense.

I've tested the size impact using `make smoketest XTENSA=0`. There is
none, except for two cases:

  * One with `-opt=0` so const-propagation for the callback didn't take
    place.
  * One other on AVR, I don't know why but as it's only 16 bytes in a
    very specific case I'm going to assume it's just a random change in
    compiler output that caused a size difference.
2023-09-05 00:42:01 +02:00
deadprogram dc449882ad docs: update CHANGELOG for release 0.29
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-25 15:57:40 +02:00
deadprogram a158d3194f all: update version for 0.29 release
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-25 15:57:40 +02:00
Pertti Erkkilä 806498f099 nRF52: set SPI TX/RX lengths even data is empty. Fixes #3868 (#3877)
machine/hrf: Set SPI TX/RX lengths even data is empty. Fixes #3868
2023-08-24 13:15:18 +02:00
deadprogram e3bc6da9e4 make: add task to check NodeJS version before running tests
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-19 20:04:10 +02:00
Pierre Constantineau 3e2471d934 adding new uf2 target for PCA10056 (#3765)
targets: adding new uf2 target for PCA10056
2023-08-19 11:06:17 +02:00
Ayke van Laethem a545f17d2e wasm: add support for GOOS=wasip1
This adds true GOOS=wasip1 support in addition to our existing
-target=wasi support. The old support for WASI isn't removed, but should
be treated as deprecated and will likely be removed eventually to reduce
the test burden.
2023-08-17 18:16:54 +02:00
Kenneth Bell f4375d0452 samd51,rp2040,nrf528xx,stm32: implement watchdog 2023-08-15 11:50:07 +02:00
deadprogram 756cdf44ed loader: merge go.env file which is now required starting in Go 1.21 to correctly get required packages
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-13 17:11:11 +02:00
Ayke van Laethem 9037bf8bf0 main: add target JSON file in tinygo info output
It looks like this on my system, for example:

    {
      "target": {
        "llvm-target": "aarch64-unknown-linux",
        "cpu": "generic",
        "features": "+neon",
        "goos": "linux",
        "goarch": "arm64",
        "build-tags": [
          "linux",
          "arm64"
        ],
        "gc": "precise",
        "scheduler": "tasks",
        "linker": "ld.lld",
        "rtlib": "compiler-rt",
        "libc": "musl",
        "default-stack-size": 65536,
        "ldflags": [
          "--gc-sections"
        ],
        "extra-files": [
          "src/runtime/asm_arm64.S",
          "src/internal/task/task_stack_arm64.S"
        ],
        "gdb": [
          "gdb"
        ],
        "flash-1200-bps-reset": "false"
      },
      "goroot": "/home/ayke/.cache/tinygo/goroot-23c311bcaa05f188affa3c42310aba343acc82562d5e5f04dea9d5b79ac35f7e",
      "goos": "linux",
      "goarch": "arm64",
      "goarm": "6",
      ...
    }

This can be very useful while working on the automatically generated
target object for example (in my case, GOOS=wasip1).
2023-08-13 15:27:21 +02:00
deadprogram bfe9ee378f rp2040: move flash related functions into separate file from C imports for correct LSP. Fixes #3852
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-13 14:08:57 +02:00
deadprogram 37a4fa205c modules: update to go-serial package v1.6.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-11 18:21:05 +02:00
deadprogram 59cc7d4c42 docker: use Go 1.21 for Docker dev container build
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-11 17:35:43 +02:00
deadprogram ab64e215dd build: switch GH actions builds to use Go 1.21 final release
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-10 12:45:52 +02:00
deadprogram 253dbe335a builder: update message for max supported Go version
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-09 19:27:15 +02:00
Unrud 0ef86e1534 Add support for HID Keyboard LEDs 2023-08-07 14:00:32 +02:00
Kenneth Bell 72270f9052 all: use https for renesas submodule #3856 2023-08-07 10:29:31 +02:00
sago35 67ec722a74 board: add AKIZUKI DENSHI AE-RP2040 2023-08-04 17:53:01 +02:00
Ayke van Laethem 62294feb56 compiler: improve panic message when a runtime call is unavailable
This should not happen under normal circumstances. It can still happen
when there is a mismatch between TinyGo version and the associated
runtime, or while developing the compiler package.
2023-08-04 11:59:11 +02:00
Ayke van Laethem f1e25a18d2 compiler: implement clear builtin for maps 2023-08-04 11:59:11 +02:00
Ayke van Laethem a2f886a67a compiler: implement clear builtin for slices 2023-08-04 11:59:11 +02:00
Ayke van Laethem f791c821ff compiler: add min and max builtin support 2023-08-04 11:59:11 +02:00
Ayke van Laethem a93f0ed12a all: Go 1.21 support 2023-08-04 11:59:11 +02:00
Ayke van Laethem c25dd0a972 testing: add Testing function
This is new in Go 1.21.
2023-08-04 11:59:11 +02:00
sago35 215dd3f0be machine/usb/hid: rename Handler() to TxHandler() 2023-08-04 09:43:53 +02:00
sago35 c51f5cea0b machine/usb/hid: add RxHandler interface 2023-08-04 09:43:53 +02:00
sago35 395ee2d338 machine/usb: refactor endpoint configuration 2023-08-02 09:16:29 +02:00
sago35 069e4f0d98 machine/usb: allow USB Endpoint settings to be changed externally 2023-08-02 09:16:29 +02:00
sago35 d1eb642d45 machine/usb: remove usbDescriptorConfig 2023-08-01 15:24:42 +02:00
Charlie Haley 5f2e72f371 compiler: add compiler-rt to wasm.json 2023-08-01 09:56:58 +02:00
Charlie Haley 5b581d83b3 compiler: add compiler-rt and wasm symbols to table 2023-07-31 14:52:26 +02:00
Ayke van Laethem d845f1e1b2 wasm: fix functions exported through //export
When a function is exported using //export, but also had a
//go:wasm-module pragma, the //export name was ignored. The
//go:wasm-module doesn't actually do anything besides breaking the
export (exported functions don't have a module name).

I've refactored and cleaned up the code, and in the process removed this
weird edge case.
2023-07-28 13:57:24 +02:00
Yurii Soldak 00d46bd25d avr: pin change interrupt 2023-07-27 18:15:22 +02:00
deadprogram 01d2ef327d sync: add implementation from upstream Go for OnceFunc, OnceValue, and OnceValues
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-20 08:09:03 +02:00
soypat 4da1f6bcbe rp2040:add NoPin support 2023-07-16 16:25:30 +02:00
Ayke van Laethem 14ddba8519 nrf: add I2C timeout
This commit adds I2C timeouts for nrf51 and nrf52 (but not yet for
others like nrf52840).

Tested on the PineTime, where I now got a timeout instead of hanging and
resetting due to a watchdog reset.
2023-07-16 10:27:33 +02:00
Patricio Whittingslow a7b205c26c machine.UART refactor (#3832)
* add gosched calls to UART

* add UART.flush() stubs for all supported architectures

* add comment un uart.go on flush functionality

* uart.writeByte as base of UART usage

* fix NXP having duplicate WriteByte

* fix writeByte not returning error on some platforms

* add flush method for fe310 device

* check for error in WriteByte call to writeByte
2023-07-15 11:24:53 -03:00
deadprogram c83f712c17 machine/rp2040: wait for 1000 us after flash reset to avoid issues with busy USB bus
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-07 22:13:26 +02:00
Ayke van Laethem fffad84a63 reflect: add SetZero
This was added in Go 1.20 and is required by encoding/json starting with
Go 1.21.
2023-07-07 18:12:57 +02:00
Ayke van Laethem e075e0591d main: use go env instead of doing all detection manually
This replaces our own manual detection of various variables (GOROOT,
GOPATH, Go version) with a simple call to `go env`.

If the `go` command is not found:

    error: could not find 'go' command: executable file not found in $PATH

If the Go version is too old:

    error: requires go version 1.18 through 1.20, got go1.17

If the Go tool itself outputs an error (using GOROOT=foobar here):

    go: cannot find GOROOT directory: foobar

This does break the case where `go` wasn't available in $PATH but we
would detect it anyway (via some hardcoded OS-dependent paths). I'm not
sure we want to fix that: I think it's better to tell users "make sure
`go version` prints the right value" than to do some automagic detection
of Go binary locations.
2023-07-07 16:55:59 +02:00
Achille Roussel 46d2696363 wasi: allow zero inodes when reading directories
Signed-off-by: Achille Roussel <achille.roussel@gmail.com>
2023-07-07 09:56:38 +02:00
Ayke van Laethem e98dfd6c46 reflect: implement Value.Grow
This was added in Go 1.20 and becomes necessary for encoding/json in Go
1.21.
2023-07-07 08:10:47 +02:00
sago35 9126b95737 machine/samd51: fix i2cTimeout was decreasing due to cache activation 2023-07-06 21:35:54 +02:00
deadprogram 3871b831af make: add make task to generate Renesas device wrappers
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-05 13:30:21 +02:00
deadprogram 7755f2385c tools/gen-device-svd: small changes needed for Renesas MCUs
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-05 13:30:21 +02:00
deadprogram 04601a29e8 modules: add submodule for Renesas SVD file mirror repo
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-07-05 13:30:21 +02:00
Ayke van Laethem 9aadea930f main: improve detection of filesystems
This is a rewrite of how filesystems are detected. Specifically, it
fixes an issue on Linux where the location of the FAT filesystem can
vary between distributions (for example, we supported most distros by
checking two different paths, but NixOS uses a different path): it now
uses the data in /proc/mounts instead which should be universal.
2023-07-03 13:37:38 +02:00
sago35 6efa94035e machine/macropad_rp2040: add machine.BUTTON 2023-07-03 11:44:44 +02:00
Mansour Behabadi db8d80755f rp2040: add missing suffix to CMD_READ_STATUS 2023-07-03 10:52:33 +02:00
Yurii Soldak dc91c96305 example: adjust time offset 2023-07-02 18:25:20 +02:00
Damian Gryski 08b3a4576d compiler: update .ll test output 2023-07-02 15:35:42 +02:00
Damian Gryski acba0748f1 compiler,reflect: NumMethods reports exported methods only
Fixes #3796
2023-07-02 15:35:42 +02:00
Damian Gryski ef72c5bb4e reflect: fix iterating over maps with interface{} keys
Fixes #3794
2023-07-02 11:47:08 +02:00
Rado M 93cc03b15a docker: update clang to version 15 2023-07-02 10:54:23 +02:00
Tyler Rockwood fdc4bbbfad reflect: Add FieldByNameFunc
This adds FieldByNameFunc, which some libraries like reflect2 need.

For my usecase I could also just stub FieldByNameFunc to panic, but
figured that it would work OK to just make it work. I'm not sure if
the overhead to FieldByName using a closure is acceptable.

Signed-off-by: Tyler Rockwood <rockwood@redpanda.com>
2023-07-02 09:14:36 +02:00
soypat dd4e9e86e7 reflect: remove unecessary heap allocations 2023-07-01 21:03:10 +02:00
sago35 ad32d26511 machine/usb/hid,joystick: fix hidreport (3) (#3802)
* machine/usb/hid,joystick: fix hidreport (3) and handling of logical, usage, and physical minimum/maximum values
2023-07-01 11:58:23 +02:00
Ayke van Laethem 4619896c19 nrf: wait for stop condition after reading from the I2C bus
Found while working on the PineTime. For some reason it still kind of
works in most cases, but I was hitting this issue when interacting with
two different I2C devices (the touch sensor and the BMA421).
2023-06-22 10:41:05 +02:00
Yurii Soldak cec237917f example: simplify pininterrupt 2023-06-18 10:59:23 +02:00
Ayke van Laethem 4d2a6d2bbe wasm: remove i64 workaround, use BigInt instead
Browsers previously didn't support the WebAssembly i64 type, so we had
to work around that limitation by converting the LLVM i64 type to
something else. Some people used a pair of i32 values, but we used a
pointer to a stack allocated i64.

Now however, all major browsers and Node.js do support WebAssembly
BigInt integration so that i64 values can be passed back and forth
between WebAssembly and JavaScript easily. Therefore, I think the time
has come to drop support for this workaround.

For more information: https://v8.dev/features/wasm-bigint (note that
TinyGo has used a slightly different way of passing i64 values between
JS and Wasm).

For information on browser support: https://webassembly.org/roadmap/
2023-06-17 18:08:09 +02:00
Ayke van Laethem 6d5f4c4be2 ci: update Node.js from version 14 to version 16
Node.js 14 is not maintained anymore, so we can drop support for it.
2023-06-17 18:08:09 +02:00
Ayke van Laethem 0cb5d336f4 reflect: use .key() instead of a type assert
This should be ever so slightly more efficient.
2023-06-17 15:58:03 +02:00
Stepan Rakitin 91ee4d0030 os: define ErrNoDeadline 2023-06-17 10:38:48 +02:00
Ayke van Laethem 785eb93b62 ci: rename release-double-zipped to something more useful
The Linux artifacts have clear names (linux-amd64-double-zipped etc),
but the MacOS and Windows ones didn't. This patch renames these artifact
names to be more readable, especially when downloading the artifacts.
2023-06-16 21:32:22 +02:00
sago35 e041a8ed88 goenv: update to new v0.29.0 development version 2023-06-16 11:36:41 +02:00
soypat d01d85930d rp2040: add spi busy waits on read and read/write transactions 2023-06-11 22:24:26 +02:00
Ayke van Laethem ac821d8295 goenv: fix version number to 0.28.1 (without -dev) 2023-06-11 18:56:38 +02:00
197 changed files with 3427 additions and 1370 deletions
+2 -2
View File
@@ -10,12 +10,12 @@ commands:
steps:
- restore_cache:
keys:
- llvm-source-14-v3
- llvm-source-16-v3
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-14-v3
key: llvm-source-16-v3
paths:
- llvm-project/clang/lib/Headers
- llvm-project/clang/include
+6 -6
View File
@@ -27,13 +27,13 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-15-macos-v3
key: llvm-source-16-macos-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -58,7 +58,7 @@ jobs:
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-15-macos-v4
key: llvm-build-16-macos-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -108,7 +108,7 @@ jobs:
# We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v2
with:
name: release-double-zipped
name: darwin-amd64-double-zipped
path: build/tinygo.darwin-amd64.tar.gz
- name: Smoke tests
shell: bash
@@ -120,13 +120,13 @@ jobs:
- name: Install LLVM
shell: bash
run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@15
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@16
- name: Checkout
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Build TinyGo
run: go install
+1 -1
View File
@@ -53,7 +53,7 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-15
build-contexts: tinygo-llvm-build=docker-image://tinygo/llvm-16
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Trigger Drivers repo build on Github Actions
+15 -14
View File
@@ -18,7 +18,7 @@ jobs:
# statically linked binary.
runs-on: ubuntu-latest
container:
image: golang:1.20-alpine
image: golang:1.21-alpine
steps:
- name: Install apk dependencies
# tar: needed for actions/cache@v3
@@ -43,7 +43,7 @@ jobs:
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-15-linux-alpine-v3
key: llvm-source-16-linux-alpine-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -68,7 +68,7 @@ jobs:
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-15-linux-alpine-v4
key: llvm-build-16-linux-alpine-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -135,7 +135,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Install wasmtime
run: |
@@ -153,6 +153,7 @@ jobs:
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
- run: make tinygo-test-wasi-fast
- run: make tinygo-test-wasip1-fast
- run: make smoketest
assert-test-linux:
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
@@ -177,12 +178,12 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '14'
node-version: '16'
- name: Install wasmtime
run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
@@ -193,7 +194,7 @@ jobs:
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-15-linux-asserts-v3
key: llvm-source-16-linux-asserts-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -218,7 +219,7 @@ jobs:
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-15-linux-asserts-v4
key: llvm-build-16-linux-asserts-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -290,13 +291,13 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-15-linux-v3
key: llvm-source-16-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -321,7 +322,7 @@ jobs:
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-15-linux-arm-v4
key: llvm-build-16-linux-arm-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -407,13 +408,13 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-15-linux-v3
key: llvm-source-16-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -438,7 +439,7 @@ jobs:
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-15-linux-arm64-v4
key: llvm-build-16-linux-arm64-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
+2 -2
View File
@@ -35,8 +35,8 @@ jobs:
uses: docker/metadata-action@v4
with:
images: |
tinygo/llvm-15
ghcr.io/${{ github.repository_owner }}/llvm-15
tinygo/llvm-16
ghcr.io/${{ github.repository_owner }}/llvm-16
tags: |
type=sha,format=long
type=raw,value=latest
+6 -6
View File
@@ -24,19 +24,19 @@ jobs:
submodules: true
- name: Install apt dependencies
run: |
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main' | sudo tee /etc/apt/sources.list.d/llvm.list
echo 'deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 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 -y \
llvm-15-dev \
clang-15 \
libclang-15-dev \
lld-15
llvm-16-dev \
clang-16 \
libclang-16-dev \
lld-16
- name: Restore LLVM source cache
uses: actions/cache@v3
id: cache-llvm-source
with:
key: llvm-source-15-sizediff-v1
key: llvm-source-16-sizediff-v1
path: |
llvm-project/compiler-rt
- name: Download LLVM source
+10 -10
View File
@@ -35,13 +35,13 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Restore cached LLVM source
uses: actions/cache/restore@v3
id: cache-llvm-source
with:
key: llvm-source-15-windows-v4
key: llvm-source-16-windows-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
@@ -66,7 +66,7 @@ jobs:
uses: actions/cache/restore@v3
id: cache-llvm-build
with:
key: llvm-build-15-windows-v6
key: llvm-build-16-windows-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -118,7 +118,7 @@ jobs:
# We're doing the former here, to keep artifact uploads fast.
uses: actions/upload-artifact@v3
with:
name: release-double-zipped
name: windows-amd64-double-zipped
path: build/release/release.zip
smoke-test-windows:
@@ -143,12 +143,12 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
with:
name: release-double-zipped
name: windows-amd64-double-zipped
path: build/
- name: Unzip TinyGo build
shell: bash
@@ -173,12 +173,12 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
with:
name: release-double-zipped
name: windows-amd64-double-zipped
path: build/
- name: Unzip TinyGo build
shell: bash
@@ -209,12 +209,12 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
with:
name: release-double-zipped
name: windows-amd64-double-zipped
path: build/
- name: Unzip TinyGo build
shell: bash
+3
View File
@@ -31,3 +31,6 @@
[submodule "lib/macos-minimal-sdk"]
path = lib/macos-minimal-sdk
url = https://github.com/aykevl/macos-minimal-sdk.git
[submodule "lib/renesas-svd"]
path = lib/renesas-svd
url = https://github.com/tinygo-org/renesas-svd.git
+93
View File
@@ -1,3 +1,96 @@
0.30.0
---
* **general**
- add LLVM 16 support, use it by default
* **compiler**
- `build`: work around a race condition by building Go SSA serially
- `compiler`: fix a crash by not using the LLVM global context types
- `interp`: don't copy unknown values in `runtime.sliceCopy` to fix miscompile
- `interp`: fix crash in error report by not returning raw LLVM values
* **standard library**
- `machine/usb/adc/midi`: various improvements and API changes
- `reflect`: add support for `[...]T``[]T` in reflect
* **targets**
- `atsamd21`, `atsamd51`: add support for USB INTERRUPT OUT
- `rp2040`: always use the USB device enumeration fix, even in chips that supposedly have the HW fix
- `wasm`: increase default stack size to 32k for wasi/wasm
* **boards**
- `gobadge`: add GoBadge target as alias for PyBadge :)
- `gemma-m0`: add support for the Adafruit Gemma M0
0.29.0
---
* **general**
- Go 1.21 support
- use https for renesas submodule #3856
- ci: rename release-double-zipped to something more useful
- ci: update Node.js from version 14 to version 16
- ci: switch GH actions builds to use Go 1.21 final release
- docker: update clang to version 15
- docker: use Go 1.21 for Docker dev container build
- `main`: add target JSON file in `tinygo info` output
- `main`: improve detection of filesystems
- `main`: use `go env` instead of doing all detection manually
- make: add make task to generate Renesas device wrappers
- make: add task to check NodeJS version before running tests
- add submodule for Renesas SVD file mirror repo
- update to go-serial package v1.6.0
- `testing`: add Testing function
- `tools/gen-device-svd`: small changes needed for Renesas MCUs
* **compiler**
- `builder`: update message for max supported Go version
- `compiler,reflect`: NumMethods reports exported methods only
- `compiler`: add compiler-rt and wasm symbols to table
- `compiler`: add compiler-rt to wasm.json
- `compiler`: add min and max builtin support
- `compiler`: implement clear builtin for maps
- `compiler`: implement clear builtin for slices
- `compiler`: improve panic message when a runtime call is unavailable
- `compiler`: update .ll test output
- `loader`: merge go.env file which is now required starting in Go 1.21 to correctly get required packages
* **standard library**
- `os`: define ErrNoDeadline
- `reflect`: Add FieldByNameFunc
- `reflect`: add SetZero
- `reflect`: fix iterating over maps with interface{} keys
- `reflect`: implement Value.Grow
- `reflect`: remove unecessary heap allocations
- `reflect`: use .key() instead of a type assert
- `sync`: add implementation from upstream Go for OnceFunc, OnceValue, and OnceValues
* **targets**
- `machine`: UART refactor (#3832)
- `machine/avr`: pin change interrupt
- `machine/macropad_rp2040`: add machine.BUTTON
- `machine/nrf`: add I2C timeout
- `machine/nrf`: wait for stop condition after reading from the I2C bus
- `machine/nRF52`: set SPI TX/RX lengths even data is empty. Fixes #3868 (#3877)
- `machine/rp2040`: add missing suffix to CMD_READ_STATUS
- `machine/rp2040`: add NoPin support
- `machine/rp2040`: move flash related functions into separate file from C imports for correct - LSP. Fixes #3852
- `machine/rp2040`: wait for 1000 us after flash reset to avoid issues with busy USB bus
- `machine/samd51,rp2040,nrf528xx,stm32`: implement watchdog
- `machine/samd51`: fix i2cTimeout was decreasing due to cache activation
- `machine/usb`: Add support for HID Keyboard LEDs
- `machine/usb`: allow USB Endpoint settings to be changed externally
- `machine/usb`: refactor endpoint configuration
- `machine/usb`: remove usbDescriptorConfig
- `machine/usb/hid,joystick`: fix hidreport (3) (#3802)
- `machine/usb/hid`: add RxHandler interface
- `machine/usb/hid`: rename Handler() to TxHandler()
- `wasi`: allow zero inodes when reading directories
- `wasm`: add support for GOOS=wasip1
- `wasm`: fix functions exported through //export
- `wasm`: remove i64 workaround, use BigInt instead
- `example`: adjust time offset
- `example`: simplify pininterrupt
* **boards**
- `targets`: add AKIZUKI DENSHI AE-RP2040
- `targets`: adding new uf2 target for PCA10056 (#3765)
0.28.0
---
+2 -2
View File
@@ -1,8 +1,8 @@
# tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.20 AS tinygo-llvm
FROM golang:1.21 AS tinygo-llvm
RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-11 ninja-build
apt-get install -y apt-utils make cmake clang-15 ninja-build
COPY ./Makefile /tinygo/Makefile
+32 -6
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools.
# Versions are listed here in descending priority order.
LLVM_VERSIONS = 15 14 13 12 11
LLVM_VERSIONS = 16 15 14 13 12 11
errifempty = $(if $(1),$(1),$(error $(2)))
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
toolSearchPathsVersion = $(1)-$(2)
@@ -113,7 +113,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 debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendhlsl frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsdriver windowsmanifest
ifeq ($(OS),Windows_NT)
EXE = .exe
@@ -154,7 +154,7 @@ LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
# Other libraries that are needed to link TinyGo.
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMRISCVTargetMCA LLVMX86TargetMCA
# 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)
@@ -170,7 +170,7 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(ad
# For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(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_CXXFLAGS=-std=c++17
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
endif
@@ -232,9 +232,13 @@ gen-device-rp: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/RaspberryPi lib/cmsis-svd/data/RaspberryPi/ src/device/rp/
GO111MODULE=off $(GO) fmt ./src/device/rp
gen-device-renesas: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/tinygo-org/renesas-svd lib/renesas-svd/ src/device/renesas/
GO111MODULE=off $(GO) fmt ./src/device/renesas
# Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm:
git clone -b xtensa_release_15.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
git clone -b xtensa_release_16.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM.
@@ -263,12 +267,22 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
cd lib/wasi-libc && make -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
# Check for Node.js used during WASM tests.
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
MIN_NODEJS_VERSION=16
.PHONY: check-nodejs-version
check-nodejs-version:
ifeq (, $(shell which node))
@echo "Install NodeJS version 16+ to run tests."; exit 1;
endif
@if [ $(NODEJS_VERSION) -lt $(MIN_NODEJS_VERSION) ]; then echo "Install NodeJS version 16+ to run tests."; exit 1; fi
# Build the Go compiler.
tinygo:
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc
test: wasi-libc check-nodejs-version
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
@@ -413,8 +427,12 @@ tinygo-bench-fast:
# Same thing, except for wasi rather than the current platform.
tinygo-test-wasi:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
tinygo-test-wasip1:
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
tinygo-test-wasi-fast:
$(TINYGO) test -target wasi $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-test-wasip1-fast:
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-bench-wasi:
$(TINYGO) test -target wasi -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasi-fast:
@@ -478,12 +496,16 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/time-offset
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/i2c-target
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
@$(MD5SUM) test.hex
# test simulated boards on play.tinygo.org
ifneq ($(WASM), 0)
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
@@ -536,6 +558,8 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=trinket-m0 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=gemma-m0 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
@@ -642,6 +666,8 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=gopher-badge examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=ae-rp2040 examples/echo
@$(MD5SUM) test.hex
# test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex
+19 -1
View File
@@ -12,6 +12,7 @@ import (
"path/filepath"
"time"
wasm "github.com/aykevl/go-wasm"
"github.com/blakesmith/ar"
)
@@ -74,8 +75,25 @@ func makeArchive(arfile *os.File, objs []string) error {
fileIndex int
}{symbol.Name, i})
}
} else if dbg, err := wasm.Parse(objfile); err == nil {
for _, s := range dbg.Sections {
switch section := s.(type) {
case *wasm.SectionImport:
for _, ln := range section.Entries {
if ln.Kind != wasm.ExtKindFunction {
// Not a function
continue
}
symbolTable = append(symbolTable, struct {
name string
fileIndex int
}{ln.Field, i})
}
}
}
} else {
return fmt.Errorf("failed to open file %s as ELF or PE/COFF: %w", objpath, err)
return fmt.Errorf("failed to open file %s as WASM, ELF or PE/COFF: %w", objpath, err)
}
// Close file, to avoid issues with too many open files (especially on
+15 -14
View File
@@ -217,19 +217,27 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
var packageJobs []*compileJob
packageActionIDJobs := make(map[string]*compileJob)
if config.Options.GlobalValues == nil {
config.Options.GlobalValues = make(map[string]map[string]string)
}
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
version := goenv.Version
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
version += "-" + goenv.GitSha1
}
if config.Options.GlobalValues == nil {
config.Options.GlobalValues = make(map[string]map[string]string)
}
if config.Options.GlobalValues["runtime"] == nil {
config.Options.GlobalValues["runtime"] = make(map[string]string)
}
config.Options.GlobalValues["runtime"]["buildVersion"] = version
}
if config.TestConfig.CompileTestBinary {
// The testing.testBinary is set to "1" when in a test.
// This is needed for testing.Testing() to work correctly.
if config.Options.GlobalValues["testing"] == nil {
config.Options.GlobalValues["testing"] = make(map[string]string)
}
config.Options.GlobalValues["testing"]["testBinary"] = "1"
}
var embedFileObjects []*compileJob
for _, pkg := range lprogram.Sorted() {
@@ -336,6 +344,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
// Build the SSA for the given package.
ssaPkg := program.Package(pkg.Pkg)
ssaPkg.Build()
// Now create the job to actually build the package. It will exit early
// if the package is already compiled.
job := &compileJob{
@@ -1054,17 +1066,6 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
return err
}
// Browsers cannot handle external functions that have type i64 because it
// cannot be represented exactly in JavaScript (JS only has doubles). To
// keep functions interoperable, pass int64 types as pointers to
// stack-allocated values.
if config.WasmAbi() == "js" {
err := transform.ExternalInt64AsPtr(mod, config)
if err != nil {
return err
}
}
// Optimization levels here are roughly the same as Clang, but probably not
// exactly.
optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
+1
View File
@@ -60,6 +60,7 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "arm64"},
{GOOS: "wasip1", GOARCH: "wasm"},
} {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" {
+19 -2
View File
@@ -56,6 +56,7 @@
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <optional>
#include <system_error>
using namespace clang;
using namespace clang::driver;
@@ -103,6 +104,14 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple))
Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue());
if (Arg *A = Args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) {
VersionTuple Version;
if (Version.tryParse(A->getValue()))
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
else
Opts.DarwinTargetVariantSDKVersion = Version;
}
Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu));
Opts.Features = Args.getAllArgValues(OPT_target_feature);
@@ -122,11 +131,12 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.CompressDebugSections =
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
.Case("none", llvm::DebugCompressionType::None)
.Case("zlib", llvm::DebugCompressionType::Z)
.Case("zlib", llvm::DebugCompressionType::Zlib)
.Case("zstd", llvm::DebugCompressionType::Zstd)
.Default(llvm::DebugCompressionType::None);
}
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
Opts.RelaxELFRelocations = !Args.hasArg(OPT_mrelax_relocations_no);
if (auto *DwarfFormatArg = Args.getLastArg(OPT_gdwarf64, OPT_gdwarf32))
Opts.Dwarf64 = DwarfFormatArg->getOption().matches(OPT_gdwarf64);
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
@@ -189,6 +199,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn);
Opts.NoTypeCheck = Args.hasArg(OPT_mno_type_check);
Opts.RelocationModel =
std::string(Args.getLastArgValue(OPT_mrelocation_model, "pic"));
Opts.TargetABI = std::string(Args.getLastArgValue(OPT_target_abi));
@@ -214,6 +225,8 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
.Case("default", EmitDwarfUnwindType::Default);
}
Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
return Success;
}
@@ -265,6 +278,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MCTargetOptions MCOptions;
MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions));
@@ -314,6 +328,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
TheTarget->createMCObjectFileInfo(Ctx, PIC));
if (Opts.DarwinTargetVariantTriple)
MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple);
if (!Opts.DarwinTargetVariantSDKVersion.empty())
MOFI->setDarwinTargetVariantSDKVersion(Opts.DarwinTargetVariantSDKVersion);
Ctx.setObjectFileInfo(MOFI.get());
if (Opts.SaveTemporaryLabels)
@@ -353,6 +369,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
MCOptions.MCNoWarn = Opts.NoWarn;
MCOptions.MCFatalWarnings = Opts.FatalWarnings;
MCOptions.MCNoTypeCheck = Opts.NoTypeCheck;
MCOptions.ABIName = Opts.TargetABI;
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
+10 -1
View File
@@ -82,6 +82,7 @@ struct AssemblerInvocation {
unsigned NoExecStack : 1;
unsigned FatalWarnings : 1;
unsigned NoWarn : 1;
unsigned NoTypeCheck : 1;
unsigned IncrementalLinkerCompatible : 1;
unsigned EmbedBitcode : 1;
@@ -97,7 +98,14 @@ struct AssemblerInvocation {
/// Darwin target variant triple, the variant of the deployment target
/// for which the code is being compiled.
llvm::Optional<llvm::Triple> DarwinTargetVariantTriple;
std::optional<llvm::Triple> DarwinTargetVariantTriple;
/// The version of the darwin target variant SDK which was used during the
/// compilation
llvm::VersionTuple DarwinTargetVariantSDKVersion;
/// The name of a file to use with \c .secure_log_unique directives.
std::string AsSecureLogFile;
/// @}
public:
@@ -114,6 +122,7 @@ public:
NoExecStack = 0;
FatalWarnings = 0;
NoWarn = 0;
NoTypeCheck = 0;
IncrementalLinkerCompatible = 0;
Dwarf64 = 0;
DwarfVersion = 0;
+4 -10
View File
@@ -1,7 +1,6 @@
package builder
import (
"errors"
"fmt"
"github.com/tinygo-org/tinygo/compileopts"
@@ -24,19 +23,14 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
spec.OpenOCDCommands = options.OpenOCDCommands
}
goroot := goenv.Get("GOROOT")
if goroot == "" {
return nil, errors.New("cannot locate $GOROOT, please set it manually")
}
major, minor, err := goenv.GetGorootVersion(goroot)
major, minor, err := goenv.GetGorootVersion()
if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
return nil, err
}
if major != 1 || minor < 18 || minor > 20 {
if major != 1 || minor < 18 || minor > 21 {
// Note: when this gets updated, also update the Go compatibility matrix:
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
return nil, fmt.Errorf("requires go version 1.18 through 1.20, got go%d.%d", major, minor)
return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor)
}
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
+3 -3
View File
@@ -41,9 +41,9 @@ func TestBinarySize(t *testing.T) {
// This is a small number of very diverse targets that we want to test.
tests := []sizeTest{
// microcontrollers
{"hifive1b", "examples/echo", 4612, 280, 0, 2252},
{"microbit", "examples/serial", 2724, 388, 8, 2256},
{"wioterminal", "examples/pininterrupt", 6159, 1485, 116, 6816},
{"hifive1b", "examples/echo", 4568, 280, 0, 2252},
{"microbit", "examples/serial", 2728, 388, 8, 2256},
{"wioterminal", "examples/pininterrupt", 5996, 1484, 116, 6816},
// TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the
+16 -2
View File
@@ -60,6 +60,7 @@ CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c);
unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
@@ -653,8 +654,19 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
// Strip typedefs, if any.
underlyingType := typ
if underlyingType.kind == C.CXType_Elaborated {
// Starting with LLVM 16, the elaborated type is used for more types.
// According to the Clang documentation, the elaborated type has no
// semantic meaning so can be stripped (it is used to better convey type
// name information).
// Source:
// https://clang.llvm.org/doxygen/classclang_1_1ElaboratedType.html#details
// > The type itself is always "sugar", used to express what was written
// > in the source code but containing no additional semantic information.
underlyingType = C.clang_Type_getNamedType(underlyingType)
}
if underlyingType.kind == C.CXType_Typedef {
c := C.tinygo_clang_getTypeDeclaration(typ)
c := C.tinygo_clang_getTypeDeclaration(underlyingType)
underlyingType = C.tinygo_clang_getTypedefDeclUnderlyingType(c)
// TODO: support a chain of typedefs. At the moment, it seems to get
// stuck in an endless loop when trying to get to the most underlying
@@ -788,6 +800,8 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
return f.makeASTType(underlying, pos)
case C.CXType_Enum:
return f.makeASTType(underlying, pos)
case C.CXType_Typedef:
return f.makeASTType(underlying, pos)
default:
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
@@ -806,7 +820,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
// makeASTRecordType will create an appropriate error.
cgoRecordPrefix = "record_"
}
if name == "" {
if name == "" || C.tinygo_clang_Cursor_isAnonymous(cursor) != 0 {
// Anonymous record, probably inside a typedef.
location := f.getUniqueLocationID(pos, cursor)
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !byollvm && !llvm14
//go:build !byollvm && llvm15
package cgo
+21
View File
@@ -0,0 +1,21 @@
//go:build !byollvm && !llvm14 && !llvm15
package cgo
// As of 2023-05-05, there is a packaging issue on Debian:
// https://github.com/llvm/llvm-project/issues/62199
// A workaround is to fix this locally, using something like this:
//
// ln -sf ../../x86_64-linux-gnu/libclang-16.so.1 /usr/lib/llvm-16/lib/libclang.so
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-16/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@16/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@16/include
#cgo freebsd CFLAGS: -I/usr/local/llvm16/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-16/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
*/
import "C"
+5 -1
View File
@@ -77,6 +77,10 @@ CXType tinygo_clang_getEnumDeclIntegerType(CXCursor c) {
return clang_getEnumDeclIntegerType(c);
}
unsigned tinygo_clang_Cursor_isAnonymous(CXCursor c) {
return clang_Cursor_isAnonymous(c);
}
unsigned tinygo_clang_Cursor_isBitField(CXCursor c) {
return clang_Cursor_isBitField(c);
}
}
+2 -2
View File
@@ -51,10 +51,10 @@ type (
C.longlong int64
C.ulonglong uint64
)
type C._Ctype_struct___0 struct {
type C.struct_point_t struct {
x C.int
y C.int
}
type C.point_t = C._Ctype_struct___0
type C.point_t = C.struct_point_t
const C.SOME_CONST_3 = 1234
+34 -36
View File
@@ -38,11 +38,11 @@ type (
C.ulonglong uint64
)
type C.myint = C.int
type C._Ctype_struct___0 struct {
type C.struct_point2d_t struct {
x C.int
y C.int
}
type C.point2d_t = C._Ctype_struct___0
type C.point2d_t = C.struct_point2d_t
type C.struct_point3d struct {
x C.int
y C.int
@@ -55,21 +55,19 @@ type C.struct_type1 struct {
___type C.int
}
type C.struct_type2 struct{ _type C.int }
type C._Ctype_union___1 struct{ i C.int }
type C.union1_t = C._Ctype_union___1
type C._Ctype_union___2 struct{ $union uint64 }
type C.union_union1_t struct{ i C.int }
type C.union1_t = C.union_union1_t
type C.union_union3_t struct{ $union uint64 }
func (union *C._Ctype_union___2) unionfield_i() *C.int {
return (*C.int)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___2) unionfield_d() *float64 {
func (union *C.union_union3_t) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_union3_t) unionfield_d() *float64 {
return (*float64)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___2) unionfield_s() *C.short {
func (union *C.union_union3_t) unionfield_s() *C.short {
return (*C.short)(unsafe.Pointer(&union.$union))
}
type C.union3_t = C._Ctype_union___2
type C.union3_t = C.union_union3_t
type C.union_union2d struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
@@ -78,50 +76,50 @@ func (union *C.union_union2d) unionfield_d() *[2]float64 {
}
type C.union2d_t = C.union_union2d
type C._Ctype_union___3 struct{ arr [10]C.uchar }
type C.unionarray_t = C._Ctype_union___3
type C._Ctype_union___5 struct{ $union [3]uint32 }
type C.union_unionarray_t struct{ arr [10]C.uchar }
type C.unionarray_t = C.union_unionarray_t
type C._Ctype_union___0 struct{ $union [3]uint32 }
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
func (union *C._Ctype_union___0) unionfield_area() *C.point2d_t {
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
func (union *C._Ctype_union___0) unionfield_solid() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
type C._Ctype_struct___4 struct {
type C.struct_struct_nested_t struct {
begin C.point2d_t
end C.point2d_t
tag C.int
coord C._Ctype_union___5
coord C._Ctype_union___0
}
type C.struct_nested_t = C._Ctype_struct___4
type C._Ctype_union___6 struct{ $union [2]uint64 }
type C.struct_nested_t = C.struct_struct_nested_t
type C.union_union_nested_t struct{ $union [2]uint64 }
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
func (union *C.union_union_nested_t) unionfield_point() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
func (union *C.union_union_nested_t) unionfield_array() *C.unionarray_t {
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
func (union *C.union_union_nested_t) unionfield_thing() *C.union3_t {
return (*C.union3_t)(unsafe.Pointer(&union.$union))
}
type C.union_nested_t = C._Ctype_union___6
type C.union_nested_t = C.union_union_nested_t
type C.enum_option = C.int
type C.option_t = C.enum_option
type C._Ctype_enum___7 = C.uint
type C.option2_t = C._Ctype_enum___7
type C._Ctype_struct___8 struct {
type C.enum_option2_t = C.uint
type C.option2_t = C.enum_option2_t
type C.struct_types_t struct {
f float32
d float64
ptr *C.int
}
type C.types_t = C._Ctype_struct___8
type C.types_t = C.struct_types_t
type C.myIntArray = [10]C.int
type C._Ctype_struct___9 struct {
type C.struct_bitfield_t struct {
start C.uchar
__bitfield_1 C.uchar
@@ -129,21 +127,21 @@ type C._Ctype_struct___9 struct {
e C.uchar
}
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
func (s *C.struct_bitfield_t) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
func (s *C.struct_bitfield_t) set_bitfield_a(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
}
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
func (s *C.struct_bitfield_t) bitfield_b() C.uchar {
return s.__bitfield_1 >> 5 & 0x1
}
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
func (s *C.struct_bitfield_t) set_bitfield_b(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
}
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
func (s *C.struct_bitfield_t) bitfield_c() C.uchar {
return s.__bitfield_1 >> 6
}
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
func (s *C.struct_bitfield_t) set_bitfield_c(value C.uchar,
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
type C.bitfield_t = C._Ctype_struct___9
type C.bitfield_t = C.struct_bitfield_t
-5
View File
@@ -492,11 +492,6 @@ func (c *Config) RelocationModel() string {
return "static"
}
// WasmAbi returns the WASM ABI which is specified in the target JSON file.
func (c *Config) WasmAbi() string {
return c.Target.WasmAbi
}
// EmulatorName is a shorthand to get the command for this emulator, something
// like qemu-system-arm or simavr.
func (c *Config) EmulatorName() string {
+75 -42
View File
@@ -23,46 +23,45 @@ import (
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html
// https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo/blob/master/blink/arduino.json
type TargetSpec struct {
Inherits []string `json:"inherits"`
Triple string `json:"llvm-target"`
CPU string `json:"cpu"`
ABI string `json:"target-abi"` // rougly equivalent to -mabi= flag
Features string `json:"features"`
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
BuildTags []string `json:"build-tags"`
GC string `json:"gc"`
Scheduler string `json:"scheduler"`
Serial string `json:"serial"` // which serial output to use (uart, usb, none)
Linker string `json:"linker"`
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
Libc string `json:"libc"`
AutoStackSize *bool `json:"automatic-stack-size"` // Determine stack size automatically at compile time.
DefaultStackSize uint64 `json:"default-stack-size"` // Default stack size if the size couldn't be determined at compile time.
CFlags []string `json:"cflags"`
LDFlags []string `json:"ldflags"`
LinkerScript string `json:"linkerscript"`
ExtraFiles []string `json:"extra-files"`
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
Emulator string `json:"emulator"`
FlashCommand string `json:"flash-command"`
GDB []string `json:"gdb"`
PortReset string `json:"flash-1200-bps-reset"`
SerialPort []string `json:"serial-port"` // serial port IDs in the form "vid:pid"
FlashMethod string `json:"flash-method"`
FlashVolume []string `json:"msd-volume-name"`
FlashFilename string `json:"msd-firmware-name"`
UF2FamilyID string `json:"uf2-family-id"`
BinaryFormat string `json:"binary-format"`
OpenOCDInterface string `json:"openocd-interface"`
OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"`
OpenOCDCommands []string `json:"openocd-commands"`
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
JLinkDevice string `json:"jlink-device"`
CodeModel string `json:"code-model"`
RelocationModel string `json:"relocation-model"`
WasmAbi string `json:"wasm-abi"`
Inherits []string `json:"inherits,omitempty"`
Triple string `json:"llvm-target,omitempty"`
CPU string `json:"cpu,omitempty"`
ABI string `json:"target-abi,omitempty"` // rougly equivalent to -mabi= flag
Features string `json:"features,omitempty"`
GOOS string `json:"goos,omitempty"`
GOARCH string `json:"goarch,omitempty"`
BuildTags []string `json:"build-tags,omitempty"`
GC string `json:"gc,omitempty"`
Scheduler string `json:"scheduler,omitempty"`
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
Linker string `json:"linker,omitempty"`
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
Libc string `json:"libc,omitempty"`
AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time.
DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
CFlags []string `json:"cflags,omitempty"`
LDFlags []string `json:"ldflags,omitempty"`
LinkerScript string `json:"linkerscript,omitempty"`
ExtraFiles []string `json:"extra-files,omitempty"`
RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum
Emulator string `json:"emulator,omitempty"`
FlashCommand string `json:"flash-command,omitempty"`
GDB []string `json:"gdb,omitempty"`
PortReset string `json:"flash-1200-bps-reset,omitempty"`
SerialPort []string `json:"serial-port,omitempty"` // serial port IDs in the form "vid:pid"
FlashMethod string `json:"flash-method,omitempty"`
FlashVolume []string `json:"msd-volume-name,omitempty"`
FlashFilename string `json:"msd-firmware-name,omitempty"`
UF2FamilyID string `json:"uf2-family-id,omitempty"`
BinaryFormat string `json:"binary-format,omitempty"`
OpenOCDInterface string `json:"openocd-interface,omitempty"`
OpenOCDTarget string `json:"openocd-target,omitempty"`
OpenOCDTransport string `json:"openocd-transport,omitempty"`
OpenOCDCommands []string `json:"openocd-commands,omitempty"`
OpenOCDVerify *bool `json:"openocd-verify,omitempty"` // enable verify when flashing with openocd
JLinkDevice string `json:"jlink-device,omitempty"`
CodeModel string `json:"code-model,omitempty"`
RelocationModel string `json:"relocation-model,omitempty"`
}
// overrideProperties overrides all properties that are set in child into itself using reflection.
@@ -192,12 +191,15 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
default:
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
}
case "wasm":
llvmarch = "wasm32"
default:
llvmarch = options.GOARCH
}
llvmvendor := "unknown"
llvmos := options.GOOS
if llvmos == "darwin" {
switch llvmos {
case "darwin":
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
// to iOS!
llvmos = "macosx10.12.0"
@@ -208,6 +210,8 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
llvmos = "macosx11.0.0"
}
llvmvendor = "apple"
case "wasip1":
llvmos = "wasi"
}
// Target triples (which actually have four components, but are called
// triples for historical reasons) have the form:
@@ -277,7 +281,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
}
case "arm64":
spec.CPU = "generic"
spec.Features = "+neon"
if goos == "darwin" {
spec.Features = "+neon"
} else { // windows, linux
spec.Features = "+neon,-fmv"
}
case "wasm":
spec.CPU = "generic"
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
spec.BuildTags = append(spec.BuildTags, "tinygo.wasm")
spec.CFlags = append(spec.CFlags,
"-mbulk-memory",
"-mnontrapping-fptoint",
"-msign-ext",
)
}
if goos == "darwin" {
spec.Linker = "ld.lld"
@@ -321,6 +338,22 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
"--no-insert-timestamp",
"--no-dynamicbase",
)
} else if goos == "wasip1" {
spec.GC = "" // use default GC
spec.Scheduler = "asyncify"
spec.Linker = "wasm-ld"
spec.RTLib = "compiler-rt"
spec.Libc = "wasi-libc"
spec.DefaultStackSize = 1024 * 32 // 32kB
spec.LDFlags = append(spec.LDFlags,
"--stack-first",
"--no-demangle",
)
spec.Emulator = "wasmtime --mapdir=/tmp::{tmpDir} {}"
spec.ExtraFiles = append(spec.ExtraFiles,
"src/runtime/asm_tinygowasm.S",
"src/internal/task/task_asyncify_wasm.S",
)
} else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
}
+5 -1
View File
@@ -36,7 +36,11 @@ const (
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
// createRuntimeInvoke instead.
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
member := b.program.ImportedPackage("runtime").Members[fnName]
if member == nil {
panic("unknown runtime call: " + fnName)
}
fn := member.(*ssa.Function)
fnType, llvmFn := b.getFunction(fn)
if llvmFn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil))
+67
View File
@@ -1600,6 +1600,45 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx, nil
case "clear":
value := argValues[0]
switch typ := argTypes[0].Underlying().(type) {
case *types.Slice:
elementType := b.getLLVMType(typ.Elem())
elementSize := b.targetData.TypeAllocSize(elementType)
elementAlign := b.targetData.ABITypeAlignment(elementType)
// The pointer to the data to be cleared.
llvmBuf := b.CreateExtractValue(value, 0, "buf")
if llvmBuf.Type() != b.i8ptrType { // compatibility with LLVM 14
llvmBuf = b.CreateBitCast(llvmBuf, b.i8ptrType, "")
}
// The length (in bytes) to be cleared.
llvmLen := b.CreateExtractValue(value, 1, "len")
llvmLen = b.CreateMul(llvmLen, llvm.ConstInt(llvmLen.Type(), elementSize, false), "")
// Do the clear operation using the LLVM memset builtin.
// This is also correct for nil slices: in those cases, len will be
// 0 which means the memset call is a no-op (according to the LLVM
// LangRef).
memset := b.getMemsetFunc()
call := b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
llvmBuf, // dest
llvm.ConstInt(b.ctx.Int8Type(), 0, false), // val
llvmLen, // len
llvm.ConstInt(b.ctx.Int1Type(), 0, false), // isVolatile
}, "")
call.AddCallSiteAttribute(1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elementAlign)))
return llvm.Value{}, nil
case *types.Map:
m := argValues[0]
b.createMapClear(m)
return llvm.Value{}, nil
default:
return llvm.Value{}, b.makeError(pos, "unsupported type in clear builtin: "+typ.String())
}
case "copy":
dst := argValues[0]
src := argValues[1]
@@ -1637,6 +1676,24 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int")
}
return llvmLen, nil
case "min", "max":
// min and max builtins, added in Go 1.21.
// We can simply reuse the existing binop comparison code, which has all
// the edge cases figured out already.
tok := token.LSS
if callName == "max" {
tok = token.GTR
}
result := argValues[0]
typ := argTypes[0]
for _, arg := range argValues[1:] {
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
if err != nil {
return result, err
}
result = b.CreateSelect(cmp, result, arg, "")
}
return result, nil
case "print", "println":
for i, value := range argValues {
if i >= 1 && callName == "println" {
@@ -2948,6 +3005,11 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
panic("expected nil pointer constant")
}
return llvm.ConstPointerNull(c.getLLVMType(typ))
case *types.Array:
if expr.Value != nil {
panic("expected nil array constant")
}
return llvm.ConstNull(c.getLLVMType(expr.Type()))
case *types.Slice:
if expr.Value != nil {
panic("expected nil slice constant")
@@ -2961,6 +3023,11 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value
llvmLen, // cap
}, false)
return slice
case *types.Struct:
if expr.Value != nil {
panic("expected nil struct constant")
}
return llvm.ConstNull(c.getLLVMType(expr.Type()))
case *types.Map:
if !expr.IsNil() {
// I believe this is not allowed by the Go spec.
+4 -1
View File
@@ -29,7 +29,7 @@ func TestCompiler(t *testing.T) {
t.Parallel()
// Determine Go minor version (e.g. 16 in go1.16.3).
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
_, goMinor, err := goenv.GetGorootVersion()
if err != nil {
t.Fatal("could not read Go version:", err)
}
@@ -54,6 +54,9 @@ func TestCompiler(t *testing.T) {
if goMinor >= 20 {
tests = append(tests, testCase{"go1.20.go", "", ""})
}
if goMinor >= 21 {
tests = append(tests, testCase{"go1.21.go", "", ""})
}
for _, tc := range tests {
name := tc.file
+22 -11
View File
@@ -128,6 +128,15 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
hasMethodSet = false
}
var numMethods int
if hasMethodSet {
for i := 0; i < ms.Len(); i++ {
if ms.At(i).Obj().Exported() {
numMethods++
}
}
}
// Short-circuit all the global pointer logic here for pointers to pointers.
if typ, ok := typ.(*types.Pointer); ok {
if _, ok := typ.Elem().(*types.Pointer); ok {
@@ -138,7 +147,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.addError(token.NoPos, fmt.Sprintf("too many levels of pointers for typecode: %s", typstr))
}
return llvm.ConstGEP(c.ctx.Int8Type(), ptr, []llvm.Value{
llvm.ConstInt(llvm.Int32Type(), 1, false),
llvm.ConstInt(c.ctx.Int32Type(), 1, false),
})
}
}
@@ -209,6 +218,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
types.NewVar(token.NoPos, nil, "sliceOf", types.Typ[types.UnsafePointer]),
)
case *types.Map:
typeFieldTypes = append(typeFieldTypes,
@@ -277,11 +287,11 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
}
pkgPathPtr := c.pkgPathPtr(pkgpath)
typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Underlying()), // underlying
pkgPathPtr, // pkgpath pointer
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Underlying()), // underlying
pkgPathPtr, // pkgpath pointer
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
}
metabyte |= 1 << 5 // "named" flag
case *types.Chan:
@@ -308,7 +318,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
}
case *types.Pointer:
typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(typ.Elem()),
}
case *types.Array:
@@ -317,6 +327,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elementType
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
c.getTypeCode(types.NewSlice(typ.Elem())), // slicePtr
}
case *types.Map:
typeFields = []llvm.Value{
@@ -337,8 +348,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
llvmStructType := c.getLLVMType(typ)
size := c.targetData.TypeStoreSize(llvmStructType)
typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), uint64(ms.Len()), false), // numMethods
c.getTypeCode(types.NewPointer(typ)), // ptrTo
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(types.NewPointer(typ)), // ptrTo
pkgPathPtr,
llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
@@ -441,8 +452,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
offset = 1
}
return llvm.ConstGEP(global.GlobalValueType(), global, []llvm.Value{
llvm.ConstInt(llvm.Int32Type(), 0, false),
llvm.ConstInt(llvm.Int32Type(), offset, false),
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), offset, false),
})
}
+15 -9
View File
@@ -70,15 +70,7 @@ func (b *builder) createMemoryCopyImpl() {
// regular libc memset calls if they aren't optimized out in a different way.
func (b *builder) createMemoryZeroImpl() {
b.createFunctionStart(true)
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
}
llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
}
llvmFn := b.getMemsetFunc()
params := []llvm.Value{
b.getValue(b.fn.Params[0], getPos(b.fn)),
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
@@ -89,6 +81,20 @@ func (b *builder) createMemoryZeroImpl() {
b.CreateRetVoid()
}
// Return the llvm.memset.p0.i8 function declaration.
func (c *compilerContext) getMemsetFunc() llvm.Value {
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
}
llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
}
return llvmFn
}
// createKeepAlive creates the runtime.KeepAlive function. It is implemented
// using inline assembly.
func (b *builder) createKeepAliveImpl() {
+5
View File
@@ -185,6 +185,11 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
}
}
// Clear the given map.
func (b *builder) createMapClear(m llvm.Value) {
b.createRuntimeCall("hashmapClear", []llvm.Value{m}, "")
}
// createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
// map. It returns a tuple of {bool, key, value} with the result of the
// iteration.
+15 -34
View File
@@ -23,9 +23,9 @@ import (
// The linkName value contains a valid link name, even if //go:linkname is not
// present.
type functionInfo struct {
module string // go:wasm-module
importName string // go:linkname, go:export - The name the developer assigns
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
wasmModule string // go:wasm-module
wasmName string // wasm-export-name or wasm-import-name in the IR
linkName string // go:linkname, go:export - the IR function name
section string // go:section - object file section name
exported bool // go:export, CGo
interrupt bool // go:interrupt
@@ -180,7 +180,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
if strings.Split(c.Triple, "-")[0] == "avr" {
// These functions are compiler-rt/libgcc functions that are
// currently implemented in Go. Assembly versions should appear in
// LLVM 16 hopefully. Until then, they need to be made available to
// LLVM 17 hopefully. Until then, they need to be made available to
// the linker and the best way to do that is llvm.compiler.used.
// I considered adding a pragma for this, but the LLVM language
// reference explicitly says that this feature should not be exposed
@@ -194,20 +194,14 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// External/exported functions may not retain pointer values.
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
if info.exported {
if c.archFamily() == "wasm32" {
if c.archFamily() == "wasm32" && len(fn.Blocks) == 0 {
// We need to add the wasm-import-module and the wasm-import-name
// attributes.
module := info.module
if module == "" {
module = "env"
if info.wasmModule != "" {
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", info.wasmModule))
}
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", module))
name := info.importName
if name == "" {
name = info.linkName
}
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", name))
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName))
}
nocaptureKind := llvm.AttributeKindID("nocapture")
nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0)
@@ -260,10 +254,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
return
}
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
// Our importName for a wasm module (if we are compiling to wasm), or llvm link name
var importName string
for _, comment := range decl.Doc.List {
text := comment.Text
if strings.HasPrefix(text, "//export ") {
@@ -281,7 +271,8 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
continue
}
importName = parts[1]
info.linkName = parts[1]
info.wasmName = info.linkName
info.exported = true
case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) {
@@ -289,10 +280,11 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
}
case "//go:wasm-module":
// Alternative comment for setting the import module.
// This is deprecated, use //go:wasmimport instead.
if len(parts) != 2 {
continue
}
info.module = parts[1]
info.wasmModule = parts[1]
case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function.
// Original proposal: https://github.com/golang/go/issues/38248
@@ -302,8 +294,8 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
}
c.checkWasmImport(f, comment.Text)
info.exported = true
info.module = parts[1]
info.importName = parts[2]
info.wasmModule = parts[1]
info.wasmName = parts[2]
case "//go:inline":
info.inline = inlineHint
case "//go:noinline":
@@ -347,17 +339,6 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
}
}
}
// Set the importName for our exported function if we have one
if importName != "" {
if info.module == "" {
info.linkName = importName
} else {
// WebAssembly import
info.importName = importName
}
}
}
}
@@ -367,7 +348,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
// The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
if c.pkg.Path() == "runtime" {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" {
// The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers).
return
+3 -12
View File
@@ -196,10 +196,6 @@ entry:
; Function Attrs: nounwind
define hidden void @main.foo(ptr %context) unnamed_addr #2 {
entry:
%complit = alloca %main.kv.0, align 8
%stackalloc = alloca i8, align 1
store %main.kv.0 zeroinitializer, ptr %complit, align 8
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3
call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef)
ret void
}
@@ -207,15 +203,10 @@ entry:
; Function Attrs: nounwind
define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 {
entry:
%b1 = alloca %main.kv.0, align 8
%stackalloc = alloca i8, align 1
store %main.kv.0 zeroinitializer, ptr %b1, align 8
call void @runtime.trackPointer(ptr nonnull %b1, ptr nonnull %stackalloc, ptr undef) #3
store %main.kv.0 %b, ptr %b1, align 8
ret void
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind }
+6 -9
View File
@@ -31,12 +31,12 @@ entry:
ret void
}
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
; Function Attrs: nounwind
@@ -57,10 +57,7 @@ declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferen
; Function Attrs: nounwind
define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 {
entry:
%complit = alloca {}, align 8
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #4
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList)
call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList)
@@ -110,8 +107,8 @@ select.body: ; preds = %select.next
declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #1
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { argmemonly nocallback nofree nosync nounwind willreturn }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #4 = { nounwind }
+3 -3
View File
@@ -93,6 +93,6 @@ entry:
ret i8 %0
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
+3 -3
View File
@@ -44,7 +44,7 @@ entry:
ret void
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind }
+3 -3
View File
@@ -135,7 +135,7 @@ entry:
ret %runtime._interface %1
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind }
+3 -3
View File
@@ -57,7 +57,7 @@ entry:
ret ptr %s.data
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind }
+65
View File
@@ -0,0 +1,65 @@
package main
func min1(a int) int {
return min(a)
}
func min2(a, b int) int {
return min(a, b)
}
func min3(a, b, c int) int {
return min(a, b, c)
}
func min4(a, b, c, d int) int {
return min(a, b, c, d)
}
func minUint8(a, b uint8) uint8 {
return min(a, b)
}
func minUnsigned(a, b uint) uint {
return min(a, b)
}
func minFloat32(a, b float32) float32 {
return min(a, b)
}
func minFloat64(a, b float64) float64 {
return min(a, b)
}
func minString(a, b string) string {
return min(a, b)
}
func maxInt(a, b int) int {
return max(a, b)
}
func maxUint(a, b uint) uint {
return max(a, b)
}
func maxFloat32(a, b float32) float32 {
return max(a, b)
}
func maxString(a, b string) string {
return max(a, b)
}
func clearSlice(s []int) {
clear(s)
}
func clearZeroSizedSlice(s []struct{}) {
clear(s)
}
func clearMap(m map[string]int) {
clear(m)
}
+179
View File
@@ -0,0 +1,179 @@
; ModuleID = 'go1.21.go'
source_filename = "go1.21.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 }
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
entry:
ret i32 %a
}
; Function Attrs: nounwind
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
ret i32 %1
}
; Function Attrs: nounwind
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
%2 = call i32 @llvm.smin.i32(i32 %1, i32 %d)
ret i32 %2
}
; Function Attrs: nounwind
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
ret i8 %0
}
; Function Attrs: nounwind
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
entry:
%0 = fcmp olt float %a, %b
%1 = select i1 %0, float %a, float %b
ret float %1
}
; Function Attrs: nounwind
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
entry:
%0 = fcmp olt double %a, %b
%1 = select i1 %0, double %a, double %b
ret double %1
}
; Function Attrs: nounwind
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
%stackalloc = alloca i8, align 1
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
%6 = extractvalue %runtime._string %5, 0
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._string %5
}
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
; Function Attrs: nounwind
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
entry:
%0 = fcmp ogt float %a, %b
%1 = select i1 %0, float %a, float %b
ret float %1
}
; Function Attrs: nounwind
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
%stackalloc = alloca i8, align 1
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
%6 = extractvalue %runtime._string %5, 0
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._string %5
}
; Function Attrs: nounwind
define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
entry:
%0 = shl i32 %s.len, 2
call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false)
ret void
}
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
; Function Attrs: nounwind
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
entry:
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
ret void
}
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smin.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i8 @llvm.umin.i8(i8, i8) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smax.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umax.i32(i32, i32) #4
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #5 = { nounwind }
+14 -14
View File
@@ -21,7 +21,7 @@ entry:
; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 16384, ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
ret void
}
@@ -43,7 +43,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 16384, ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
ret void
}
@@ -76,7 +76,7 @@ entry:
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
store ptr %n, ptr %1, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
%2 = load i32, ptr %n, align 4
call void @runtime.printint32(i32 %2, ptr undef) #9
ret void
@@ -113,7 +113,7 @@ entry:
store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
store ptr %fn.funcptr, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 16384, ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 32768, ptr undef) #9
ret void
}
@@ -167,7 +167,7 @@ entry:
store i32 4, ptr %.repack1, align 4
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
store ptr %itf.typecode, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 16384, ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
ret void
}
@@ -188,13 +188,13 @@ entry:
unreachable
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
attributes #4 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #6 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
attributes #7 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #8 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" }
attributes #4 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #6 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" }
attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #8 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #9 = { nounwind }
+8 -8
View File
@@ -9,7 +9,7 @@ target triple = "wasm32-unknown-wasi"
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 0, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
@@ -130,11 +130,11 @@ entry:
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
attributes #4 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
attributes #5 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
attributes #6 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #7 = { nounwind }
+3 -3
View File
@@ -44,7 +44,7 @@ entry:
ret ptr %x
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind }
+13
View File
@@ -62,6 +62,19 @@ func exportedFunctionInSection() {
//go:wasmimport modulename import1
func declaredImport()
// Legacy way of importing a function.
//
//go:wasm-module foobar
//export imported
func foobarImport()
// The wasm-module pragma is not functional here, but it should be safe.
//
//go:wasm-module foobar
//export exported
func foobarExportModule() {
}
// This function should not: it's only a declaration and not a definition.
//
//go:section .special_function_section
+19 -9
View File
@@ -6,7 +6,7 @@ target triple = "wasm32-unknown-wasi"
@extern_global = external global [0 x i8], align 1
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
@llvm.used = appending global [2 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection]
@llvm.used = appending global [3 x ptr] [ptr @extern_func, ptr @exportedFunctionInSection, ptr @exported]
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
@undefinedGlobalNotInSection = external global i32, align 4
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
@@ -62,13 +62,23 @@ entry:
declare void @main.declaredImport() #7
declare void @imported() #8
; Function Attrs: nounwind
define void @exported() #9 {
entry:
ret void
}
declare void @main.undefinedFunctionNotInSection(ptr) #1
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" "wasm-import-module"="env" "wasm-import-name"="extern_func" }
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" "wasm-import-module"="env" "wasm-import-name"="exportedFunctionInSection" }
attributes #7 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" }
attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" }
attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" }
attributes #8 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="foobar" "wasm-import-name"="imported" }
attributes #9 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exported" }
+3 -3
View File
@@ -322,7 +322,7 @@ unsafe.Slice.throw: ; preds = %entry
unreachable
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind }
+3 -3
View File
@@ -97,7 +97,7 @@ lookup.throw: ; preds = %entry
unreachable
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nounwind }
+17 -49
View File
@@ -21,14 +21,9 @@ define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1
entry:
%hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4
%s = alloca %main.hasPadding, align 8
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s, align 8
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #5
store %main.hasPadding %2, ptr %s, align 8
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 8
@@ -43,14 +38,14 @@ entry:
ret i32 %6
}
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4
declare void @runtime.memzero(ptr, i32, ptr) #1
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4
; Function Attrs: noinline nounwind
@@ -58,14 +53,9 @@ define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b
entry:
%hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4
%s = alloca %main.hasPadding, align 8
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s, align 8
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #5
store %main.hasPadding %2, ptr %s, align 8
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
@@ -87,24 +77,13 @@ define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2
entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4
%s1 = alloca [2 x %main.hasPadding], align 8
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s1, align 8
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #5
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %s1, align 8
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %hashmap.key, align 8
%hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
@@ -125,25 +104,14 @@ define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2
entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4
%s1 = alloca [2 x %main.hasPadding], align 8
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s1, align 8
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #5
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %s1, align 8
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %hashmap.key, align 8
%hashmap.key.repack1 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt2 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt2, ptr %hashmap.key.repack1, align 4
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #5
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
@@ -164,9 +132,9 @@ entry:
ret void
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #4 = { argmemonly nocallback nofree nosync nounwind willreturn }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #5 = { nounwind }
+4 -4
View File
@@ -14,11 +14,11 @@ require (
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-tty v0.0.4
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
go.bug.st/serial v1.3.5
golang.org/x/sys v0.4.0
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8
go.bug.st/serial v1.6.0
golang.org/x/sys v0.11.0
golang.org/x/tools v0.12.0
gopkg.in/yaml.v2 v2.4.0
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32
tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc
)
require (
+9 -9
View File
@@ -46,9 +46,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
go.bug.st/serial v1.3.5 h1:k50SqGZCnHZ2MiBQgzccXWG+kd/XpOs1jUljpDDKzaE=
go.bug.st/serial v1.3.5/go.mod h1:z8CesKorE90Qr/oRSJiEuvzYRKol9r/anJZEb5kt304=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A=
go.bug.st/serial v1.6.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -56,14 +56,14 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8 h1:LHoToPgySGSr2NcUHbjENAidHz38RkKaNmmntwn9TjI=
golang.org/x/tools v0.5.1-0.20230114154351-e035d0c426c8/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32 h1:LvdmoXncO43m2cws1chRB2hkLBAxfN6CbSjDI7+gk4Y=
tinygo.org/x/go-llvm v0.0.0-20221028183034-8341240c0b32/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc h1:IVX1dqCX3c88P7iEMBtz1xCAM4UIqCMgbqHdSefBaWE=
tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+53 -99
View File
@@ -4,15 +4,16 @@ package goenv
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/fs"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strings"
"sync"
)
// Keys is a slice of all available environment variable keys.
@@ -37,6 +38,53 @@ func init() {
// directory.
var TINYGOROOT string
// Variables read from a `go env` command invocation.
var goEnvVars struct {
GOPATH string
GOROOT string
GOVERSION string
}
var goEnvVarsOnce sync.Once
var goEnvVarsErr error // error returned from cmd.Run
// Make sure goEnvVars is fresh. This can be called multiple times, the first
// time will update all environment variables in goEnvVars.
func readGoEnvVars() error {
goEnvVarsOnce.Do(func() {
cmd := exec.Command("go", "env", "-json", "GOPATH", "GOROOT", "GOVERSION")
output, err := cmd.Output()
if err != nil {
// Check for "command not found" error.
if execErr, ok := err.(*exec.Error); ok {
goEnvVarsErr = fmt.Errorf("could not find '%s' command: %w", execErr.Name, execErr.Err)
return
}
// It's perhaps a bit ugly to handle this error here, but I couldn't
// think of a better place further up in the call chain.
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() != 0 {
if len(exitErr.Stderr) != 0 {
// The 'go' command exited with an error message. Print that
// message and exit, so we behave in a similar way.
os.Stderr.Write(exitErr.Stderr)
os.Exit(exitErr.ExitCode())
}
}
// Other errors. Not sure whether there are any, but just in case.
goEnvVarsErr = err
return
}
err = json.Unmarshal(output, &goEnvVars)
if err != nil {
// This should never happen if we have a sane Go toolchain
// installed.
goEnvVarsErr = fmt.Errorf("unexpected error while unmarshalling `go env` output: %w", err)
}
})
return goEnvVarsErr
}
// Get returns a single environment variable, possibly calculating it on-demand.
// The empty string is returned for unknown environment variables.
func Get(name string) string {
@@ -70,15 +118,11 @@ func Get(name string) string {
// especially when floating point instructions are involved.
return "6"
case "GOROOT":
return getGoroot()
readGoEnvVars()
return goEnvVars.GOROOT
case "GOPATH":
if dir := os.Getenv("GOPATH"); dir != "" {
return dir
}
// fallback
home := getHomeDir()
return filepath.Join(home, "go")
readGoEnvVars()
return goEnvVars.GOPATH
case "GOCACHE":
// Get the cache directory, usually ~/.cache/tinygo
dir, err := os.UserCacheDir()
@@ -240,93 +284,3 @@ func isSourceDir(root string) bool {
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
return err == nil
}
func getHomeDir() string {
u, err := user.Current()
if err != nil {
panic("cannot get current user: " + err.Error())
}
if u.HomeDir == "" {
// This is very unlikely, so panic here.
// Not the nicest solution, however.
panic("could not find home directory")
}
return u.HomeDir
}
// getGoroot returns an appropriate GOROOT from various sources. If it can't be
// found, it returns an empty string.
func getGoroot() string {
// An explicitly set GOROOT always has preference.
goroot := os.Getenv("GOROOT")
if goroot != "" {
// Convert to the standard GOROOT being referenced, if it's a TinyGo cache.
return getStandardGoroot(goroot)
}
// Check for the location of the 'go' binary and base GOROOT on that.
binpath, err := exec.LookPath("go")
if err == nil {
binpath, err = filepath.EvalSymlinks(binpath)
if err == nil {
goroot := filepath.Dir(filepath.Dir(binpath))
if isGoroot(goroot) {
return goroot
}
}
}
// Check what GOROOT was at compile time.
if isGoroot(runtime.GOROOT()) {
return runtime.GOROOT()
}
// Check for some standard locations, as a last resort.
var candidates []string
switch runtime.GOOS {
case "linux":
candidates = []string{
"/usr/local/go", // manually installed
"/usr/lib/go", // from the distribution
"/snap/go/current/", // installed using snap
}
case "darwin":
candidates = []string{
"/usr/local/go", // manually installed
"/usr/local/opt/go/libexec", // from Homebrew
}
}
for _, candidate := range candidates {
if isGoroot(candidate) {
return candidate
}
}
// Can't find GOROOT...
return ""
}
// isGoroot checks whether the given path looks like a GOROOT.
func isGoroot(goroot string) bool {
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
return err == nil
}
// getStandardGoroot returns the physical path to a real, standard Go GOROOT
// implied by the given path.
// If the given path appears to be a TinyGo cached GOROOT, it returns the path
// referenced by symlinks contained in the cache. Otherwise, it returns the
// given path as-is.
func getStandardGoroot(path string) string {
// Check if the "bin" subdirectory of our given GOROOT is a symlink, and then
// return the _parent_ directory of its destination.
if dest, err := os.Readlink(filepath.Join(path, "bin")); nil == err {
// Clean the destination to remove any trailing slashes, so that
// filepath.Dir will always return the parent.
// (because both "/foo" and "/foo/" are valid symlink destinations,
// but filepath.Dir would return "/" and "/foo", respectively)
return filepath.Dir(filepath.Clean(dest))
}
return path
}
+8 -26
View File
@@ -4,15 +4,12 @@ import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
)
// Version of TinyGo.
// Update this value before release of new version of software.
const Version = "0.28.0-dev"
const Version = "0.30.0"
var (
// This variable is set at build time using -ldflags parameters.
@@ -22,8 +19,8 @@ var (
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
func GetGorootVersion(goroot string) (major, minor int, err error) {
s, err := GorootVersionString(goroot)
func GetGorootVersion() (major, minor int, err error) {
s, err := GorootVersionString()
if err != nil {
return 0, 0, err
}
@@ -51,24 +48,9 @@ func GetGorootVersion(goroot string) (major, minor int, err error) {
}
// GorootVersionString returns the version string as reported by the Go
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
// can have some variations (for beta releases, for example).
func GorootVersionString(goroot string) (string, error) {
if data, err := os.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
return string(data), nil
} else if data, err := os.ReadFile(filepath.Join(
goroot, "src", "internal", "buildcfg", "zbootstrap.go")); err == nil {
r := regexp.MustCompile("const version = `(.*)`")
matches := r.FindSubmatch(data)
if len(matches) != 2 {
return "", errors.New("Invalid go version output:\n" + string(data))
}
return string(matches[1]), nil
} else {
return "", err
}
// toolchain. It is usually of the form `go1.x.y` but can have some variations
// (for beta releases, for example).
func GorootVersionString() (string, error) {
err := readGoEnvVars()
return goEnvVars.GOVERSION, err
}
+4 -1
View File
@@ -49,7 +49,10 @@ func (inst *instruction) String() string {
operands[i] = op.String()
}
name := instructionNameMap[inst.opcode]
name := ""
if int(inst.opcode) < len(instructionNameMap) {
name = instructionNameMap[inst.opcode]
}
if name == "" {
name = "<unknown op>"
}
+4 -4
View File
@@ -35,14 +35,14 @@ func isRecoverableError(err error) bool {
// ErrorLine is one line in a traceback. The position may be missing.
type ErrorLine struct {
Pos token.Position
Inst llvm.Value
Inst string
}
// Error encapsulates compile-time interpretation errors with an associated
// import path. The errors may not have a precise location attached.
type Error struct {
ImportPath string
Inst llvm.Value
Inst string
Pos token.Position
Err error
Traceback []ErrorLine
@@ -60,10 +60,10 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
pos := getPosition(inst.llvmInst)
return &Error{
ImportPath: r.pkgName,
Inst: inst.llvmInst,
Inst: inst.String(),
Pos: pos,
Err: err,
Traceback: []ErrorLine{{pos, inst.llvmInst}},
Traceback: []ErrorLine{{pos, inst.String()}},
}
}
+3 -5
View File
@@ -57,16 +57,14 @@ func runTest(t *testing.T, pathPrefix string) {
if err != nil {
if err, match := err.(*Error); match {
println(err.Error())
if !err.Inst.IsNil() {
err.Inst.Dump()
println()
if len(err.Inst) != 0 {
println(err.Inst)
}
if len(err.Traceback) > 0 {
println("\ntraceback:")
for _, line := range err.Traceback {
println(line.Pos.String() + ":")
line.Inst.Dump()
println()
println(line.Inst)
}
}
}
+15 -1
View File
@@ -338,6 +338,20 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
if mem.hasExternalStore(src) || mem.hasExternalLoadOrStore(dst) {
// These are the same checks as there are on llvm.Load
// and llvm.Store in the interpreter. Copying is
// essentially loading from the source array and storing
// to the destination array, hence why we need to do the
// same checks here.
// This fixes the following bug:
// https://github.com/tinygo-org/tinygo/issues/3890
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
nBytes := uint32(n * elemSize)
dstObj := mem.getWritable(dst.index())
dstBuf := dstObj.buffer.asRawValue(r)
@@ -529,7 +543,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// how this function got called.
callErr.Traceback = append(callErr.Traceback, ErrorLine{
Pos: getPosition(inst.llvmInst),
Inst: inst.llvmInst,
Inst: inst.String(),
})
return nil, mem, callErr
}
+23
View File
@@ -7,6 +7,10 @@ target triple = "x86_64--linux"
@main.int16SliceSrc.buf = internal global [3 x i16] [i16 5, i16 123, i16 1024]
@main.int16SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.int16SliceSrc.buf, i64 3, i64 3 }
@main.int16SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer
@main.sliceSrcUntaint.buf = internal global [2 x i8] c"ab"
@main.sliceDstUntaint.buf = internal global [2 x i8] zeroinitializer
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
@@ -16,6 +20,8 @@ declare void @runtime.printuint8(i8)
declare void @runtime.printint16(i16)
declare void @use(ptr)
define void @runtime.initAll() unnamed_addr {
entry:
call void @main.init()
@@ -43,6 +49,15 @@ entry:
%int16SliceDst.buf = load ptr, ptr @main.int16SliceDst
%int16SliceDst.val = load i16, ptr %int16SliceDst.buf
call void @runtime.printint16(i16 %int16SliceDst.val)
; print(sliceDstUntaint[0])
%sliceDstUntaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstUntaint.buf, i32 0)
call void @runtime.printuint8(i8 %sliceDstUntaint.val)
; print(sliceDstTaint[0])
%sliceDstTaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstTaint.buf, i32 0)
call void @runtime.printuint8(i8 %sliceDstTaint.val)
ret void
}
@@ -79,5 +94,13 @@ entry:
%int16SliceSrc.buf = extractvalue { ptr, i64, i64 } %int16SliceSrc, 0
%copy.n2 = call i64 @runtime.sliceCopy(ptr %int16SliceDst.buf, ptr %int16SliceSrc.buf, i64 %int16SliceSrc.len, i64 %int16SliceSrc.len, i64 2)
; Copy slice that has a known value.
%copy.n3 = call i64 @runtime.sliceCopy(ptr @main.sliceDstUntaint.buf, ptr @main.sliceSrcUntaint.buf, i64 2, i64 2, i64 1)
; Copy slice that might have been modified by the external @use call.
; This is a fix for https://github.com/tinygo-org/tinygo/issues/3890.
call void @use(ptr @main.sliceSrcTaint.buf)
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
ret void
}
+12
View File
@@ -1,12 +1,21 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
declare i64 @runtime.sliceCopy(ptr, ptr, i64, i64, i64) unnamed_addr
declare void @runtime.printuint8(i8) local_unnamed_addr
declare void @runtime.printint16(i16) local_unnamed_addr
declare void @use(ptr) local_unnamed_addr
define void @runtime.initAll() unnamed_addr {
entry:
call void @use(ptr @main.sliceSrcTaint.buf)
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
ret void
}
@@ -16,5 +25,8 @@ entry:
call void @runtime.printuint8(i8 3)
call void @runtime.printint16(i16 5)
call void @runtime.printint16(i16 5)
call void @runtime.printuint8(i8 97)
%sliceDstTaint.val = load i8, ptr @main.sliceDstTaint.buf, align 1
call void @runtime.printuint8(i8 %sliceDstTaint.val)
ret void
}
+1
Submodule lib/renesas-svd added at 03d7688085
+5
View File
@@ -207,6 +207,11 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
merges[dir] = filepath.Join(goroot, dir)
}
// Required starting in Go 1.21 due to https://github.com/golang/go/issues/61928
if _, err := os.Stat(filepath.Join(goroot, "go.env")); err == nil {
merges["go.env"] = filepath.Join(goroot, "go.env")
}
return merges, nil
}
+124 -105
View File
@@ -19,6 +19,7 @@ import (
"regexp"
"runtime"
"runtime/pprof"
"sort"
"strconv"
"strings"
"sync"
@@ -946,112 +947,130 @@ func touchSerialPortAt1200bps(port string) (err error) {
}
func flashUF2UsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
// find standard UF2 info path
infoPaths := make([]string, 0, len(volumes))
for _, volume := range volumes {
switch runtime.GOOS {
case "linux", "freebsd":
fi, err := os.Stat("/run/media")
if err != nil || !fi.IsDir() {
infoPaths = append(infoPaths, "/media/*/"+volume+"/INFO_UF2.TXT")
} else {
infoPaths = append(infoPaths, "/run/media/*/"+volume+"/INFO_UF2.TXT")
}
case "darwin":
infoPaths = append(infoPaths, "/Volumes/"+volume+"/INFO_UF2.TXT")
case "windows":
path, err := windowsFindUSBDrive(volume, options)
if err == nil {
infoPaths = append(infoPaths, path+"/INFO_UF2.TXT")
for start := time.Now(); time.Since(start) < options.Timeout; {
// Find a UF2 mount point.
mounts, err := findFATMounts(options)
if err != nil {
return err
}
for _, mount := range mounts {
for _, volume := range volumes {
if mount.name != volume {
continue
}
if _, err := os.Stat(filepath.Join(mount.path, "INFO_UF2.TXT")); err != nil {
// No INFO_UF2.TXT found, which is expected on a UF2
// filesystem.
continue
}
// Found the filesystem, so flash the device!
return moveFile(tmppath, filepath.Join(mount.path, "flash.uf2"))
}
}
time.Sleep(500 * time.Millisecond)
}
d, err := locateDevice(volumes, infoPaths, options.Timeout)
if err != nil {
return err
}
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
}
func flashHexUsingMSD(volumes []string, tmppath string, options *compileopts.Options) error {
// find expected volume path
destPaths := make([]string, 0, len(volumes))
for _, volume := range volumes {
switch runtime.GOOS {
case "linux", "freebsd":
fi, err := os.Stat("/run/media")
if err != nil || !fi.IsDir() {
destPaths = append(destPaths, "/media/*/"+volume)
} else {
destPaths = append(destPaths, "/run/media/*/"+volume)
}
case "darwin":
destPaths = append(destPaths, "/Volumes/"+volume)
case "windows":
path, err := windowsFindUSBDrive(volume, options)
if err == nil {
destPaths = append(destPaths, path+"/")
for start := time.Now(); time.Since(start) < options.Timeout; {
// Find all mount points.
mounts, err := findFATMounts(options)
if err != nil {
return err
}
for _, mount := range mounts {
for _, volume := range volumes {
if mount.name != volume {
continue
}
// Found the filesystem, so flash the device!
return moveFile(tmppath, filepath.Join(mount.path, "flash.hex"))
}
}
}
d, err := locateDevice(volumes, destPaths, options.Timeout)
if err != nil {
return err
}
return moveFile(tmppath, d+"/flash.hex")
}
func locateDevice(volumes, paths []string, timeout time.Duration) (string, error) {
var d []string
var err error
for start := time.Now(); time.Since(start) < timeout; {
for _, path := range paths {
d, err = filepath.Glob(path)
if err != nil {
return "", err
}
if d != nil {
break
}
}
if d != nil {
break
}
time.Sleep(500 * time.Millisecond)
}
if d == nil {
return "", errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
}
return d[0], nil
return errors.New("unable to locate any volume: [" + strings.Join(volumes, ",") + "]")
}
func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, error) {
cmd := executeCommand(options, "wmic",
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
"get", "DeviceID,VolumeName,FileSystem,DriveType")
type mountPoint struct {
name string
path string
}
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return "", err
}
for _, line := range strings.Split(out.String(), "\n") {
words := strings.Fields(line)
if len(words) >= 3 {
if words[1] == "2" && words[2] == "FAT" {
return words[0], nil
}
// Find all the mount points on the system that use the FAT filesystem.
func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
var points []mountPoint
switch runtime.GOOS {
case "darwin":
list, err := os.ReadDir("/Volumes")
if err != nil {
return nil, fmt.Errorf("could not list mount points: %w", err)
}
for _, elem := range list {
// TODO: find a way to check for the filesystem type.
// (Only return FAT filesystems).
points = append(points, mountPoint{
name: elem.Name(),
path: filepath.Join("/Volumes", elem.Name()),
})
}
sort.Slice(points, func(i, j int) bool {
return points[i].path < points[j].name
})
return points, nil
case "linux":
tab, err := os.ReadFile("/proc/mounts") // symlink to /proc/self/mounts on my system
if err != nil {
return nil, fmt.Errorf("could not list mount points: %w", err)
}
for _, line := range strings.Split(string(tab), "\n") {
fields := strings.Fields(line)
if len(fields) <= 2 {
continue
}
fstype := fields[2]
if fstype != "vfat" {
continue
}
points = append(points, mountPoint{
name: filepath.Base(fields[1]),
path: fields[1],
})
}
return points, nil
case "windows":
// Obtain a list of all currently mounted volumes.
cmd := executeCommand(options, "wmic",
"PATH", "Win32_LogicalDisk",
"get", "DeviceID,VolumeName,FileSystem,DriveType")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return nil, fmt.Errorf("could not list mount points: %w", err)
}
// Extract data to convert to a []mountPoint slice.
for _, line := range strings.Split(out.String(), "\n") {
words := strings.Fields(line)
if len(words) < 3 {
continue
}
if words[1] != "2" || words[2] != "FAT" {
// - DriveType 2 is removable (which we're looking for).
// - We only want to return FAT filesystems.
continue
}
points = append(points, mountPoint{
name: words[3],
path: words[0],
})
}
return points, nil
default:
return nil, fmt.Errorf("unknown GOOS for listing mount points: %s", runtime.GOOS)
}
return "", errors.New("unable to locate a USB device to be flashed")
}
// getDefaultPort returns the default serial port depending on the operating system.
@@ -1270,16 +1289,14 @@ func printCompilerError(logln func(...interface{}), err error) {
case *interp.Error:
logln("#", err.ImportPath)
logln(err.Error())
if !err.Inst.IsNil() {
err.Inst.Dump()
logln()
if len(err.Inst) != 0 {
logln(err.Inst)
}
if len(err.Traceback) > 0 {
logln("\ntraceback:")
for _, line := range err.Traceback {
logln(line.Pos.String() + ":")
line.Inst.Dump()
logln()
logln(line.Inst)
}
}
case loader.Errors:
@@ -1777,15 +1794,17 @@ func main() {
}
if flagJSON {
json, _ := json.MarshalIndent(struct {
GOROOT string `json:"goroot"`
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
GOARM string `json:"goarm"`
BuildTags []string `json:"build_tags"`
GC string `json:"garbage_collector"`
Scheduler string `json:"scheduler"`
LLVMTriple string `json:"llvm_triple"`
Target *compileopts.TargetSpec `json:"target"`
GOROOT string `json:"goroot"`
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
GOARM string `json:"goarm"`
BuildTags []string `json:"build_tags"`
GC string `json:"garbage_collector"`
Scheduler string `json:"scheduler"`
LLVMTriple string `json:"llvm_triple"`
}{
Target: config.Target,
GOROOT: cachedGOROOT,
GOOS: config.GOOS(),
GOARCH: config.GOARCH(),
@@ -1852,7 +1871,7 @@ func main() {
usage(command)
case "version":
goversion := "<unknown>"
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
if s, err := goenv.GorootVersionString(); err == nil {
goversion = s
}
version := goenv.Version
+24 -2
View File
@@ -34,6 +34,16 @@ var supportedLinuxArches = map[string]string{
"X86Linux": "linux/386",
"ARMLinux": "linux/arm/6",
"ARM64Linux": "linux/arm64",
"WASIp1": "wasip1/wasm",
}
func init() {
major, _, _ := goenv.GetGorootVersion()
if major < 21 {
// Go 1.20 backwards compatibility.
// Should be removed once we drop support for Go 1.20.
delete(supportedLinuxArches, "WASIp1")
}
}
var sema = make(chan struct{}, runtime.NumCPU())
@@ -71,6 +81,16 @@ func TestBuild(t *testing.T) {
"zeroalloc.go",
}
// Go 1.21 made some changes to the language, which we can only test when
// we're actually on Go 1.21.
_, minor, err := goenv.GetGorootVersion()
if err != nil {
t.Fatal("could not get version:", minor)
}
if minor >= 21 {
tests = append(tests, "go1.21.go")
}
if *testTarget != "" {
// This makes it possible to run one specific test (instead of all),
// which is especially useful to quickly check whether some changes
@@ -166,6 +186,8 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
t.Fatal("failed to load target spec:", err)
}
isWebAssembly := options.Target == "wasi" || options.Target == "wasm" || (options.Target == "" && options.GOARCH == "wasm")
for _, name := range tests {
if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") {
switch name {
@@ -216,7 +238,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
})
}
if options.Target == "wasi" || options.Target == "wasm" {
if isWebAssembly {
t.Run("alias.go-scheduler-none", func(t *testing.T) {
t.Parallel()
options := compileopts.Options(options)
@@ -236,7 +258,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
runTest("rand.go", options, t, nil, nil)
})
}
if options.Target != "wasi" && options.Target != "wasm" {
if !isWebAssembly {
// The recover() builtin isn't supported yet on WebAssembly and Windows.
t.Run("recover.go", func(t *testing.T) {
t.Parallel()
+11
View File
@@ -0,0 +1,11 @@
//go:build arduino
package main
import "machine"
const (
button = machine.D2
buttonMode = machine.PinInputPullup
buttonPinChange = machine.PinRising
)
@@ -5,6 +5,7 @@ package main
import "machine"
const (
button = machine.BUTTON
buttonMode = machine.PinInputPulldown
buttonPinChange = machine.PinFalling
)
+1
View File
@@ -5,6 +5,7 @@ package main
import "machine"
const (
button = machine.BUTTON
buttonMode = machine.PinInputPullup
buttonPinChange = machine.PinRising
)
+2 -12
View File
@@ -8,18 +8,14 @@ package main
import (
"machine"
"runtime/volatile"
"time"
)
const (
button = machine.BUTTON
led = machine.LED
led = machine.LED
)
func main() {
var lightLed volatile.Register8
lightLed.Set(0)
// Configure the LED, defaulting to on (usually setting the pin to low will
// turn the LED on).
@@ -33,13 +29,7 @@ func main() {
// Set an interrupt on this pin.
err := button.SetInterrupt(buttonPinChange, func(machine.Pin) {
if lightLed.Get() != 0 {
lightLed.Set(0)
led.Low()
} else {
lightLed.Set(1)
led.High()
}
led.Set(!led.Get())
})
if err != nil {
println("could not configure pin interrupt:", err.Error())
+1
View File
@@ -5,6 +5,7 @@ package main
import "machine"
const (
button = machine.BUTTON
buttonMode = machine.PinInputPulldown
buttonPinChange = machine.PinRising | machine.PinFalling
)
+1
View File
@@ -5,6 +5,7 @@ package main
import "machine"
const (
button = machine.BUTTON
buttonMode = machine.PinInput
buttonPinChange = machine.PinFalling
)
+32
View File
@@ -0,0 +1,32 @@
package main
// This example demonstrates how to set the system time.
//
// Usually, boards don't keep time on power cycles and restarts, it resets to Unix epoch.
// The system time can be set by calling runtime.AdjustTimeOffset().
//
// Possible time sources: an external RTC clock or network (WiFi access point or NTP server)
import (
"fmt"
"runtime"
"time"
)
const myTime = "2006-01-02T15:04:05Z" // this is an example time you source somewhere
func main() {
// measure how many nanoseconds the internal clock is behind
timeOfMeasurement := time.Now()
actualTime, _ := time.Parse(time.RFC3339, myTime)
offset := actualTime.Sub(timeOfMeasurement)
// adjust internal clock by adding the offset to the internal clock
runtime.AdjustTimeOffset(int64(offset))
for {
fmt.Printf("%v\r\n", time.Now().Format(time.RFC3339))
time.Sleep(5 * time.Second)
}
}
+25 -15
View File
@@ -10,47 +10,57 @@ import (
// Try it easily by opening the following site in Chrome.
// https://www.onlinemusictools.com/kb/
const (
cable = 0
channel = 1
velocity = 0x40
)
func main() {
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
button := machine.BUTTON
button.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
m := midi.Port()
m.SetHandler(func(b []byte) {
m.SetRxHandler(func(b []byte) {
// blink when we receive a MIDI message
led.Set(!led.Get())
})
m.SetTxHandler(func() {
// blink when we send a MIDI message
led.Set(!led.Get())
m.Write(b)
})
prev := true
chords := []struct {
name string
keys []midi.Note
name string
notes []midi.Note
}{
{name: "C ", keys: []midi.Note{midi.C4, midi.E4, midi.G4}},
{name: "G ", keys: []midi.Note{midi.G3, midi.B3, midi.D4}},
{name: "Am", keys: []midi.Note{midi.A3, midi.C4, midi.E4}},
{name: "F ", keys: []midi.Note{midi.F3, midi.A3, midi.C4}},
{name: "C ", notes: []midi.Note{midi.C4, midi.E4, midi.G4}},
{name: "G ", notes: []midi.Note{midi.G3, midi.B3, midi.D4}},
{name: "Am", notes: []midi.Note{midi.A3, midi.C4, midi.E4}},
{name: "F ", notes: []midi.Note{midi.F3, midi.A3, midi.C4}},
}
index := 0
for {
current := button.Get()
if prev != current {
led.Set(current)
if current {
for _, c := range chords[index].keys {
m.NoteOff(0, 0, c, 0x40)
for _, note := range chords[index].notes {
m.NoteOff(cable, channel, note, velocity)
}
index = (index + 1) % len(chords)
} else {
for _, c := range chords[index].keys {
m.NoteOn(0, 0, c, 0x40)
for _, note := range chords[index].notes {
m.NoteOn(cable, channel, note, velocity)
}
}
prev = current
}
time.Sleep(10 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
}
}
+42
View File
@@ -0,0 +1,42 @@
package main
import (
"fmt"
"machine"
"time"
)
func main() {
//sleep for 2 secs for console
time.Sleep(2 * time.Second)
config := machine.WatchdogConfig{
TimeoutMillis: 1000,
}
println("configuring watchdog for max 1 second updates")
machine.Watchdog.Configure(config)
// From this point the watchdog is running and Update must be
// called periodically, per the config
machine.Watchdog.Start()
// This loop should complete because watchdog update is called
// every 100ms.
start := time.Now()
println("updating watchdog for 3 seconds")
for i := 0; i < 30; i++ {
time.Sleep(100 * time.Millisecond)
machine.Watchdog.Update()
fmt.Printf("alive @ %v\n", time.Now().Sub(start))
}
// This loop should cause a watchdog reset after 1s since
// there is no update call.
start = time.Now()
println("entering tight loop")
for {
time.Sleep(100 * time.Millisecond)
fmt.Printf("alive @ %v\n", time.Now().Sub(start))
}
}
+10
View File
@@ -251,3 +251,13 @@ func IndexRabinKarp(s, substr string) int {
}
return -1
}
// MakeNoZero makes a slice of length and capacity n without zeroing the bytes.
// It is the caller's responsibility to ensure uninitialized bytes
// do not leak to the end user.
func MakeNoZero(n int) []byte {
// Note: this does zero the buffer even though that's not necessary.
// For performance reasons we might want to change this (similar to the
// malloc function implemented in the runtime).
return make([]byte, n)
}
+111
View File
@@ -0,0 +1,111 @@
//go:build ae_rp2040
package machine
import (
"device/rp"
"runtime/interrupt"
)
// GPIO pins
const (
GP0 Pin = GPIO0
GP1 Pin = GPIO1
GP2 Pin = GPIO2
GP3 Pin = GPIO3
GP4 Pin = GPIO4
GP5 Pin = GPIO5
GP6 Pin = GPIO6
GP7 Pin = GPIO7
GP8 Pin = GPIO8
GP9 Pin = GPIO9
GP10 Pin = GPIO10
GP11 Pin = GPIO11
GP12 Pin = GPIO12
GP13 Pin = GPIO13
GP14 Pin = GPIO14
GP15 Pin = GPIO15
GP16 Pin = GPIO16
GP17 Pin = GPIO17
GP18 Pin = GPIO18
GP19 Pin = GPIO19
GP20 Pin = GPIO20
GP21 Pin = GPIO21
GP22 Pin = GPIO22
GP26 Pin = GPIO26
GP27 Pin = GPIO27
GP28 Pin = GPIO28
GP29 Pin = GPIO29
// Onboard crystal oscillator frequency, in MHz.
xoscFreq = 12 // MHz
)
// I2C Default pins on Raspberry Pico.
const (
I2C0_SDA_PIN = GP4
I2C0_SCL_PIN = GP5
I2C1_SDA_PIN = GP2
I2C1_SCL_PIN = GP3
)
// SPI default pins
const (
// Default Serial Clock Bus 0 for SPI communications
SPI0_SCK_PIN = GPIO18
// Default Serial Out Bus 0 for SPI communications
SPI0_SDO_PIN = GPIO19 // Tx
// Default Serial In Bus 0 for SPI communications
SPI0_SDI_PIN = GPIO16 // Rx
// Default Serial Clock Bus 1 for SPI communications
SPI1_SCK_PIN = GPIO10
// Default Serial Out Bus 1 for SPI communications
SPI1_SDO_PIN = GPIO11 // Tx
// Default Serial In Bus 1 for SPI communications
SPI1_SDI_PIN = GPIO12 // Rx
)
// UART pins
const (
UART0_TX_PIN = GPIO0
UART0_RX_PIN = GPIO1
UART1_TX_PIN = GPIO8
UART1_RX_PIN = GPIO9
UART_TX_PIN = UART0_TX_PIN
UART_RX_PIN = UART0_RX_PIN
)
// UART on the RP2040
var (
UART0 = &_UART0
_UART0 = UART{
Buffer: NewRingBuffer(),
Bus: rp.UART0,
}
UART1 = &_UART1
_UART1 = UART{
Buffer: NewRingBuffer(),
Bus: rp.UART1,
}
)
var DefaultUART = UART0
func init() {
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt)
}
// USB identifiers
const (
usb_STRING_PRODUCT = "AE-RP2040"
usb_STRING_MANUFACTURER = "AKIZUKI DENSHI"
)
var (
usb_VID uint16 = 0x2E8A
usb_PID uint16 = 0x000A
)
+96
View File
@@ -0,0 +1,96 @@
//go:build sam && atsamd21 && gemma_m0
package machine
// Used to reset into bootloader.
const resetMagicValue = 0xf01669ef
// GPIO Pins.
const (
D0 = PA04 // SERCOM0/PAD[0]
D1 = PA02
D2 = PA05 // SERCOM0/PAD[1]
D3 = PA00 // DotStar LED: SERCOM1/PAD[0]: APA102/MOSI
D4 = PA01 // DotStar LED: SERCOM1/PAD[1]: APA102/SCK
D11 = PA30 // Flash Access: SERCOM1/PAD[2]
D12 = PA31 // Flash Access: SERCOM1/PAD[3]
D13 = PA23 // LED: SERCOM3/PAD[1] SERCOM5/PAD[1]
)
// Analog pins.
const (
A0 = D1
A1 = D2
A2 = D0
)
const (
LED = PA23
)
// USBCDC pins.
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART0 pins.
const (
UART_TX_PIN = PA04 // TX: SERCOM0/PAD[0]
UART_RX_PIN = PA05 // RX: SERCOM0/PAD[1]
)
// UART0s on the Gemma M0.
var UART0 = &sercomUSART0
// SPI pins.
const (
SPI0_SCK_PIN = PA05 // SCK: SERCOM0/PAD[1]
SPI0_SDO_PIN = PA04 // MOSI: SERCOM0/PAD[0]
SPI0_SDI_PIN = NoPin
SPI0_CS_PIN = NoPin
)
// SPI on the Gemma M0.
var SPI0 = sercomSPIM0
// SPI pins for DotStar LED (using APA102 software SPI) and Flash.
const (
SPI1_SCK_PIN = PA01 // SCK: SERCOM1/PAD[0]
SPI1_SDO_PIN = PA00 // MOSI: SERCOM1/PAD[1]
SPI1_SDI_PIN = PA31 // MISO: SERCOM1/PAD[3]
SPI1_CS_PIN = PA30 // CS: SERCOM1/PAD[2]
)
// I2C pins.
const (
SDA_PIN = PA04 // SDA: SERCOM0/PAD[0]
SCL_PIN = PA05 // SCL: SERCOM0/PAD[1]
)
// I2C on the Gemma M0.
var (
I2C0 = sercomI2CM0
)
// I2S (not connected, needed for atsamd21).
const (
I2S_SCK_PIN = NoPin
I2S_SD_PIN = NoPin
I2S_WS_PIN = NoPin
)
// USB CDC identifiers.
const (
usb_STRING_PRODUCT = "Adafruit Gemma M0"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x801E
)
var (
DefaultUART = UART0
)
+1
View File
@@ -16,6 +16,7 @@ const (
const (
SWITCH = GPIO0
BUTTON = GPIO0
KEY1 = GPIO1
KEY2 = GPIO2
+5 -2
View File
@@ -97,7 +97,7 @@ func (i2c *I2C) stop() {
}
// writeByte writes a single byte to the I2C bus.
func (i2c *I2C) writeByte(data byte) {
func (i2c *I2C) writeByte(data byte) error {
// Write data to register.
avr.TWDR.Set(data)
@@ -107,6 +107,7 @@ func (i2c *I2C) writeByte(data byte) {
// Wait till data is transmitted.
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
}
return nil
}
// readByte reads a single byte from the I2C bus.
@@ -190,7 +191,7 @@ func (uart *UART) handleInterrupt(intr interrupt.Interrupt) {
}
// WriteByte writes a byte of data to the UART.
func (uart *UART) WriteByte(c byte) error {
func (uart *UART) writeByte(c byte) error {
// Wait until UART buffer is not busy.
for !uart.statusRegA.HasBits(avr.UCSR0A_UDRE0) {
}
@@ -198,6 +199,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil
}
func (uart *UART) flush() {}
// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
Frequency uint32
+104
View File
@@ -466,3 +466,107 @@ var SPI0 = SPI{
sdo: PB3,
sdi: PB4,
cs: PB2}
// Pin Change Interrupts
type PinChange uint8
const (
PinRising PinChange = 1 << iota
PinFalling
PinToggle = PinRising | PinFalling
)
func (pin Pin) SetInterrupt(pinChange PinChange, callback func(Pin)) (err error) {
switch {
case pin >= PB0 && pin <= PB7:
// PCMSK0 - PCINT0-7
pinStates[0] = avr.PINB.Get()
pinIndex := pin - PB0
if pinChange&PinRising > 0 {
pinCallbacks[0][pinIndex][0] = callback
}
if pinChange&PinFalling > 0 {
pinCallbacks[0][pinIndex][1] = callback
}
if callback != nil {
avr.PCMSK0.SetBits(1 << pinIndex)
} else {
avr.PCMSK0.ClearBits(1 << pinIndex)
}
avr.PCICR.SetBits(avr.PCICR_PCIE0)
interrupt.New(avr.IRQ_PCINT0, handlePCINT0Interrupts)
case pin >= PC0 && pin <= PC7:
// PCMSK1 - PCINT8-14
pinStates[1] = avr.PINC.Get()
pinIndex := pin - PC0
if pinChange&PinRising > 0 {
pinCallbacks[1][pinIndex][0] = callback
}
if pinChange&PinFalling > 0 {
pinCallbacks[1][pinIndex][1] = callback
}
if callback != nil {
avr.PCMSK1.SetBits(1 << pinIndex)
} else {
avr.PCMSK1.ClearBits(1 << pinIndex)
}
avr.PCICR.SetBits(avr.PCICR_PCIE1)
interrupt.New(avr.IRQ_PCINT1, handlePCINT1Interrupts)
case pin >= PD0 && pin <= PD7:
// PCMSK2 - PCINT16-23
pinStates[2] = avr.PIND.Get()
pinIndex := pin - PD0
if pinChange&PinRising > 0 {
pinCallbacks[2][pinIndex][0] = callback
}
if pinChange&PinFalling > 0 {
pinCallbacks[2][pinIndex][1] = callback
}
if callback != nil {
avr.PCMSK2.SetBits(1 << pinIndex)
} else {
avr.PCMSK2.ClearBits(1 << pinIndex)
}
avr.PCICR.SetBits(avr.PCICR_PCIE2)
interrupt.New(avr.IRQ_PCINT2, handlePCINT2Interrupts)
default:
return ErrInvalidInputPin
}
return nil
}
var pinCallbacks [3][8][2]func(Pin)
var pinStates [3]uint8
func handlePCINTInterrupts(intr uint8, port *volatile.Register8) {
current := port.Get()
change := pinStates[intr] ^ current
pinStates[intr] = current
for i := uint8(0); i < 8; i++ {
if (change>>i)&0x01 != 0x01 {
continue
}
pin := Pin(intr*8 + i)
value := pin.Get()
if value && pinCallbacks[intr][i][0] != nil {
pinCallbacks[intr][i][0](pin)
}
if !value && pinCallbacks[intr][i][1] != nil {
pinCallbacks[intr][i][1](pin)
}
}
}
func handlePCINT0Interrupts(intr interrupt.Interrupt) {
handlePCINTInterrupts(0, avr.PINB)
}
func handlePCINT1Interrupts(intr interrupt.Interrupt) {
handlePCINTInterrupts(1, avr.PINC)
}
func handlePCINT2Interrupts(intr interrupt.Interrupt) {
handlePCINTInterrupts(2, avr.PIND)
}
+3 -1
View File
@@ -626,7 +626,7 @@ func (uart *UART) SetBaudRate(br uint32) {
}
// WriteByte writes a byte of data to the UART.
func (uart *UART) WriteByte(c byte) error {
func (uart *UART) writeByte(c byte) error {
// wait until ready to receive
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INTFLAG_DRE) {
}
@@ -634,6 +634,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil
}
func (uart *UART) flush() {}
// handleInterrupt should be called from the appropriate interrupt handler for
// this UART instance.
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
+17 -1
View File
@@ -225,7 +225,23 @@ func initEndpoint(ep, config uint32) {
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
// TODO: not really anything, seems like...
// set packet size
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
// receive interrupts when current transfer complete
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
// set byte count to zero, we have not received anything yet
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// ready for next transfer
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
// set packet size
+53 -2
View File
@@ -1114,7 +1114,7 @@ func (uart *UART) SetBaudRate(br uint32) {
}
// WriteByte writes a byte of data to the UART.
func (uart *UART) WriteByte(c byte) error {
func (uart *UART) writeByte(c byte) error {
// wait until ready to receive
for !uart.Bus.INTFLAG.HasBits(sam.SERCOM_USART_INT_INTFLAG_DRE) {
}
@@ -1122,6 +1122,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil
}
func (uart *UART) flush() {}
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
// should reset IRQ
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
@@ -1162,7 +1164,7 @@ const (
wireCmdStop = 3
)
const i2cTimeout = 1000
const i2cTimeout = 28000 // about 210us
// Configure is intended to setup the I2C interface.
func (i2c *I2C) Configure(config I2CConfig) error {
@@ -2297,3 +2299,52 @@ func checkFlashError() error {
return nil
}
// Watchdog provides access to the hardware watchdog available
// in the SAMD51.
var Watchdog = &watchdogImpl{}
const (
// WatchdogMaxTimeout in milliseconds (16s)
WatchdogMaxTimeout = (16384 * 1000) / 1024 // CYC16384/1024kHz
)
type watchdogImpl struct{}
// Configure the watchdog.
//
// This method should not be called after the watchdog is started and on
// some platforms attempting to reconfigure after starting the watchdog
// is explicitly forbidden / will not work.
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
// 1.024kHz clock
cycles := int((int64(config.TimeoutMillis) * 1024) / 1000)
// period is expressed as a power-of-two, starting at 8 / 1024ths of a second
period := uint8(0)
cfgCycles := 8
for cfgCycles < cycles {
period++
cfgCycles <<= 1
if period >= 0xB {
break
}
}
sam.WDT.CONFIG.Set(period << sam.WDT_CONFIG_PER_Pos)
return nil
}
// Starts the watchdog.
func (wd *watchdogImpl) Start() error {
sam.WDT.CTRLA.SetBits(sam.WDT_CTRLA_ENABLE)
return nil
}
// Update the watchdog, indicating that `source` is healthy.
func (wd *watchdogImpl) Update() {
// 0xA5 = magic value (see datasheet)
sam.WDT.CLEAR.Set(0xA5)
}
+17 -1
View File
@@ -228,7 +228,23 @@ func initEndpoint(ep, config uint32) {
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
// TODO: not really anything, seems like...
// set packet size
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, ((usb.ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
// receive interrupts when current transfer complete
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
// set byte count to zero, we have not received anything yet
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// ready for next transfer
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
// set packet size
+3 -1
View File
@@ -314,7 +314,7 @@ func (uart *UART) Configure(config UARTConfig) {
uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate)
}
func (uart *UART) WriteByte(b byte) error {
func (uart *UART) writeByte(b byte) error {
for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
// Read UART_TXFIFO_CNT from the status register, which indicates how
// many bytes there are in the transmit buffer. Wait until there are
@@ -324,6 +324,8 @@ func (uart *UART) WriteByte(b byte) error {
return nil
}
func (uart *UART) flush() {}
// Serial Peripheral Interface on the ESP32.
type SPI struct {
Bus *esp.SPI_Type
+3 -1
View File
@@ -493,7 +493,7 @@ func (uart *UART) enableReceiver() {
uart.Bus.SetINT_ENA_RXFIFO_OVF_INT_ENA(1)
}
func (uart *UART) WriteByte(b byte) error {
func (uart *UART) writeByte(b byte) error {
for (uart.Bus.STATUS.Get()&esp.UART_STATUS_TXFIFO_CNT_Msk)>>esp.UART_STATUS_TXFIFO_CNT_Pos >= 128 {
// Read UART_TXFIFO_CNT from the status register, which indicates how
// many bytes there are in the transmit buffer. Wait until there are
@@ -502,3 +502,5 @@ func (uart *UART) WriteByte(b byte) error {
uart.Bus.FIFO.Set(uint32(b))
return nil
}
func (uart *UART) flush() {}
+4 -2
View File
@@ -181,12 +181,14 @@ func (uart *UART) Configure(config UARTConfig) {
esp.UART0.UART_CLKDIV.Set(CPUFrequency() / config.BaudRate)
}
// WriteByte writes a single byte to the output buffer. Note that the hardware
// writeByte writes a single byte to the output buffer. Note that the hardware
// includes a buffer of 128 bytes which will be used first.
func (uart *UART) WriteByte(c byte) error {
func (uart *UART) writeByte(c byte) error {
for (esp.UART0.UART_STATUS.Get()>>16)&0xff >= 128 {
// Wait until the TX buffer has room.
}
esp.UART0.UART_FIFO.Set(uint32(c))
return nil
}
func (uart *UART) flush() {}
+4 -1
View File
@@ -111,13 +111,16 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
uart.Receive(c)
}
func (uart *UART) WriteByte(c byte) {
func (uart *UART) writeByte(c byte) error {
for sifive.UART0.TXDATA.Get()&sifive.UART_TXDATA_FULL != 0 {
}
sifive.UART0.TXDATA.Set(uint32(c))
return nil
}
func (uart *UART) flush() {}
// SPI on the FE310. The normal SPI0 is actually a quad-SPI meant for flash, so it is best
// to use SPI1 or SPI2 port for most applications.
type SPI struct {
+4 -1
View File
@@ -392,13 +392,16 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
uart.Receive(c)
}
func (uart *UART) WriteByte(c byte) {
func (uart *UART) writeByte(c byte) error {
for uart.Bus.TXDATA.Get()&kendryte.UARTHS_TXDATA_FULL != 0 {
}
uart.Bus.TXDATA.Set(uint32(c))
return nil
}
func (uart *UART) flush() {}
type SPI struct {
Bus *kendryte.SPI_Type
}
+3 -1
View File
@@ -173,7 +173,7 @@ func (uart *UART) Sync() error {
}
// WriteByte writes a single byte of data to the UART interface.
func (uart *UART) WriteByte(c byte) error {
func (uart *UART) writeByte(c byte) error {
uart.startTransmitting()
for !uart.txBuffer.Put(c) {
}
@@ -181,6 +181,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil
}
func (uart *UART) flush() {}
// getBaudRateDivisor finds the greatest over-sampling factor (4..32) and
// corresponding baud rate divisor (1..8191) that best partition a given baud
// rate into equal intervals.
+12 -2
View File
@@ -186,7 +186,7 @@ func (uart *UART) SetBaudRate(br uint32) {
}
// WriteByte writes a byte of data to the UART.
func (uart *UART) WriteByte(c byte) error {
func (uart *UART) writeByte(c byte) error {
nrf.UART0.EVENTS_TXDRDY.Set(0)
nrf.UART0.TXD.Set(uint32(c))
for nrf.UART0.EVENTS_TXDRDY.Get() == 0 {
@@ -194,6 +194,8 @@ func (uart *UART) WriteByte(c byte) error {
return nil
}
func (uart *UART) flush() {}
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
uart.Receive(byte(nrf.UART0.RXD.Get()))
@@ -201,6 +203,8 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
}
}
const i2cTimeout = 0xffff // this is around 29ms on a nrf52
// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
Frequency uint32
@@ -259,11 +263,17 @@ func (i2c *I2C) Configure(config I2CConfig) error {
}
// signalStop sends a stop signal to the I2C peripheral and waits for confirmation.
func (i2c *I2C) signalStop() {
func (i2c *I2C) signalStop() error {
tries := 0
i2c.Bus.TASKS_STOP.Set(1)
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
tries++
if tries >= i2cTimeout {
return errI2CSignalStopTimeout
}
}
i2c.Bus.EVENTS_STOPPED.Set(0)
return nil
}
var rngStarted = false
+43
View File
@@ -214,3 +214,46 @@ func twisError(val uint32) error {
return errI2CBusError
}
var (
Watchdog = &watchdogImpl{}
)
const (
// WatchdogMaxTimeout in milliseconds (approx 36h)
WatchdogMaxTimeout = (0xffffffff * 1000) / 32768
)
type watchdogImpl struct {
}
// Configure the watchdog.
//
// This method should not be called after the watchdog is started and on
// some platforms attempting to reconfigure after starting the watchdog
// is explicitly forbidden / will not work.
func (wd *watchdogImpl) Configure(config WatchdogConfig) error {
// 32.768kHz counter
crv := int32((int64(config.TimeoutMillis) * 32768) / 1000)
nrf.WDT.CRV.Set(uint32(crv))
// One source
nrf.WDT.RREN.Set(0x1)
// Run during sleep
nrf.WDT.CONFIG.Set(nrf.WDT_CONFIG_SLEEP_Run)
return nil
}
// Starts the watchdog.
func (wd *watchdogImpl) Start() error {
nrf.WDT.TASKS_START.Set(nrf.WDT_TASKS_START_TASKS_START)
return nil
}
// Update the watchdog, indicating that `source` is healthy.
func (wd *watchdogImpl) Update() {
// 0x6E524635 = magic value from datasheet
nrf.WDT.RR[0].Set(0x6E524635)
}
+17 -14
View File
@@ -288,24 +288,27 @@ func (spi SPI) Tx(w, r []byte) error {
// supported.
for len(r) != 0 || len(w) != 0 {
// Prepare the SPI transfer: set the DMA pointers and lengths.
if len(r) != 0 {
// read buffer
nr := uint32(len(r))
if nr > 0 {
if nr > 255 {
nr = 255
}
spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
n := uint32(len(r))
if n > 255 {
n = 255
}
spi.Bus.RXD.MAXCNT.Set(n)
r = r[n:]
r = r[nr:]
}
if len(w) != 0 {
spi.Bus.RXD.MAXCNT.Set(nr)
// write buffer
nw := uint32(len(w))
if nw > 0 {
if nw > 255 {
nw = 255
}
spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
n := uint32(len(w))
if n > 255 {
n = 255
}
spi.Bus.TXD.MAXCNT.Set(n)
w = w[n:]
w = w[nw:]
}
spi.Bus.TXD.MAXCNT.Set(nw)
// Do the transfer.
// Note: this can be improved by not waiting until the transfer is
+24 -4
View File
@@ -70,11 +70,21 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND_Disabled)
}
// Stop explicitly when no reads were executed, stoping unconditionally would be a mistake.
// It may execute after I2C peripheral has already been stopped by the shortcut in the read block,
// so stop task will trigger first thing in a subsequent transaction, hanging it.
if len(r) == 0 {
i2c.signalStop()
// Stop the I2C transaction after the write.
err = i2c.signalStop()
} else {
// The last byte read has already stopped the transaction, via
// TWI_SHORTS_BB_STOP. But we still need to wait until we receive the
// STOPPED event.
tries := 0
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
tries++
if tries >= i2cTimeout {
return errI2CSignalStopTimeout
}
}
i2c.Bus.EVENTS_STOPPED.Set(0)
}
return
@@ -82,12 +92,17 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
// writeByte writes a single byte to the I2C bus and waits for confirmation.
func (i2c *I2C) writeByte(data byte) error {
tries := 0
i2c.Bus.TXD.Set(uint32(data))
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
i2c.Bus.EVENTS_ERROR.Set(0)
return errI2CBusError
}
tries++
if tries >= i2cTimeout {
return errI2CWriteTimeout
}
}
i2c.Bus.EVENTS_TXDSENT.Set(0)
return nil
@@ -95,11 +110,16 @@ func (i2c *I2C) writeByte(data byte) error {
// readByte reads a single byte from the I2C bus when it is ready.
func (i2c *I2C) readByte() (byte, error) {
tries := 0
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
i2c.Bus.EVENTS_ERROR.Set(0)
return 0, errI2CBusError
}
tries++
if tries >= i2cTimeout {
return 0, errI2CReadTimeout
}
}
i2c.Bus.EVENTS_RXDREADY.Set(0)
return byte(i2c.Bus.RXD.Get()), nil
+3 -1
View File
@@ -292,7 +292,7 @@ func (u *UART) handleStatusInterrupt(interrupt.Interrupt) {
}
// WriteByte writes a byte of data to the UART.
func (u *UART) WriteByte(c byte) error {
func (u *UART) writeByte(c byte) error {
if !u.Configured {
return ErrNotConfigured
}
@@ -305,3 +305,5 @@ func (u *UART) WriteByte(c byte) error {
u.C2.Set(uartC2TXActive)
return nil
}
func (uart *UART) flush() {}
+1 -1
View File
@@ -182,7 +182,7 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) {
// Must be called before any other clock function.
func (clks *clocksType) init() {
// Start the watchdog tick
watchdog.startTick(xoscFreq)
Watchdog.startTick(xoscFreq)
// Disable resus that may be enabled from previous software
clks.resus.ctrl.Set(0)
+97
View File
@@ -0,0 +1,97 @@
//go:build rp2040
package machine
import (
"bytes"
"unsafe"
)
// EnterBootloader should perform a system reset in preparation
// to switch to the bootloader to flash new firmware.
func EnterBootloader() {
enterBootloader()
}
// compile-time check for ensuring we fulfill BlockDevice interface
var _ BlockDevice = flashBlockDevice{}
var Flash flashBlockDevice
type flashBlockDevice struct {
}
// ReadAt reads the given number of bytes from the block device.
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if readAddress(off) > FlashDataEnd() {
return 0, errFlashCannotReadPastEOF
}
data := unsafe.Slice((*byte)(unsafe.Pointer(readAddress(off))), len(p))
copy(p, data)
return len(p), nil
}
// WriteAt writes the given number of bytes to the block device.
// Only word (32 bits) length data can be programmed.
// If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
return f.writeAt(p, off)
}
// Size returns the number of bytes in this block device.
func (f flashBlockDevice) Size() int64 {
return int64(FlashDataEnd() - FlashDataStart())
}
const writeBlockSize = 1 << 8
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
func (f flashBlockDevice) WriteBlockSize() int64 {
return writeBlockSize
}
const eraseBlockSizeValue = 1 << 12
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
func (f flashBlockDevice) EraseBlockSize() int64 {
return eraseBlockSize()
}
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, length int64) error {
return f.eraseBlocks(start, length)
}
// pad data if needed so it is long enough for correct byte alignment on writes.
func (f flashBlockDevice) pad(p []byte) []byte {
overflow := int64(len(p)) % f.WriteBlockSize()
if overflow == 0 {
return p
}
padding := bytes.Repeat([]byte{0xff}, int(f.WriteBlockSize()-overflow))
return append(p, padding...)
}
// return the correct address to be used for write
func writeAddress(off int64) uintptr {
return readAddress(off) - uintptr(memoryStart)
}
// return the correct address to be used for reads
func readAddress(off int64) uintptr {
return FlashDataStart() + uintptr(off)
}
+9
View File
@@ -174,6 +174,9 @@ func (p Pin) init() {
// Configure configures the gpio pin as per mode.
func (p Pin) Configure(config PinConfig) {
if p == NoPin {
return
}
p.init()
mask := uint32(1) << p
switch config.Mode {
@@ -213,6 +216,9 @@ func (p Pin) Configure(config PinConfig) {
// Set drives the pin high if value is true else drives it low.
func (p Pin) Set(value bool) {
if p == NoPin {
return
}
if value {
p.set()
} else {
@@ -251,6 +257,9 @@ var (
// nil func to unset the pin change interrupt. If you do so, the change
// parameter is ignored and can be set to any value (such as 0).
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
if p == NoPin {
return nil
}
if p > 31 || p < 0 {
return ErrInvalidInputPin
}
+3 -81
View File
@@ -3,7 +3,6 @@
package machine
import (
"bytes"
"runtime/interrupt"
"unsafe"
)
@@ -136,40 +135,14 @@ void ram_func flash_erase_blocks(uint32_t offset, size_t count)
*/
import "C"
// EnterBootloader should perform a system reset in preparation
// to switch to the bootloader to flash new firmware.
func EnterBootloader() {
func enterBootloader() {
C.reset_usb_boot(0, 0)
}
// Flash related code
const memoryStart = C.XIP_BASE // memory start for purpose of erase
// compile-time check for ensuring we fulfill BlockDevice interface
var _ BlockDevice = flashBlockDevice{}
var Flash flashBlockDevice
type flashBlockDevice struct {
}
// ReadAt reads the given number of bytes from the block device.
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if readAddress(off) > FlashDataEnd() {
return 0, errFlashCannotReadPastEOF
}
data := unsafe.Slice((*byte)(unsafe.Pointer(readAddress(off))), len(p))
copy(p, data)
return len(p), nil
}
// WriteAt writes the given number of bytes to the block device.
// Only word (32 bits) length data can be programmed.
// If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
func (f flashBlockDevice) writeAt(p []byte, off int64) (n int, err error) {
if writeAddress(off)+uintptr(C.XIP_BASE) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF
}
@@ -190,37 +163,7 @@ func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
return len(padded), nil
}
// Size returns the number of bytes in this block device.
func (f flashBlockDevice) Size() int64 {
return int64(FlashDataEnd() - FlashDataStart())
}
const writeBlockSize = 1 << 8
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
func (f flashBlockDevice) WriteBlockSize() int64 {
return writeBlockSize
}
const eraseBlockSizeValue = 1 << 12
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
func (f flashBlockDevice) EraseBlockSize() int64 {
return eraseBlockSize()
}
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, length int64) error {
func (f flashBlockDevice) eraseBlocks(start, length int64) error {
address := writeAddress(start * f.EraseBlockSize())
if address+uintptr(C.XIP_BASE) > FlashDataEnd() {
return errFlashCannotErasePastEOF
@@ -233,24 +176,3 @@ func (f flashBlockDevice) EraseBlocks(start, length int64) error {
return nil
}
// pad data if needed so it is long enough for correct byte alignment on writes.
func (f flashBlockDevice) pad(p []byte) []byte {
overflow := int64(len(p)) % f.WriteBlockSize()
if overflow == 0 {
return p
}
padding := bytes.Repeat([]byte{0xff}, int(f.WriteBlockSize()-overflow))
return append(p, padding...)
}
// return the correct address to be used for write
func writeAddress(off int64) uintptr {
return readAddress(off) - uintptr(C.XIP_BASE)
}
// return the correct address to be used for reads
func readAddress(off int64) uintptr {
return FlashDataStart() + uintptr(off)
}
+6 -1
View File
@@ -368,6 +368,9 @@ func (spi SPI) rx(rx []byte, txrepeat byte) error {
continue
}
}
for spi.isBusy() {
gosched()
}
return nil
}
@@ -397,6 +400,8 @@ func (spi SPI) txrx(tx, rx []byte) error {
// Transaction ended early due to timeout
return ErrSPITimeout
}
for spi.isBusy() {
gosched()
}
return nil
}
+8 -1
View File
@@ -86,9 +86,10 @@ func (uart *UART) SetBaudRate(br uint32) {
}
// WriteByte writes a byte of data to the UART.
func (uart *UART) WriteByte(c byte) error {
func (uart *UART) writeByte(c byte) error {
// wait until buffer is not full
for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_TXFF) {
gosched()
}
// write data
@@ -96,6 +97,12 @@ func (uart *UART) WriteByte(c byte) error {
return nil
}
func (uart *UART) flush() {
for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_BUSY) {
gosched()
}
}
// SetFormat for number of data bits, stop bits, and parity for the UART.
func (uart *UART) SetFormat(databits, stopbits uint8, parity UARTParity) error {
var pen, pev uint8
@@ -3,25 +3,26 @@
package machine
import (
"device/arm"
"device/rp"
)
// https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/rp2040_usb_device_enumeration.c
// According to errata RP2040-E5:
// "It is safe (and inexpensive) to enable the software workaround even when using versions of RP2040
// which include the fix in hardware."
// So let us always use the software fix.
func fixRP2040UsbDeviceEnumeration() {
// After coming out of reset, the hardware expects 800us of LS_J (linestate J) time
// before it will move to the connected state. However on a hub that broadcasts packets
// for other devices this isn't the case. The plan here is to wait for the end of the bus
// reset, force an LS_J for 1ms and then switch control back to the USB phy. Unfortunately
// this requires us to use GPIO15 as there is no other way to force the input path.
// We only need to force DP as DM can be left at zero. It will be gated off by GPIO
// logic if it isn't func selected.
// Actually check for B0/B1 h/w
if ChipVersion() == 1 {
// After coming out of reset, the hardware expects 800us of LS_J (linestate J) time
// before it will move to the connected state. However on a hub that broadcasts packets
// for other devices this isn't the case. The plan here is to wait for the end of the bus
// reset, force an LS_J for 1ms and then switch control back to the USB phy. Unfortunately
// this requires us to use GPIO15 as there is no other way to force the input path.
// We only need to force DP as DM can be left at zero. It will be gated off by GPIO
// logic if it isn't func selected.
// Wait SE0 phase will call force ls_j phase which will call finish phase
hw_enumeration_fix_wait_se0()
}
// Wait SE0 phase will call force ls_j phase which will call finish phase
hw_enumeration_fix_wait_se0()
}
func hw_enumeration_fix_wait_se0() {
@@ -86,7 +87,8 @@ func hw_enumeration_fix_force_ls_j() {
// Switch to GPIO phy with LS_J forced
rp.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_DIGITAL_PAD | rp.USBCTRL_REGS_USB_MUXING_SOFTCON)
// LS_J is now forced but while loop here just to check
// LS_J is now forced but while loop to wait ~800us here just to check
waitCycles(25000)
// if timer pool disabled, or no timer available, have to busy wait.
hw_enumeration_fix_finish()
@@ -109,3 +111,10 @@ func hw_enumeration_fix_finish() {
// Restore the pad ctrl value
padsBank0.io[dp].Set(padCtrlPrev)
}
func waitCycles(n int) {
for n > 0 {
arm.Asm("nop")
n--
}
}

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