Compare commits

..

40 Commits

Author SHA1 Message Date
Ayke van Laethem 19e2d12517 compiler: reimplement interface type asserts
This is a big reimplementation that simplifies the compiler a lot.
Instead of storing the method set in metadata and lowering the type
asserts as a whole program pass, this change just puts the list of
methods in the type code (and a separate global for the interface type).
2024-07-31 19:55:18 +02:00
Ayke van Laethem 6184a6cd35 Revert "Getting DeepEqual to work with maps whose key is an interface (#4360)"
This reverts commit 1fd75ffbda.

The change is not correct and allows code to continue while it should
panic. For details, see:
https://github.com/tinygo-org/tinygo/pull/4360#issuecomment-2252943012
2024-07-29 14:10:02 +02:00
Laurent Demailly 1fd75ffbda Getting DeepEqual to work with maps whose key is an interface (#4360)
reflect: Getting DeepEqual to work with maps whose key is an interface
2024-07-24 00:30:02 +02:00
Laurent Demailly 61d36fb3f8 Add missing T.Deadline 2024-07-23 22:23:35 +02:00
leongross b318a941a4 add Fork and Exec libc hooks
Signed-off-by: leongross <leon.gross@9elements.com>
2024-07-23 16:24:41 +02:00
Ayke van Laethem 725518f007 all: add linux/mipsle support
This adds linux/mipsle (little endian Mips) support to TinyGo.

It also adds experimental linux/mips (big-endian) support. It doesn't
quite work yet, some parts of the standard library (like the reflect
package) currently seem to assume a little-endian system.
2024-07-22 16:21:26 +02:00
Ayke van Laethem 04d1261f8a build: add package ID to compiler and optimization error messages
This improves error reporting slightly.
2024-07-21 18:23:49 +02:00
leongross 3e2230eadb os/Chown (#4213)
src/os, src/syscall: add os.Chown
2024-07-21 10:56:55 +02:00
deadprogram 4f908f4877 net: update to latest net package
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-07-21 09:14:08 +02:00
Laurent Demailly 319683b863 Add enough tls.ConnectionState and tls.CipherSuiteName() to compile fortio/log (#4345)
src/crypt: add enough tls.ConnectionState and tls.CipherSuiteName() to compile fortio/log
2024-07-20 20:19:03 +02:00
Matthew Hiles 4af3f618f8 rewrite Reply() to fix sending long replies in I2C Target Mode 2024-07-20 18:36:59 +02:00
Laurent Demailly 89340f82dc Allows compilation of code using debug.BuildInfo and show correct tinygo version (#4343)
debug: Allows compilation of code using debug.BuildInfo and show correct tinygo version
2024-07-20 15:26:08 +02:00
Ayke van Laethem 824bf24445 diagnostics: sort package diagnostics by position
Previously, the error messages could be shown out of order.
With this patch, the errors are sorted before being shown to the user.
This makes it easier to read the error messages, and matches what the
`go` toolchain does.
2024-07-20 14:30:34 +02:00
Ayke van Laethem 80269b98ba diagnostics: move diagnostic printing to a new package
This is a refactor, which should (in theory) not change the behavior of
the compiler. But since this is a pretty large change, there is a chance
there will be some regressions. For that reason, the previous commits
added a bunch of tests to make sure most error messages will not be
changed due to this refactor.
2024-07-20 14:30:34 +02:00
Ayke van Laethem 3788b31ba5 main: add test for error coming from the optimizer
This is just one optimizer pass that's easy to test for. There are
others, but I'm ignoring them for now.

The one other that would be reasonable to test is when starting a
goroutine with -gc=none, but I'm leaving that one for another time.
2024-07-20 14:30:34 +02:00
Ayke van Laethem f4ce11ef37 main: add error tests for the compiler
The compiler can in fact return errors, but these weren't tested yet.
2024-07-20 14:30:34 +02:00
Ayke van Laethem a5f78a3900 main: add interp tests
This one needed more advanced checking of the error messages, because we
don't want to hardcode things like `!dbg !10` in the output.
2024-07-20 14:30:34 +02:00
Ayke van Laethem e409950492 main: refactor error messages to use FileCheck-like patterns
Match the error messages in testdata/errors/*.go like LLVM FileCheck,
which includes regular expressions.

I believe this is much more flexible, and LLVM uses it in nearly all of
their tests so it must work quite well for compilers.
2024-07-20 14:30:34 +02:00
deadprogram 208eb1a817 fix: set ubuntu version to 24.04 to try to address #4347
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-07-20 12:47:20 +02:00
Ayke van Laethem 2920492e0a syscall: remove some dead code
Not sure why it's here but it's not referenced anywhere.
2024-07-18 08:57:02 +02:00
Randy Reddig 87a8aafc4f all: simplify wasm-tools-go dependency
- add internal/wasm-tools/go.mod file to depend on wasm-tools-go
- copy package cm into src/internal/cm
- remove wasm-tools-go "vendor" submodule

internal/tools: fix typo

go.{mod,sum}, internal/tools: add wit-bindgen-go to tools

GNUmakefile: use go run for wit-bindgen-go

GNUmakefile: add tools target to go:generate tools binaries in internal/tools

GNUmakefile: add .PHONY for lint and spell

GNUmakefile, internal/cm: vendor package cm into internal/cm

go.{mod,sum}: update wasm-tools-go to v0.1.4

internal/wasi: use internal/cm package

remove submodule src/vendor/github.com/ydnar/wasm-tools-go

GNUmakefile: add comment documenting what wasi-cm target does

go.{mod,sum}: remove toolchain; go mod tidy

go.mod: revert to Go 1.19

go.mod: go 1.19

go.{mod,sum}, internal/{tools,wasm-tools}: revert root go.mod file to go1.19

Create a wasm-tools specific module that can require go1.22 for wasm-tools-go.
2024-07-17 20:17:03 +02:00
Tai Groot f026422b04 add chromeos 9p support 2024-07-15 10:13:43 +02:00
Anatol Pomozov fb91c7097f Do not stop compilation on compiler warnings
Compilers like GCC keep adding new checks that produce new warnings.
Sometimes it can be false positives.

Do not treat such warnings in binaryen library as errors. tinygo won't
be able to provide zero warnings in its dependencies.

Closes #4332
2024-07-14 14:31:14 +02:00
Ayke van Laethem d7773d3e86 loader: handle go list errors inside TinyGo
Instead of exiting with an error, handle these errors internally.
This will enable a few improvements in the future.
2024-07-13 13:26:26 +02:00
Ayke van Laethem b04b690842 libclang: do not make error locations relative
This is done at a later time anyway, so doesn't need to be done in the
cgo package. In fact, _not_ doing it there makes it easier to print
correct relative packages.
2024-07-13 13:26:26 +02:00
Ayke van Laethem 8a357af3d8 all: add testing for compiler error messages
This is needed for some improvements I'm going to make next.

This commit also refactors error handling slightly to make it more
easily testable, this should hopefully not result in any actual changes
in behavior.
2024-07-13 13:26:26 +02:00
Ayke van Laethem 7ac1ca0ae2 builder: remove workaround for generics race condition
This commit reverts commit 13a8eae0d.

It appars that the race has been fixed by
https://github.com/golang/tools/commit/db513b091504, which we now use
because it fixes another race condition as well.
See: https://github.com/tinygo-org/tinygo/issues/4206
2024-07-13 13:05:28 +02:00
Ayke van Laethem 2f3d821b13 cgo: support preprocessor macros passed on the command line
Go code might sometimes want to use preprocessor macros that were passed
on the command line. This wasn't working before and resulted in the
following error:

    internal error: could not find file where macro is defined

This is now supported, though location information isn't available
(which makes sense: the command line is not a file).

I had to use the `clang_tokenize` API for this and reconstruct the
original source location. Apparently this is the only way to do it:
https://stackoverflow.com/a/19074846/559350
In the future we could consider replacing our own tokenization with the
tokenizer that's built into Clang directly. This should reduce the
possibility of bugs a bit.
2024-07-13 13:03:50 +02:00
Dmitry Shemin 6f462fba4c fix: remove message after test binary built 2024-07-09 18:57:05 +02:00
L. Pereira ac396708da main_test: Diff expected and actual results when tests fail (#4288)
Uses a vendored "internal/diff" from Big Go to show the differences
between the expected and actual outputs when tests fail.

Signed-off-by: L. Pereira <l.pereira@fastly.com>
2024-07-08 16:35:06 -07:00
L. Pereira d150badf33 gc_leaking: Don't zero out new allocations in some targets (#4302)
Wasm linear memory is always initialized to zero by definition,
so there's no need to waste time zeroing out this allocation. This
is the case for freshly-obtained memory from mmap().

Signed-off-by: L. Pereira <l.pereira@fastly.com>
2024-07-08 16:34:40 -07:00
Randy Reddig 2d85fc6a64 internal/wasi: update to wasm-tools-go@v0.1.2 (#4326)
* internal/wasi: update to wasm-tools-go@v0.1.1

See https://github.com/ydnar/wasm-tools-go/releases/tag/v0.1.1 for additional information.

* internal/wasi: update to wasm-tools-go@v0.1.2

* internal/wasi: regenerate with wasm-tools-go@v0.1.3
2024-07-08 13:09:47 -07:00
Elias Naur 5ca3e4a2da runtime: implement dummy getAuxv to satisfy golang.org/x/sys/cpu (#4325)
Fixes the program

  package main

  import _ "golang.org/x/sys/cpu"

  func main() {
  }
2024-07-08 13:09:30 -07:00
Ayke van Laethem e6caa3fe9e transform: fix incorrect alignment of heap-to-stack transform
It assumed the maximum alignment was equal to sizeof(void*), which is
definitely not the case. So this only worked more or less by accident
previously.

It now uses the alignment as specified by the frontend, or else
`unsafe.Alignof(complex128)` which is typically the maximum alignment of
a given platform (though this shouldn't really happen in practice: the
optimizer should keep the 'align' attribute in place).
2024-07-03 07:31:37 +02:00
Ayke van Laethem 571447c7c1 compiler: add 'align' attribute to runtime.alloc calls
This adds something like 'align 4' to the runtime.alloc calls, so that
the compiler knows the alignment of the allocation and can optimize
accordingly.

The optimization is very small, only 0.01% in my small test. However, my
plan is to read the value in the heap-to-stack transform pass to make it
more correct and let it produce slightly more optimal code.
2024-07-03 07:31:37 +02:00
Damian Gryski 9cb263479c wasi preview 2 support (#4027)
* all: wasip2 support

Co-authored-by: Randy Reddig <randy.reddig@fastly.com>
2024-07-02 07:02:03 -07:00
Ayke van Laethem f18c6e342f test: support GOOS/GOARCH pairs in the -target flag
This means it's possible to test just a particular OS/arch with a
command like this:

    go test -run=Build -target=linux/arm

I found it useful while working on MIPS support.
2024-06-28 12:31:38 +02:00
leongross 2d5a8d407b add support for unix.{RawSyscall,RawSyscallNoError} 2024-06-27 21:14:22 +02:00
leongross 36958b2875 add support for unix.Syscall* invocations
Signed-off-by: leongross <leon.gross@9elements.com>
2024-06-27 21:14:22 +02:00
Ayke van Laethem fae0402fde wasm-unknown: make sure the os package can be imported
See: https://github.com/tinygo-org/tinygo/issues/4314

The os package isn't particularly useful on wasm-unknown, but just like
on baremetal systems it's imported by a lot of packages so it should at
least be possible to import this package.
2024-06-27 21:11:11 +02:00
230 changed files with 10039 additions and 1040 deletions
+1 -1
View File
@@ -68,7 +68,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
key: llvm-build-18-${{ matrix.os }}-v2
key: llvm-build-18-${{ matrix.os }}-v3
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
+16 -14
View File
@@ -68,7 +68,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
key: llvm-build-18-linux-alpine-v1
key: llvm-build-18-linux-alpine-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -134,17 +134,19 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
- name: Install wasmtime
run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
curl https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
uses: bytecodealliance/actions/wasmtime/setup@v1
with:
version: "19.0.1"
- name: Install wasm-tools
uses: bytecodealliance/actions/wasm-tools/setup@v1
- name: Download release artifact
uses: actions/download-artifact@v4
with:
@@ -154,8 +156,8 @@ jobs:
mkdir -p ~/lib
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 tinygo-test-wasip2-fast
- run: make smoketest
assert-test-linux:
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
@@ -187,11 +189,11 @@ jobs:
with:
node-version: '18'
- name: Install wasmtime
run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
uses: bytecodealliance/actions/wasmtime/setup@v1
with:
version: "19.0.1"
- name: Setup `wasm-tools`
uses: bytecodealliance/actions/wasm-tools/setup@v1
- name: Restore LLVM source cache
uses: actions/cache/restore@v4
id: cache-llvm-source
@@ -221,7 +223,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
key: llvm-build-18-linux-asserts-v1
key: llvm-build-18-linux-asserts-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -334,7 +336,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
key: llvm-build-18-linux-${{ matrix.goarch }}-v1
key: llvm-build-18-linux-${{ matrix.goarch }}-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
+1 -1
View File
@@ -9,7 +9,7 @@ concurrency:
jobs:
sizediff:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
permissions:
pull-requests: write
steps:
+3 -3
View File
@@ -66,7 +66,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
key: llvm-build-18-windows-v1
key: llvm-build-18-windows-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -220,5 +220,5 @@ jobs:
shell: bash
working-directory: build
run: 7z x release.zip -r
- name: Test stdlib packages on wasi
run: make tinygo-test-wasi-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
- name: Test stdlib packages on wasip1
run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
+6 -3
View File
@@ -1,3 +1,8 @@
.DS_Store
.vscode
go.work
go.work.sum
docs/_build
src/device/avr/*.go
src/device/avr/*.ld
@@ -17,9 +22,7 @@ src/device/kendryte/*.go
src/device/kendryte/*.s
src/device/rp/*.go
src/device/rp/*.s
src/device/wch/*.go
src/device/wch/*.s
vendor
./vendor
llvm-build
llvm-project
build/*
+3
View File
@@ -39,3 +39,6 @@
path = src/net
url = https://github.com/tinygo-org/net.git
branch = dev
[submodule "lib/wasi-cli"]
path = lib/wasi-cli
url = https://github.com/WebAssembly/wasi-cli
+52 -15
View File
@@ -185,7 +185,7 @@ fmt-check:
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp gen-device-wch
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp gen-device-rp
ifneq ($(STM32), 0)
gen-device: gen-device-stm32
endif
@@ -237,10 +237,6 @@ 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
gen-device-wch: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/ch32-rs/ch32-rs/ lib/cmsis-svd/data/WCH-Community/ src/device/wch
GO111MODULE=off $(GO) fmt ./src/device/wch
# Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm:
git clone -b tinygo_xtensa_release_18.1.2 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
@@ -249,7 +245,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja:
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;AVR;Mips;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
# Build LLVM.
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
@@ -261,7 +257,7 @@ ifneq ($(USE_SYSTEM_BINARYEN),1)
binaryen: build/wasm-opt$(EXE)
build/wasm-opt$(EXE):
mkdir -p build
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
cd lib/binaryen && cmake -G Ninja . -DBUILD_STATIC_LIB=ON -DBUILD_TESTS=OFF -DENABLE_WERROR=OFF $(BINARYEN_OPTION) && ninja bin/wasm-opt$(EXE)
cp lib/binaryen/bin/wasm-opt$(EXE) build/wasm-opt$(EXE)
endif
@@ -272,6 +268,18 @@ 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)
# Generate WASI syscall bindings
WASM_TOOLS_MODULE=github.com/ydnar/wasm-tools-go
.PHONY: wasi-syscall
wasi-syscall: wasi-cm
go run -modfile ./internal/wasm-tools/go.mod $(WASM_TOOLS_MODULE)/cmd/wit-bindgen-go generate --versioned -o ./src/internal -p internal --cm internal/cm ./lib/wasi-cli/wit
# Copy package cm into src/internal/cm
.PHONY: wasi-cm
wasi-cm:
# rm -rf ./src/internal/cm
rsync -rv --delete --exclude '*_test.go' $(shell go list -modfile ./internal/wasm-tools/go.mod -m -f {{.Dir}} $(WASM_TOOLS_MODULE))/cm ./src/internal/
# Check for Node.js used during WASM tests.
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
MIN_NODEJS_VERSION=18
@@ -434,15 +442,36 @@ tinygo-test-wasi:
$(TINYGO) test -target wasip1 $(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 wasip1 $(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=wasip1 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-test-wasip2-slow:
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_SLOW)
tinygo-test-wasip2-fast:
$(TINYGO) test -target=wasip2 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-test-wasip2-sum-slow:
TINYGO=$(TINYGO) \
TARGET=wasip2 \
TESTOPTS="-x -work" \
PACKAGES="$(TEST_PACKAGES_SLOW)" \
gotestsum --raw-command -- ./tools/tgtestjson.sh
tinygo-test-wasip2-sum-fast:
TINYGO=$(TINYGO) \
TARGET=wasip2 \
TESTOPTS="-x -work" \
PACKAGES="$(TEST_PACKAGES_FAST)" \
gotestsum --raw-command -- ./tools/tgtestjson.sh
tinygo-bench-wasip1:
$(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasi-fast:
tinygo-bench-wasip1-fast:
$(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST)
tinygo-bench-wasip2:
$(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
tinygo-bench-wasip2-fast:
$(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST)
# Test external packages in a large corpus.
test-corpus:
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml
@@ -787,8 +816,6 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=maixbit examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nanoch32v003 examples/blinky1
@$(MD5SUM) test.hex
ifneq ($(WASM), 0)
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
@@ -808,6 +835,7 @@ endif
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
@$(MD5SUM) test.hex
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
GOOS=linux GOARCH=mips $(TINYGO) build -size short -o test.elf ./testdata/cgo
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
@@ -838,6 +866,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@mkdir -p build/release/tinygo/lib/wasi-cli/
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus
@mkdir -p build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4
@@ -854,6 +883,7 @@ endif
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/mips build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
@@ -897,6 +927,7 @@ endif
@cp -rp lib/wasi-libc/libc-top-half/musl/src/string build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
@cp -rp src build/release/tinygo/src
@@ -924,6 +955,11 @@ release: build/release
deb: build/release
endif
.PHONY: tools
tools:
go generate -C ./internal/tools -tags tools ./
.PHONY: lint
lint:
go run github.com/mgechev/revive -version
# TODO: lint more directories!
@@ -932,6 +968,7 @@ lint:
# Use 'grep .' to get rid of stray blank line
go run github.com/mgechev/revive -config revive.toml compiler/... src/{os,reflect}/*.go | grep -v "should have comment or be unexported" | grep '.' | awk '{print}; END {exit NR>0}'
.PHONY: spell
spell:
# Check for typos in comments. Skip git submodules etc.
# Check for typos in comments. Skip git submodules etc.
go run github.com/client9/misspell/cmd/misspell -i 'ackward,devided,extint,inbetween,programmmer,rela' $$( find . -depth 1 -type d | egrep -w -v 'lib|llvm|src/net' )
+61 -9
View File
@@ -353,10 +353,6 @@ 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{
@@ -379,7 +375,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
defer mod.Context().Dispose()
defer mod.Dispose()
if errs != nil {
return newMultiError(errs)
return newMultiError(errs, pkg.ImportPath)
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after compiling package " + pkg.ImportPath)
@@ -840,11 +836,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
"--output", result.Executable,
)
wasmopt := goenv.Get("WASMOPT")
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(goenv.Get("WASMOPT"), args...)
config.Options.PrintCommands(wasmopt, args...)
}
cmd := exec.Command(goenv.Get("WASMOPT"), args...)
cmd := exec.Command(wasmopt, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -854,6 +850,62 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
}
// Run wasm-tools for component-model binaries
witPackage := strings.ReplaceAll(config.Target.WITPackage, "{root}", goenv.Get("TINYGOROOT"))
if config.Options.WITPackage != "" {
witPackage = config.Options.WITPackage
}
witWorld := config.Target.WITWorld
if config.Options.WITWorld != "" {
witWorld = config.Options.WITWorld
}
if witPackage != "" && witWorld != "" {
// wasm-tools component embed -w wasi:cli/command
// $$(tinygo env TINYGOROOT)/lib/wasi-cli/wit/ main.wasm -o embedded.wasm
args := []string{
"component",
"embed",
"-w", witWorld,
witPackage,
result.Executable,
"-o", result.Executable,
}
wasmtools := goenv.Get("WASMTOOLS")
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(wasmtools, args...)
}
cmd := exec.Command(wasmtools, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("wasm-tools failed: %w", err)
}
// wasm-tools component new embedded.wasm -o component.wasm
args = []string{
"component",
"new",
result.Executable,
"-o", result.Executable,
}
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(wasmtools, args...)
}
cmd = exec.Command(wasmtools, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return fmt.Errorf("wasm-tools failed: %w", err)
}
}
// Print code size if requested.
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
packagePathMap := make(map[string]string, len(lprogram.Packages))
@@ -1078,7 +1130,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m
// O0/O1/O2/Os/Oz optimization pipeline).
errs := transform.Optimize(mod, config)
if len(errs) > 0 {
return newMultiError(errs)
return newMultiError(errs, "")
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification failure after LLVM optimization passes")
+4 -1
View File
@@ -27,7 +27,6 @@ func TestClangAttributes(t *testing.T) {
"cortex-m33",
"cortex-m4",
"cortex-m7",
"ch32v003",
"esp32c3",
"fe310",
"gameboy-advance",
@@ -35,6 +34,7 @@ func TestClangAttributes(t *testing.T) {
"nintendoswitch",
"riscv-qemu",
"wasip1",
"wasip2",
"wasm",
"wasm-unknown",
}
@@ -57,11 +57,14 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
{GOOS: "linux", GOARCH: "arm64"},
{GOOS: "linux", GOARCH: "mips"},
{GOOS: "linux", GOARCH: "mipsle"},
{GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "arm64"},
{GOOS: "wasip1", GOARCH: "wasm"},
{GOOS: "wasip2", GOARCH: "wasm"},
} {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" {
+5 -3
View File
@@ -3,7 +3,8 @@ package builder
// MultiError is a list of multiple errors (actually: diagnostics) returned
// during LLVM IR generation.
type MultiError struct {
Errs []error
ImportPath string
Errs []error
}
func (e *MultiError) Error() string {
@@ -15,14 +16,15 @@ func (e *MultiError) Error() string {
// newMultiError returns a *MultiError if there is more than one error, or
// returns that error directly when there is only one. Passing an empty slice
// will lead to a panic.
func newMultiError(errs []error) error {
// The importPath may be passed if this error is for a single package.
func newMultiError(errs []error, importPath string) error {
switch len(errs) {
case 0:
panic("attempted to create empty MultiError")
case 1:
return errs[0]
default:
return &MultiError{errs}
return &MultiError{importPath, errs}
}
}
+3
View File
@@ -182,6 +182,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if strings.HasPrefix(target, "riscv64-") {
args = append(args, "-march=rv64gc")
}
if strings.HasPrefix(target, "mips") {
args = append(args, "-fno-pic")
}
var once sync.Once
+8 -1
View File
@@ -7,6 +7,7 @@ import (
"go/ast"
"go/format"
"go/parser"
"go/scanner"
"go/token"
"go/types"
"os"
@@ -219,7 +220,13 @@ func (i simpleImporter) Import(path string) (*types.Package, error) {
// formatDiagnostic formats the error message to be an indented comment. It
// also fixes Windows path name issues (backward slashes).
func formatDiagnostic(err error) string {
msg := err.Error()
var msg string
switch err := err.(type) {
case scanner.Error:
msg = err.Pos.String() + ": " + err.Msg
default:
msg = err.Error()
}
if runtime.GOOS == "windows" {
// Fix Windows path slashes.
msg = strings.ReplaceAll(msg, "testdata\\", "testdata/")
+6 -2
View File
@@ -195,7 +195,9 @@ func (t *tokenizer) Next() {
t.curValue = t.peekValue
// Parse the next peek token.
t.peekPos += token.Pos(len(t.curValue))
if t.peekPos != token.NoPos {
t.peekPos += token.Pos(len(t.curValue))
}
for {
if len(t.buf) == 0 {
t.peekToken = token.EOF
@@ -207,7 +209,9 @@ func (t *tokenizer) Next() {
// Skip whitespace.
// Based on this source, not sure whether it represents C whitespace:
// https://en.cppreference.com/w/cpp/string/byte/isspace
t.peekPos++
if t.peekPos != token.NoPos {
t.peekPos++
}
t.buf = t.buf[1:]
case len(t.buf) >= 2 && (string(t.buf[:2]) == "||" || string(t.buf[:2]) == "&&" || string(t.buf[:2]) == "<<" || string(t.buf[:2]) == ">>"):
// Two-character tokens.
+36 -39
View File
@@ -4,6 +4,7 @@ package cgo
// modification. It does not touch the AST itself.
import (
"bytes"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
@@ -369,42 +370,45 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
case C.CXCursor_MacroDefinition:
// Extract tokens from the Clang tokenizer.
// See: https://stackoverflow.com/a/19074846/559350
sourceRange := C.tinygo_clang_getCursorExtent(c)
start := C.clang_getRangeStart(sourceRange)
end := C.clang_getRangeEnd(sourceRange)
var file, endFile C.CXFile
var startOffset, endOffset C.unsigned
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
if file == nil {
f.addError(pos, "internal error: could not find file where macro is defined")
return nil, nil
}
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
if file != endFile {
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
return nil, nil
}
if startOffset > endOffset {
f.addError(pos, "internal error: start offset of macro is after end offset")
return nil, nil
}
// read file contents and extract the relevant byte range
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
var size C.size_t
sourcePtr := C.clang_getFileContents(tu, file, &size)
if endOffset >= C.uint(size) {
f.addError(pos, "internal error: end offset of macro lies after end of file")
return nil, nil
var rawTokens *C.CXToken
var numTokens C.unsigned
C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens)
tokens := unsafe.Slice(rawTokens, numTokens)
// Convert this range of tokens back to source text.
// Ugly, but it works well enough.
sourceBuf := &bytes.Buffer{}
var startOffset int
for i, token := range tokens {
spelling := getString(C.clang_getTokenSpelling(tu, token))
location := C.clang_getTokenLocation(tu, token)
var tokenOffset C.unsigned
C.clang_getExpansionLocation(location, nil, nil, nil, &tokenOffset)
if i == 0 {
// The first token is the macro name itself.
// Skip it (after using its location).
startOffset = int(tokenOffset) + len(name)
} else {
// Later tokens are the macro contents.
for int(tokenOffset) > (startOffset + sourceBuf.Len()) {
// Pad the source text with whitespace (that must have been
// present in the original source as well).
sourceBuf.WriteByte(' ')
}
sourceBuf.WriteString(spelling)
}
}
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
if !strings.HasPrefix(source, name) {
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
return nil, nil
}
value := source[len(name):]
C.clang_disposeTokens(tu, rawTokens, numTokens)
value := sourceBuf.String()
// Try to convert this #define into a Go constant expression.
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
tokenPos := token.NoPos
if pos != token.NoPos {
tokenPos = pos + token.Pos(len(name))
}
expr, scannerError := parseConst(tokenPos, f.fset, value)
if scannerError != nil {
f.errors = append(f.errors, *scannerError)
return nil, nil
@@ -642,13 +646,6 @@ func (p *cgoPackage) addErrorAfter(pos token.Pos, after, msg string) {
// addErrorAt is a utility function to add an error to the list of errors.
func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
if filepath.IsAbs(position.Filename) {
// Relative paths for readability, like other Go parser errors.
relpath, err := filepath.Rel(p.currentDir, position.Filename)
if err == nil {
position.Filename = relpath
}
}
p.errors = append(p.errors, scanner.Error{
Pos: position,
Msg: msg,
+13
View File
@@ -13,10 +13,14 @@ typedef someType noType; // undefined type
#define SOME_CONST_1 5) // invalid const syntax
#define SOME_CONST_2 6) // const not used (so no error)
#define SOME_CONST_3 1234 // const too large for byte
#define SOME_CONST_b 3 ) // const with lots of weird whitespace (to test error locations)
# define SOME_CONST_startspace 3)
*/
//
//
// #define SOME_CONST_4 8) // after some empty lines
// #cgo CFLAGS: -DSOME_PARAM_CONST_invalid=3/+3
// #cgo CFLAGS: -DSOME_PARAM_CONST_valid=3+4
import "C"
// #warning another warning
@@ -24,6 +28,7 @@ import "C"
// Make sure that errors for the following lines won't change with future
// additions to the CGo preamble.
//
//line errors.go:100
var (
// constant too large
@@ -38,4 +43,12 @@ var (
_ byte = C.SOME_CONST_3
_ = C.SOME_CONST_4
_ = C.SOME_CONST_b
_ = C.SOME_CONST_startspace
// constants passed by a command line parameter
_ = C.SOME_PARAM_CONST_invalid
_ = C.SOME_PARAM_CONST_valid
)
+9 -2
View File
@@ -1,9 +1,12 @@
// CGo errors:
// testdata/errors.go:4:2: warning: some warning
// testdata/errors.go:11:9: error: unknown type name 'someType'
// testdata/errors.go:22:5: warning: another warning
// testdata/errors.go:26:5: warning: another warning
// testdata/errors.go:13:23: unexpected token ), expected end of expression
// testdata/errors.go:19:26: unexpected token ), expected end of expression
// testdata/errors.go:21:26: unexpected token ), expected end of expression
// testdata/errors.go:16:33: unexpected token ), expected end of expression
// testdata/errors.go:17:34: unexpected token ), expected end of expression
// -: unexpected token INT, expected end of expression
// Type checking errors after CGo processing:
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
@@ -11,6 +14,9 @@
// testdata/errors.go:108: undefined: C.SOME_CONST_1
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
// testdata/errors.go:112: undefined: C.SOME_CONST_4
// testdata/errors.go:114: undefined: C.SOME_CONST_b
// testdata/errors.go:116: undefined: C.SOME_CONST_startspace
// testdata/errors.go:119: undefined: C.SOME_PARAM_CONST_invalid
package main
@@ -58,3 +64,4 @@ type C.struct_point_t struct {
type C.point_t = C.struct_point_t
const C.SOME_CONST_3 = 1234
const C.SOME_PARAM_CONST_valid = 3 + 4
+4 -1
View File
@@ -212,7 +212,10 @@ func (c *Config) RP2040BootPatch() bool {
func MuslArchitecture(triple string) string {
arch := strings.Split(triple, "-")[0]
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
arch = "arm"
return "arm"
}
if arch == "mipsel" {
return "mips"
}
return arch
}
+2
View File
@@ -53,6 +53,8 @@ type Options struct {
Monitor bool
BaudRate int
Timeout time.Duration
WITPackage string // pass through to wasm-tools component embed invocation
WITWorld string // pass through to wasm-tools component embed -w option
}
// Verify performs a validation on the given options, raising an error if options are not valid.
+20 -2
View File
@@ -62,6 +62,8 @@ type TargetSpec struct {
JLinkDevice string `json:"jlink-device,omitempty"`
CodeModel string `json:"code-model,omitempty"`
RelocationModel string `json:"relocation-model,omitempty"`
WITPackage string `json:"wit-package,omitempty"`
WITWorld string `json:"wit-world,omitempty"`
}
// overrideProperties overrides all properties that are set in child into itself using reflection.
@@ -191,6 +193,10 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
default:
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
}
case "mips":
llvmarch = "mips"
case "mipsle":
llvmarch = "mipsel"
case "wasm":
llvmarch = "wasm32"
default:
@@ -325,6 +331,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
} else { // linux
spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
}
case "mips", "mipsle":
spec.CPU = "mips32r2"
spec.Features = "+fpxx,+mips32r2,+nooddspreg,-noabicalls"
spec.CFlags = append(spec.CFlags, "-fno-pic")
case "wasm":
spec.CPU = "generic"
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
@@ -417,8 +427,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
// operating systems so we need separate assembly files.
suffix = "_windows"
}
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
asmGoarch := goarch
if goarch == "mips" || goarch == "mipsle" {
asmGoarch = "mipsx"
}
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+asmGoarch+suffix+".S")
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+asmGoarch+suffix+".S")
}
if goarch != runtime.GOARCH {
// Some educated guesses as to how to invoke helper programs.
@@ -436,6 +450,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Emulator = "qemu-arm {}"
case "arm64":
spec.Emulator = "qemu-aarch64 {}"
case "mips":
spec.Emulator = "qemu-mips {}"
case "mipsle":
spec.Emulator = "qemu-mipsel {}"
}
}
}
+6 -2
View File
@@ -1846,9 +1846,9 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.emitSV64Call(instr.Args, getPos(instr))
case strings.HasPrefix(name, "(device/riscv.CSR)."):
return b.emitCSROperation(instr)
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall"):
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall"):
return b.createSyscall(instr)
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"):
return b.createRawSyscallNoError(instr)
case name == "runtime.supportsRecover":
supportsRecover := uint64(0)
@@ -2017,6 +2017,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
layoutValue := b.createObjectLayout(typ, expr.Pos())
buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
align := b.targetData.ABITypeAlignment(typ)
buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
return buf, nil
} else {
buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
@@ -2223,6 +2225,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sliceType := expr.Type().Underlying().(*types.Slice)
llvmElemType := b.getLLVMType(sliceType.Elem())
elemSize := b.targetData.TypeAllocSize(llvmElemType)
elemAlign := b.targetData.ABITypeAlignment(llvmElemType)
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
maxSize := b.maxSliceSize(llvmElemType)
@@ -2246,6 +2249,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
// Extend or truncate if necessary. This is safe as we've already done
// the bounds check.
+12
View File
@@ -187,6 +187,18 @@ std z+5, r29
ldi r24, 0
1:`
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
case "mips":
// $4 flag (zero or non-zero)
// $5 defer frame
asmString = `
.set noat
move $$4, $$zero
jal 1f
1:
addiu $$ra, 8
sw $$ra, 4($$5)
.set at`
constraints = "={$4},{$5},~{$1},~{$2},~{$3},~{$5},~{$6},~{$7},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$16},~{$17},~{$18},~{$19},~{$20},~{$21},~{$22},~{$23},~{$24},~{$25},~{$26},~{$27},~{$28},~{$29},~{$30},~{$31},~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f12},~{$f13},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31},~{memory}"
case "riscv32":
asmString = `
la a2, 1f
+90 -27
View File
@@ -10,6 +10,7 @@ import (
"fmt"
"go/token"
"go/types"
"sort"
"strconv"
"strings"
@@ -180,6 +181,15 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
typeFieldTypes := []*types.Var{
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
}
var methods []*types.Func
for i := 0; i < ms.Len(); i++ {
methods = append(methods, ms.At(i).Obj().(*types.Func))
}
methodSetType := types.NewStruct([]*types.Var{
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
types.NewVar(token.NoPos, nil, "methods", types.NewArray(types.Typ[types.UnsafePointer], int64(len(methods)))),
}, nil)
methodSetValue := c.getMethodSetValue(methods)
switch typ := typ.(type) {
case *types.Basic:
typeFieldTypes = append(typeFieldTypes,
@@ -196,6 +206,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, "underlying", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "methods", methodSetType),
types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(pkgname)+1+len(name)+1))),
)
case *types.Chan:
@@ -214,6 +225,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "methods", methodSetType),
)
case *types.Array:
typeFieldTypes = append(typeFieldTypes,
@@ -238,12 +250,13 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uint32]),
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
types.NewVar(token.NoPos, nil, "methods", methodSetType),
)
case *types.Interface:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "methods", methodSetType),
)
// TODO: methods
case *types.Signature:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
@@ -294,6 +307,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Underlying()), // underlying
pkgPathPtr, // pkgpath pointer
methodSetValue, // methods
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
}
metabyte |= 1 << 5 // "named" flag
@@ -323,6 +337,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
typeFields = []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
c.getTypeCode(typ.Elem()),
methodSetValue, // methods
}
case *types.Array:
typeFields = []llvm.Value{
@@ -404,9 +419,12 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
}))
}
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
typeFields = append(typeFields, methodSetValue)
case *types.Interface:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: methods
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)),
methodSetValue,
}
case *types.Signature:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: params, return values, etc
@@ -685,7 +703,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
assertedType := b.getLLVMType(expr.AssertedType)
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
commaOk := llvm.Value{}
var commaOk llvm.Value
if intf, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
if intf.Empty() {
@@ -693,17 +711,16 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
// This type assertion always succeeds, so we can just set commaOk to true.
commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true)
} else {
// Type assert on interface type with methods.
// This is a call to an interface type assert function.
// The interface lowering pass will define this function by filling it
// with a type switch over all concrete types that implement this
// interface, and returning whether it's one of the matched types.
// This is very different from how interface asserts are implemented in
// the main Go compiler, where the runtime checks whether the type
// implements each method of the interface. See:
// Type assert using interface type with methods.
// This is implemented using a runtime call, which checks that the
// type implements each method of the interface.
// For comparison, here is how the Go compiler does this (which is
// very similar):
// https://research.swtch.com/interfaces
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
commaOk = b.createRuntimeCall("typeImplementsMethodSet", []llvm.Value{
actualTypeNum,
b.getInterfaceMethodSet(intf),
}, "")
}
} else {
name, _ := getTypeCodeName(expr.AssertedType)
@@ -780,20 +797,66 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string {
return strings.Join(methods, "; ")
}
// getInterfaceImplementsFunc returns a declared function that works as a type
// switch. The interface lowering pass will define this function.
func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
s, _ := getTypeCodeName(assertedType.Underlying())
fnName := s + ".$typeassert"
llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn)
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
// Get the global that contains an interface method set, creating it if needed.
func (c *compilerContext) getInterfaceMethodSet(t *types.Interface) llvm.Value {
s, _ := getTypeCodeName(t)
methodSetName := s + "$itfmethods"
methodSet := c.mod.NamedFunction(methodSetName)
if methodSet.IsNil() {
var methods []*types.Func
for i := 0; i < t.NumMethods(); i++ {
methods = append(methods, t.Method(i))
}
if len(methods) == 0 {
// This *should* be unreachable: the caller checks whether the
// interface is empty before creating a method set.
panic("unreachable")
}
methodSetValue := c.getMethodSetValue(methods)
methodSet = llvm.AddGlobal(c.mod, methodSetValue.Type(), methodSetName)
methodSet.SetInitializer(methodSetValue)
methodSet.SetGlobalConstant(true)
methodSet.SetLinkage(llvm.LinkOnceODRLinkage)
methodSet.SetAlignment(c.targetData.ABITypeAlignment(methodSetValue.Type()))
methodSet.SetUnnamedAddr(true)
}
return llvmFn
return methodSet
}
// Get the method set value that is used in a number of type structs.
func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
// Create a sorted list of method names.
var methodRefNames []string
for _, method := range methods {
name := method.Name()
if !token.IsExported(name) {
name = method.Pkg().Path() + "." + name
}
s, _ := getTypeCodeName(method.Type())
methodRefNames = append(methodRefNames, "reflect/types.signature:"+name+":"+s)
}
sort.Strings(methodRefNames)
// Turn this slice of strings in a slice of global variables.
var methodRefValues []llvm.Value
for _, name := range methodRefNames {
value := c.mod.NamedGlobal(name)
if value.IsNil() {
value = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), name)
value.SetInitializer(llvm.ConstNull(c.ctx.Int8Type()))
value.SetGlobalConstant(true)
value.SetLinkage(llvm.LinkOnceODRLinkage)
value.SetAlignment(1)
}
methodRefValues = append(methodRefValues, value)
}
return c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, uint64(len(methodRefValues)), false),
llvm.ConstArray(c.dataPtrType, methodRefValues),
}, false)
}
// getInvokeFunction returns the thunk to call the given interface method. The
+5
View File
@@ -127,12 +127,14 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
// Packed data is bigger than a pointer, so allocate it on the heap.
sizeValue := llvm.ConstInt(b.uintptrType, size, false)
align := b.targetData.ABITypeAlignment(packedType)
alloc := b.mod.NamedFunction("runtime.alloc")
packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{
sizeValue,
llvm.ConstNull(b.dataPtrType),
llvm.Undef(b.dataPtrType), // unused context parameter
}, "")
packedAlloc.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
if b.NeedsStackObjects {
b.trackPointer(packedAlloc)
}
@@ -427,6 +429,9 @@ func (c *compilerContext) archFamily() string {
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
return "arm"
}
if arch == "mipsel" {
return "mips"
}
return arch
}
+39 -9
View File
@@ -346,7 +346,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" || c.pkg.Path() == "syscall/js" {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" {
// The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers).
return
@@ -360,38 +360,68 @@ func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma))
} else if f.Signature.Results().Len() == 1 {
result := f.Signature.Results().At(0)
if !isValidWasmType(result.Type(), true) {
if !isValidWasmType(result.Type(), siteResult) {
c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String()))
}
}
for _, param := range f.Params {
// Check whether the type is allowed.
// Only a very limited number of types can be mapped to WebAssembly.
if !isValidWasmType(param.Type(), false) {
if !isValidWasmType(param.Type(), siteParam) {
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
}
}
}
// Check whether the type maps directly to a WebAssembly type, according to:
// Check whether the type maps directly to a WebAssembly type.
//
// This reflects the relaxed type restrictions proposed here (except for structs.HostLayout):
// https://github.com/golang/go/issues/66984
//
// This previously reflected the additional restrictions documented here:
// https://github.com/golang/go/issues/59149
func isValidWasmType(typ types.Type, isReturn bool) bool {
func isValidWasmType(typ types.Type, site wasmSite) bool {
switch typ := typ.Underlying().(type) {
case *types.Basic:
switch typ.Kind() {
case types.Int32, types.Uint32, types.Int64, types.Uint64:
case types.Bool:
return true
case types.Int, types.Uint, types.Int8, types.Uint8, types.Int16, types.Uint16, types.Int32, types.Uint32, types.Int64, types.Uint64:
return true
case types.Float32, types.Float64:
return true
case types.UnsafePointer:
if !isReturn {
return true
case types.Uintptr, types.UnsafePointer:
return true
case types.String:
// string flattens to two values, so disallowed as a result
return site == siteParam || site == siteIndirect
}
case *types.Array:
return site == siteIndirect && isValidWasmType(typ.Elem(), siteIndirect)
case *types.Struct:
if site != siteIndirect {
return false
}
for i := 0; i < typ.NumFields(); i++ {
if !isValidWasmType(typ.Field(i).Type(), siteIndirect) {
return false
}
}
return true
case *types.Pointer:
return isValidWasmType(typ.Elem(), siteIndirect)
}
return false
}
type wasmSite int
const (
siteParam wasmSite = iota
siteResult
siteIndirect // pointer or field
)
// getParams returns the function parameters, including the receiver at the
// start. This is an alternative to the Params member of *ssa.Function, which is
// not yet populated when the package has not yet been built.
+93 -1
View File
@@ -12,7 +12,8 @@ import (
// createRawSyscall creates a system call with the provided system call number
// and returns the result as a single integer (the system call result). The
// result is not further interpreted.
// result is not further interpreted (with the exception of MIPS to use the same
// return value everywhere).
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
num := b.getValue(call.Args[0], getPos(call))
switch {
@@ -137,6 +138,97 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
return b.CreateCall(fnType, target, args, ""), nil
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
// Implement the system call convention for Linux.
// Source: syscall(2) man page and musl:
// https://git.musl-libc.org/cgit/musl/tree/arch/mips/syscall_arch.h
// Also useful:
// https://web.archive.org/web/20220529105937/https://www.linux-mips.org/wiki/Syscall
// The syscall number goes in r2, the result also in r2.
// Register r7 is both an input paramter and an output parameter: if it
// is non-zero, the system call failed and r2 is the error code.
// The code below implements the O32 syscall ABI, not the N32 ABI. It
// could implement both at the same time if needed (like what appears to
// be done in musl) by forcing arg5-arg7 into the right registers but
// letting the compiler decide the registers should result in _slightly_
// faster and smaller code.
args := []llvm.Value{num}
argTypes := []llvm.Type{b.uintptrType}
constraints := "={$2},={$7},0"
syscallParams := call.Args[1:]
if len(syscallParams) > 7 {
// There is one syscall that uses 7 parameters: sync_file_range.
// But only 7, not more. Go however only has Syscall6 and Syscall9.
// Therefore, we can ignore the remaining parameters.
syscallParams = syscallParams[:7]
}
for i, arg := range syscallParams {
constraints += "," + [...]string{
"{$4}", // arg1
"{$5}", // arg2
"{$6}", // arg3
"1", // arg4, error return
"r", // arg5 on the stack
"r", // arg6 on the stack
"r", // arg7 on the stack
}[i]
llvmValue := b.getValue(arg, getPos(call))
args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
// Create assembly code.
// Parameters beyond the first 4 are passed on the stack instead of in
// registers in the O32 syscall ABI.
// We need ".set noat" because LLVM might pick register $1 ($at) as the
// register for a parameter and apparently this is not allowed on MIPS
// unless you use this specific pragma.
asm := "syscall"
switch len(syscallParams) {
case 5:
asm = "" +
".set noat\n" +
"subu $$sp, $$sp, 32\n" +
"sw $7, 16($$sp)\n" + // arg5
"syscall\n" +
"addu $$sp, $$sp, 32\n" +
".set at\n"
case 6:
asm = "" +
".set noat\n" +
"subu $$sp, $$sp, 32\n" +
"sw $7, 16($$sp)\n" + // arg5
"sw $8, 20($$sp)\n" + // arg6
"syscall\n" +
"addu $$sp, $$sp, 32\n" +
".set at\n"
case 7:
asm = "" +
".set noat\n" +
"subu $$sp, $$sp, 32\n" +
"sw $7, 16($$sp)\n" + // arg5
"sw $8, 20($$sp)\n" + // arg6
"sw $9, 24($$sp)\n" + // arg7
"syscall\n" +
"addu $$sp, $$sp, 32\n" +
".set at\n"
}
constraints += ",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}"
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
fnType := llvm.FunctionType(returnType, argTypes, false)
target := llvm.InlineAsm(fnType, asm, constraints, true, true, 0, false)
call := b.CreateCall(fnType, target, args, "")
resultCode := b.CreateExtractValue(call, 0, "") // r2
errorFlag := b.CreateExtractValue(call, 1, "") // r7
// Pseudocode to return the result with the same convention as other
// archs:
// return (errorFlag != 0) ? -resultCode : resultCode;
// At least on QEMU with the O32 ABI, the error code is always positive.
zero := llvm.ConstInt(b.uintptrType, 0, false)
isError := b.CreateICmp(llvm.IntNE, errorFlag, zero, "")
negativeResult := b.CreateSub(zero, resultCode, "")
result := b.CreateSelect(isError, negativeResult, resultCode, "")
return result, nil
default:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
}
+48 -14
View File
@@ -13,31 +13,65 @@ func implementation() {
type Uint uint32
//go:wasmimport modulename validparam
func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint)
type S struct {
a [4]uint32
b uintptr
c int
d float32
e float64
}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type string
//go:wasmimport modulename validparam
func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S)
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [4]uint32
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type *int32
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type struct{a int}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type chan struct{}
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type func()
//
//go:wasmimport modulename invalidparam
func invalidparam(a int, b string, c []byte, d *int32)
func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func())
//go:wasmimport modulename validreturn
func validreturn() int32
//go:wasmimport modulename validreturn_int32
func validreturn_int32() int32
//go:wasmimport modulename validreturn_int
func validreturn_int() int
//go:wasmimport modulename validreturn_ptr_int32
func validreturn_ptr_int32() *int32
//go:wasmimport modulename validreturn_ptr_string
func validreturn_ptr_string() *string
//go:wasmimport modulename validreturn_ptr_struct
func validreturn_ptr_struct() *S
//go:wasmimport modulename validreturn_unsafe_pointer
func validreturn_unsafe_pointer() unsafe.Pointer
// ERROR: //go:wasmimport modulename manyreturns: too many return values
//
//go:wasmimport modulename manyreturns
func manyreturns() (int32, int32)
// ERROR: //go:wasmimport modulename invalidreturn: unsupported result type int
// ERROR: //go:wasmimport modulename invalidreturn_func: unsupported result type func()
//
//go:wasmimport modulename invalidreturn
func invalidreturn() int
//go:wasmimport modulename invalidreturn_func
func invalidreturn_func() func()
// ERROR: //go:wasmimport modulename invalidUnsafePointerReturn: unsupported result type unsafe.Pointer
// ERROR: //go:wasmimport modulename invalidreturn_slice_byte: unsupported result type []byte
//
//go:wasmimport modulename invalidUnsafePointerReturn
func invalidUnsafePointerReturn() unsafe.Pointer
//go:wasmimport modulename invalidreturn_slice_byte
func invalidreturn_slice_byte() []byte
// ERROR: //go:wasmimport modulename invalidreturn_chan_int: unsupported result type chan int
//
//go:wasmimport modulename invalidreturn_chan_int
func invalidreturn_chan_int() chan int
// ERROR: //go:wasmimport modulename invalidreturn_string: unsupported result type string
//
//go:wasmimport modulename invalidreturn_string
func invalidreturn_string() string
+17 -17
View File
@@ -22,7 +22,7 @@ target triple = "wasm32-unknown-wasi"
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128", { i32, [0 x ptr] } zeroinitializer }, align 4
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
@@ -39,16 +39,16 @@ entry:
define hidden void @main.newScalar(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%new = call dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new, ptr @main.scalar1, align 4
%new1 = call dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%new1 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new1, ptr @main.scalar2, align 4
%new2 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%new2 = call align 8 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new2, ptr @main.scalar3, align 4
%new3 = call dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%new3 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new3, ptr @main.scalar4, align 4
ret void
@@ -58,13 +58,13 @@ entry:
define hidden void @main.newArray(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%new = call dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new, ptr @main.array1, align 4
%new1 = call dereferenceable(71) ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%new1 = call align 1 dereferenceable(71) ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new1, ptr @main.array2, align 4
%new2 = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
%new2 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new2, ptr @main.array3, align 4
ret void
@@ -74,16 +74,16 @@ entry:
define hidden void @main.newStruct(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%new = call ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new, ptr @main.struct1, align 4
%new1 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new1, ptr @main.struct2, align 4
%new2 = call dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #3
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new2, ptr @main.struct3, align 4
%new3 = call dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #3
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
store ptr %new3, ptr @main.struct4, align 4
ret void
@@ -93,7 +93,7 @@ entry:
define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%new = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
%new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3
ret ptr %new
}
@@ -102,17 +102,17 @@ entry:
define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%makeslice = call dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
store ptr %makeslice, ptr @main.slice1, align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 4
%makeslice1 = call dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
%makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3
store ptr %makeslice1, ptr @main.slice2, align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 1), align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 4
%makeslice3 = call dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3
%makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3
store ptr %makeslice3, ptr @main.slice3, align 4
store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 1), align 4
@@ -124,7 +124,7 @@ entry:
define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
%0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3
store double %v.r, ptr %0, align 8
%.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1
+4 -4
View File
@@ -63,9 +63,9 @@ entry:
; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry:
%n = call dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
store i32 3, ptr %n, align 4
%0 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
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
@@ -98,7 +98,7 @@ declare void @runtime.printint32(i32, ptr) #2
; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
entry:
%0 = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
store ptr %fn.context, ptr %1, align 4
@@ -148,7 +148,7 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #2
; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry:
%0 = call dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
store ptr @"main$string", ptr %1, align 4
+4 -4
View File
@@ -66,12 +66,12 @@ entry:
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%n = call dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
store i32 3, ptr %n, align 4
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #9
%0 = call dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
@@ -106,7 +106,7 @@ declare void @runtime.printint32(i32, ptr) #1
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1
@@ -158,7 +158,7 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #1
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1
+31 -31
View File
@@ -7,15 +7,19 @@ target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 }
@"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:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int", { i32, [0 x ptr] } zeroinitializer }, align 4
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:named:error", { i32, [0 x ptr] } zeroinitializer }, align 4
@"reflect/types.signature:Error:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x 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", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, [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
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] } }, align 4
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", { i32, [0 x ptr] } zeroinitializer }, align 4
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}", { i32, [0 x ptr] } zeroinitializer }, align 4
@"reflect/types.signature:String:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
@"reflect/types.typeid:basic:int" = external constant i8
@"interface:{Error:func:{}{basic:string}}$itfmethods" = linkonce_odr unnamed_addr constant { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, align 4
@"interface:{String:func:{}{basic:string}}$itfmethods" = linkonce_odr unnamed_addr constant { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] }, align 4
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
@@ -32,8 +36,8 @@ entry:
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
}
@@ -41,8 +45,8 @@ entry:
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
}
@@ -50,8 +54,8 @@ entry:
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
}
@@ -59,15 +63,15 @@ entry:
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #5
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
}
; Function Attrs: nounwind
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #5
br i1 %typecode, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -82,7 +86,7 @@ declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
; Function Attrs: nounwind
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
%0 = call i1 @runtime.typeImplementsMethodSet(ptr %itf.typecode, ptr nonnull @"interface:{Error:func:{}{basic:string}}$itfmethods", ptr undef) #5
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -92,12 +96,12 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
declare i1 @runtime.typeImplementsMethodSet(ptr, ptr, ptr) #1
; Function Attrs: nounwind
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
%0 = call i1 @runtime.typeImplementsMethodSet(ptr %itf.typecode, ptr nonnull @"interface:{String:func:{}{basic:string}}$itfmethods", ptr undef) #5
br i1 %0, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry
@@ -107,34 +111,30 @@ typeassert.ok: ; preds = %entry
br label %typeassert.next
}
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
; Function Attrs: nounwind
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #5
ret i8 %0
}
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #3
; Function Attrs: nounwind
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #5
%1 = extractvalue %runtime._string %0, 0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #5
ret %runtime._string %0
}
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #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 = { "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 }
attributes #3 = { "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 #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
attributes #5 = { nounwind }
+6 -6
View File
@@ -48,7 +48,7 @@ declare void @runtime.lookupPanic(ptr) #1
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%varargs = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #3
store i32 1, ptr %varargs, align 4
%0 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 1
@@ -100,7 +100,7 @@ entry:
br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry
%makeslice.buf = call ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
@@ -123,7 +123,7 @@ entry:
slice.next: ; preds = %entry
%makeslice.cap = shl nuw i32 %len, 1
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice.buf = call align 2 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
@@ -144,7 +144,7 @@ entry:
slice.next: ; preds = %entry
%makeslice.cap = mul i32 %len, 3
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
@@ -165,7 +165,7 @@ entry:
slice.next: ; preds = %entry
%makeslice.cap = shl nuw i32 %len, 2
%makeslice.buf = call ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice.buf = call align 4 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
@@ -216,7 +216,7 @@ declare void @runtime.sliceToArrayPointerPanic(ptr) #1
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
entry:
%stackalloc = alloca i8, align 1
%makeslice = call dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
+212
View File
@@ -0,0 +1,212 @@
// Package diagnostics formats compiler errors and prints them in a consistent
// way.
package diagnostics
import (
"bytes"
"fmt"
"go/scanner"
"go/token"
"go/types"
"io"
"path/filepath"
"sort"
"strings"
"github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/loader"
)
// A single diagnostic.
type Diagnostic struct {
Pos token.Position
Msg string
}
// One or multiple errors of a particular package.
// It can also represent whole-program errors (like linker errors) that can't
// easily be connected to a single package.
type PackageDiagnostic struct {
ImportPath string // the same ImportPath as in `go list -json`
Diagnostics []Diagnostic
}
// Diagnostics of a whole program. This can include errors belonging to multiple
// packages, or just a single package.
type ProgramDiagnostic []PackageDiagnostic
// CreateDiagnostics reads the underlying errors in the error object and creates
// a set of diagnostics that's sorted and can be readily printed.
func CreateDiagnostics(err error) ProgramDiagnostic {
if err == nil {
return nil
}
// Right now, the compiler will only show errors for the first pacakge that
// fails to build. This is likely to change in the future.
return ProgramDiagnostic{
createPackageDiagnostic(err),
}
}
// Create diagnostics for a single package (though, in practice, it may also be
// used for whole-program diagnostics in some cases).
func createPackageDiagnostic(err error) PackageDiagnostic {
// Extract diagnostics for this package.
var pkgDiag PackageDiagnostic
switch err := err.(type) {
case *builder.MultiError:
if err.ImportPath != "" {
pkgDiag.ImportPath = err.ImportPath
}
for _, err := range err.Errs {
diags := createDiagnostics(err)
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, diags...)
}
case loader.Errors:
if err.Pkg != nil {
pkgDiag.ImportPath = err.Pkg.ImportPath
}
for _, err := range err.Errs {
diags := createDiagnostics(err)
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, diags...)
}
case *interp.Error:
pkgDiag.ImportPath = err.ImportPath
w := &bytes.Buffer{}
fmt.Fprintln(w, err.Error())
if len(err.Inst) != 0 {
fmt.Fprintln(w, err.Inst)
}
if len(err.Traceback) > 0 {
fmt.Fprintln(w, "\ntraceback:")
for _, line := range err.Traceback {
fmt.Fprintln(w, line.Pos.String()+":")
fmt.Fprintln(w, line.Inst)
}
}
pkgDiag.Diagnostics = append(pkgDiag.Diagnostics, Diagnostic{
Msg: w.String(),
})
default:
pkgDiag.Diagnostics = createDiagnostics(err)
}
// Sort these diagnostics by file/line/column.
sort.SliceStable(pkgDiag.Diagnostics, func(i, j int) bool {
posI := pkgDiag.Diagnostics[i].Pos
posJ := pkgDiag.Diagnostics[j].Pos
if posI.Filename != posJ.Filename {
return posI.Filename < posJ.Filename
}
if posI.Line != posJ.Line {
return posI.Line < posJ.Line
}
return posI.Column < posJ.Column
})
return pkgDiag
}
// Extract diagnostics from the given error message and return them as a slice
// of errors (which in many cases will just be a single diagnostic).
func createDiagnostics(err error) []Diagnostic {
switch err := err.(type) {
case types.Error:
return []Diagnostic{
{
Pos: err.Fset.Position(err.Pos),
Msg: err.Msg,
},
}
case scanner.Error:
return []Diagnostic{
{
Pos: err.Pos,
Msg: err.Msg,
},
}
case scanner.ErrorList:
var diags []Diagnostic
for _, err := range err {
diags = append(diags, createDiagnostics(*err)...)
}
return diags
case loader.Error:
if err.Err.Pos.Filename != "" {
// Probably a syntax error in a dependency.
return createDiagnostics(err.Err)
} else {
// Probably an "import cycle not allowed" error.
buf := &bytes.Buffer{}
fmt.Fprintln(buf, "package", err.ImportStack[0])
for i := 1; i < len(err.ImportStack); i++ {
pkgPath := err.ImportStack[i]
if i == len(err.ImportStack)-1 {
// last package
fmt.Fprintln(buf, "\timports", pkgPath+": "+err.Err.Error())
} else {
// not the last pacakge
fmt.Fprintln(buf, "\timports", pkgPath)
}
}
return []Diagnostic{
{Msg: buf.String()},
}
}
default:
return []Diagnostic{
{Msg: err.Error()},
}
}
}
// Write program diagnostics to the given writer with 'wd' as the relative
// working directory.
func (progDiag ProgramDiagnostic) WriteTo(w io.Writer, wd string) {
for _, pkgDiag := range progDiag {
pkgDiag.WriteTo(w, wd)
}
}
// Write package diagnostics to the given writer with 'wd' as the relative
// working directory.
func (pkgDiag PackageDiagnostic) WriteTo(w io.Writer, wd string) {
if pkgDiag.ImportPath != "" {
fmt.Fprintln(w, "#", pkgDiag.ImportPath)
}
for _, diag := range pkgDiag.Diagnostics {
diag.WriteTo(w, wd)
}
}
// Write this diagnostic to the given writer with 'wd' as the relative working
// directory.
func (diag Diagnostic) WriteTo(w io.Writer, wd string) {
if diag.Pos == (token.Position{}) {
fmt.Fprintln(w, diag.Msg)
return
}
pos := diag.Pos // make a copy
if !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
// This file is not from the standard library (either the GOROOT or the
// TINYGOROOT). Make the path relative, for easier reading. Ignore any
// errors in the process (falling back to the absolute path).
pos.Filename = tryToMakePathRelative(pos.Filename, wd)
}
fmt.Fprintf(w, "%s: %s\n", pos, diag.Msg)
}
// try to make the path relative to the current working directory. If any error
// occurs, this error is ignored and the absolute path is returned instead.
func tryToMakePathRelative(dir, wd string) string {
if wd == "" {
return dir // working directory not found
}
relpath, err := filepath.Rel(wd, dir)
if err != nil {
return dir
}
return relpath
}
+261
View File
@@ -0,0 +1,261 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"fmt"
"sort"
"strings"
)
// A pair is a pair of values tracked for both the x and y side of a diff.
// It is typically a pair of line indexes.
type pair struct{ x, y int }
// Diff returns an anchored diff of the two texts old and new
// in the “unified diff” format. If old and new are identical,
// Diff returns a nil slice (no output).
//
// Unix diff implementations typically look for a diff with
// the smallest number of lines inserted and removed,
// which can in the worst case take time quadratic in the
// number of lines in the texts. As a result, many implementations
// either can be made to run for a long time or cut off the search
// after a predetermined amount of work.
//
// In contrast, this implementation looks for a diff with the
// smallest number of “unique” lines inserted and removed,
// where unique means a line that appears just once in both old and new.
// We call this an “anchored diff” because the unique lines anchor
// the chosen matching regions. An anchored diff is usually clearer
// than a standard diff, because the algorithm does not try to
// reuse unrelated blank lines or closing braces.
// The algorithm also guarantees to run in O(n log n) time
// instead of the standard O(n²) time.
//
// Some systems call this approach a “patience diff,” named for
// the “patience sorting” algorithm, itself named for a solitaire card game.
// We avoid that name for two reasons. First, the name has been used
// for a few different variants of the algorithm, so it is imprecise.
// Second, the name is frequently interpreted as meaning that you have
// to wait longer (to be patient) for the diff, meaning that it is a slower algorithm,
// when in fact the algorithm is faster than the standard one.
func Diff(oldName string, old []byte, newName string, new []byte) []byte {
if bytes.Equal(old, new) {
return nil
}
x := lines(old)
y := lines(new)
// Print diff header.
var out bytes.Buffer
fmt.Fprintf(&out, "diff %s %s\n", oldName, newName)
fmt.Fprintf(&out, "--- %s\n", oldName)
fmt.Fprintf(&out, "+++ %s\n", newName)
// Loop over matches to consider,
// expanding each match to include surrounding lines,
// and then printing diff chunks.
// To avoid setup/teardown cases outside the loop,
// tgs returns a leading {0,0} and trailing {len(x), len(y)} pair
// in the sequence of matches.
var (
done pair // printed up to x[:done.x] and y[:done.y]
chunk pair // start lines of current chunk
count pair // number of lines from each side in current chunk
ctext []string // lines for current chunk
)
for _, m := range tgs(x, y) {
if m.x < done.x {
// Already handled scanning forward from earlier match.
continue
}
// Expand matching lines as far as possible,
// establishing that x[start.x:end.x] == y[start.y:end.y].
// Note that on the first (or last) iteration we may (or definitely do)
// have an empty match: start.x==end.x and start.y==end.y.
start := m
for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] {
start.x--
start.y--
}
end := m
for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] {
end.x++
end.y++
}
// Emit the mismatched lines before start into this chunk.
// (No effect on first sentinel iteration, when start = {0,0}.)
for _, s := range x[done.x:start.x] {
ctext = append(ctext, "-"+s)
count.x++
}
for _, s := range y[done.y:start.y] {
ctext = append(ctext, "+"+s)
count.y++
}
// If we're not at EOF and have too few common lines,
// the chunk includes all the common lines and continues.
const C = 3 // number of context lines
if (end.x < len(x) || end.y < len(y)) &&
(end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) {
for _, s := range x[start.x:end.x] {
ctext = append(ctext, " "+s)
count.x++
count.y++
}
done = end
continue
}
// End chunk with common lines for context.
if len(ctext) > 0 {
n := end.x - start.x
if n > C {
n = C
}
for _, s := range x[start.x : start.x+n] {
ctext = append(ctext, " "+s)
count.x++
count.y++
}
done = pair{start.x + n, start.y + n}
// Format and emit chunk.
// Convert line numbers to 1-indexed.
// Special case: empty file shows up as 0,0 not 1,0.
if count.x > 0 {
chunk.x++
}
if count.y > 0 {
chunk.y++
}
fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", chunk.x, count.x, chunk.y, count.y)
for _, s := range ctext {
out.WriteString(s)
}
count.x = 0
count.y = 0
ctext = ctext[:0]
}
// If we reached EOF, we're done.
if end.x >= len(x) && end.y >= len(y) {
break
}
// Otherwise start a new chunk.
chunk = pair{end.x - C, end.y - C}
for _, s := range x[chunk.x:end.x] {
ctext = append(ctext, " "+s)
count.x++
count.y++
}
done = end
}
return out.Bytes()
}
// lines returns the lines in the file x, including newlines.
// If the file does not end in a newline, one is supplied
// along with a warning about the missing newline.
func lines(x []byte) []string {
l := strings.SplitAfter(string(x), "\n")
if l[len(l)-1] == "" {
l = l[:len(l)-1]
} else {
// Treat last line as having a message about the missing newline attached,
// using the same text as BSD/GNU diff (including the leading backslash).
l[len(l)-1] += "\n\\ No newline at end of file\n"
}
return l
}
// tgs returns the pairs of indexes of the longest common subsequence
// of unique lines in x and y, where a unique line is one that appears
// once in x and once in y.
//
// The longest common subsequence algorithm is as described in
// Thomas G. Szymanski, “A Special Case of the Maximal Common
// Subsequence Problem,” Princeton TR #170 (January 1975),
// available at https://research.swtch.com/tgs170.pdf.
func tgs(x, y []string) []pair {
// Count the number of times each string appears in a and b.
// We only care about 0, 1, many, counted as 0, -1, -2
// for the x side and 0, -4, -8 for the y side.
// Using negative numbers now lets us distinguish positive line numbers later.
m := make(map[string]int)
for _, s := range x {
if c := m[s]; c > -2 {
m[s] = c - 1
}
}
for _, s := range y {
if c := m[s]; c > -8 {
m[s] = c - 4
}
}
// Now unique strings can be identified by m[s] = -1+-4.
//
// Gather the indexes of those strings in x and y, building:
// xi[i] = increasing indexes of unique strings in x.
// yi[i] = increasing indexes of unique strings in y.
// inv[i] = index j such that x[xi[i]] = y[yi[j]].
var xi, yi, inv []int
for i, s := range y {
if m[s] == -1+-4 {
m[s] = len(yi)
yi = append(yi, i)
}
}
for i, s := range x {
if j, ok := m[s]; ok && j >= 0 {
xi = append(xi, i)
inv = append(inv, j)
}
}
// Apply Algorithm A from Szymanski's paper.
// In those terms, A = J = inv and B = [0, n).
// We add sentinel pairs {0,0}, and {len(x),len(y)}
// to the returned sequence, to help the processing loop.
J := inv
n := len(xi)
T := make([]int, n)
L := make([]int, n)
for i := range T {
T[i] = n + 1
}
for i := 0; i < n; i++ {
k := sort.Search(n, func(k int) bool {
return T[k] >= J[i]
})
T[k] = J[i]
L[i] = k + 1
}
k := 0
for _, v := range L {
if k < v {
k = v
}
}
seq := make([]pair, 2+k)
seq[1+k] = pair{len(x), len(y)} // sentinel at end
lastj := n
for i := n - 1; i >= 0; i-- {
if L[i] == k && J[i] < lastj {
seq[k] = pair{xi[i], yi[J[i]]}
k--
}
}
seq[0] = pair{0, 0} // sentinel at start
return seq
}
+121
View File
@@ -0,0 +1,121 @@
package main
import (
"bytes"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/diagnostics"
)
// Test the error messages of the TinyGo compiler.
func TestErrors(t *testing.T) {
for _, name := range []string{
"cgo",
"compiler",
"interp",
"loader-importcycle",
"loader-invaliddep",
"loader-invalidpackage",
"loader-nopackage",
"optimizer",
"syntax",
"types",
} {
t.Run(name, func(t *testing.T) {
testErrorMessages(t, "./testdata/errors/"+name+".go")
})
}
}
func testErrorMessages(t *testing.T, filename string) {
// Parse expected error messages.
expected := readErrorMessages(t, filename)
// Try to build a binary (this should fail with an error).
tmpdir := t.TempDir()
err := Build(filename, tmpdir+"/out", &compileopts.Options{
Target: "wasip1",
Semaphore: sema,
InterpTimeout: 180 * time.Second,
Debug: true,
VerifyIR: true,
Opt: "z",
})
if err == nil {
t.Fatal("expected to get a compiler error")
}
// Get the full ./testdata/errors directory.
wd, absErr := filepath.Abs("testdata/errors")
if absErr != nil {
t.Fatal(absErr)
}
// Write error message out as plain text.
var buf bytes.Buffer
diagnostics.CreateDiagnostics(err).WriteTo(&buf, wd)
actual := strings.TrimRight(buf.String(), "\n")
// Check whether the error is as expected.
if !matchErrors(t, expected, actual) {
t.Errorf("expected error:\n%s\ngot:\n%s", indentText(expected, "> "), indentText(actual, "> "))
}
}
func matchErrors(t *testing.T, pattern, actual string) bool {
patternLines := strings.Split(pattern, "\n")
actualLines := strings.Split(actual, "\n")
if len(patternLines) != len(actualLines) {
return false
}
for i, patternLine := range patternLines {
indices := regexp.MustCompile(`\{\{.*?\}\}`).FindAllStringIndex(patternLine, -1)
patternParts := []string{"^"}
lastStop := 0
for _, startstop := range indices {
start := startstop[0]
stop := startstop[1]
patternParts = append(patternParts,
regexp.QuoteMeta(patternLine[lastStop:start]),
patternLine[start+2:stop-2])
lastStop = stop
}
patternParts = append(patternParts, regexp.QuoteMeta(patternLine[lastStop:]), "$")
pattern := strings.Join(patternParts, "")
re, err := regexp.Compile(pattern)
if err != nil {
t.Fatalf("could not compile regexp for %#v: %v", patternLine, err)
}
if !re.MatchString(actualLines[i]) {
return false
}
}
return true
}
// Indent the given text with a given indentation string.
func indentText(text, indent string) string {
return indent + strings.ReplaceAll(text, "\n", "\n"+indent)
}
// Read "// ERROR:" prefixed messages from the given file.
func readErrorMessages(t *testing.T, file string) string {
data, err := os.ReadFile(file)
if err != nil {
t.Fatal("could not read input file:", err)
}
var errors []string
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "// ERROR: ") {
errors = append(errors, strings.TrimRight(line[len("// ERROR: "):], "\r\n"))
}
}
return strings.Join(errors, "\n")
}
+1 -1
View File
@@ -21,7 +21,7 @@ require (
golang.org/x/sys v0.21.0
golang.org/x/tools v0.22.1-0.20240621165957-db513b091504
gopkg.in/yaml.v2 v2.4.0
tinygo.org/x/go-llvm v0.0.0-20240518103902-697964f2a9dc
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8
)
require (
+2 -2
View File
@@ -106,5 +106,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
tinygo.org/x/go-llvm v0.0.0-20240518103902-697964f2a9dc h1:TCzibFa4oLu+njEP3fnRUmZ+QQeb8BjtOwctgcjzL0k=
tinygo.org/x/go-llvm v0.0.0-20240518103902-697964f2a9dc/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8 h1:bLsZXRUBavt++CJlMN7sppNziqu3LyamESLhFJcpqFQ=
tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+5
View File
@@ -159,6 +159,11 @@ func Get(name string) string {
}
return findWasmOpt()
case "WASMTOOLS":
if path := os.Getenv("WASMTOOLS"); path != "" {
return path
}
return "wasm-tools"
default:
return ""
}
+5 -2
View File
@@ -1,10 +1,13 @@
//go:build tools
// Install linter versions specified in go.mod
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idom
// Install tools specified in go.mod.
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idiom.
package tools
import (
_ "github.com/client9/misspell"
_ "github.com/mgechev/revive"
)
//go:generate go install github.com/client9/misspell/cmd/misspell
//go:generate go install github.com/mgechev/revive
+5
View File
@@ -0,0 +1,5 @@
# wasm-tools directory
This directory has a separate `go.mod` file because the `wasm-tools-go` module requires Go 1.22, while TinyGo itself supports Go 1.19.
When the minimum Go version for TinyGo is 1.22, this directory can be folded into `internal/tools` and the `go.mod` and `go.sum` files deleted.
+12
View File
@@ -0,0 +1,12 @@
module github.com/tinygo-org/tinygo/internal/tools
go 1.22.4
require github.com/ydnar/wasm-tools-go v0.1.4
require (
github.com/coreos/go-semver v0.3.1 // indirect
github.com/urfave/cli/v3 v3.0.0-alpha9 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.19.0 // indirect
)
+25
View File
@@ -0,0 +1,25 @@
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v3 v3.0.0-alpha9 h1:P0RMy5fQm1AslQS+XCmy9UknDXctOmG/q/FZkUFnJSo=
github.com/urfave/cli/v3 v3.0.0-alpha9/go.mod h1:0kK/RUFHyh+yIKSfWxwheGndfnrvYSmYFVeKCh03ZUc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/ydnar/wasm-tools-go v0.1.4 h1:+25WqBj0AhLx8OFvZvrs7bQO6L3WtQ7t6JzQEYsXQb8=
github.com/ydnar/wasm-tools-go v0.1.4/go.mod h1:lQfv2Tde3tRgZDSYriro0EmdSHzP1mrHPMmYNahSS/g=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+11
View File
@@ -0,0 +1,11 @@
//go:build tools
// Install tools specified in go.mod.
// See https://marcofranssen.nl/manage-go-tools-via-go-modules for idiom.
package tools
import (
_ "github.com/ydnar/wasm-tools-go/cmd/wit-bindgen-go"
)
//go:generate go install github.com/ydnar/wasm-tools-go/cmd/wit-bindgen-go
+4 -58
View File
@@ -417,64 +417,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
} else {
locals[inst.localIndex] = literalValue{uint8(0)}
}
case strings.HasSuffix(callFn.name, ".$typeassert"):
if r.debug {
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
}
// Load various values for the interface implements check below.
typecodePtr, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
// typecodePtr always point to the numMethod field in the type
// description struct. The methodSet, when present, comes right
// before the numMethod field (the compiler doesn't generate
// method sets for concrete types without methods).
// Considering that the compiler doesn't emit interface type
// asserts for interfaces with no methods (as the always succeed)
// then if the offset is zero, this assert must always fail.
if typecodePtr.offset() == 0 {
locals[inst.localIndex] = literalValue{uint8(0)}
break
}
typecodePtrOffset, err := typecodePtr.addOffset(-int64(r.pointerSize))
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
methodSetPtr, err := mem.load(typecodePtrOffset, r.pointerSize).asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
llvmFn := inst.llvmInst.CalledValue()
methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
methodSetString := methodSetAttr.GetStringValue()
// Make a set of all the methods on the concrete type, for
// easier checking in the next step.
concreteTypeMethods := map[string]struct{}{}
for i := 0; i < numMethods; i++ {
methodInfo := r.builder.CreateExtractValue(methodSet, 1, "")
name := r.builder.CreateExtractValue(methodInfo, i, "").Name()
concreteTypeMethods[name] = struct{}{}
}
// Check whether all interface methods are also in the list
// of defined methods calculated above. This is the interface
// assert itself.
assertOk := uint8(1) // i1 true
for _, name := range strings.Split(methodSetString, "; ") {
if _, ok := concreteTypeMethods[name]; !ok {
// There is a method on the interface that is not
// implemented by the type. The assertion will fail.
assertOk = 0 // i1 false
break
}
}
// If assertOk is still 1, the assertion succeeded.
locals[inst.localIndex] = literalValue{assertOk}
case callFn.name == "__tinygo_interp_raise_test_error":
// Special function that will trigger an error.
// This is used to test error reporting.
return nil, mem, r.errorAt(inst, errors.New("test error"))
case strings.HasSuffix(callFn.name, "$invoke"):
// This thunk is the interface method dispatcher: it is called
// with all regular parameters and a type code. It will then
Submodule
+1
Submodule lib/wasi-cli added at 6ae8261709
+2
View File
@@ -238,9 +238,11 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
"internal/": true,
"internal/binary/": false,
"internal/bytealg/": false,
"internal/cm/": false,
"internal/fuzz/": false,
"internal/reflectlite/": false,
"internal/task/": false,
"internal/wasi/": false,
"machine/": false,
"net/": true,
"net/http/": false,
+18 -3
View File
@@ -128,7 +128,7 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
}
// List the dependencies of this package, in raw JSON format.
extraArgs := []string{"-json", "-deps"}
extraArgs := []string{"-json", "-deps", "-e"}
if config.TestConfig.CompileTestBinary {
extraArgs = append(extraArgs, "-test")
}
@@ -149,6 +149,7 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
// Parse the returned json from `go list`.
decoder := json.NewDecoder(buf)
var pkgErrors []error
for {
pkg := &Package{
program: p,
@@ -188,6 +189,12 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
pos.Filename = strings.Join(fields[:len(fields)-1], ":")
pos.Line, _ = strconv.Atoi(fields[len(fields)-1])
}
if abs, err := filepath.Abs(pos.Filename); err == nil {
// Make the path absolute, so that error messages will be
// prettier (it will be turned back into a relative path
// when printing the error).
pos.Filename = abs
}
pos.Filename = p.getOriginalPath(pos.Filename)
}
err := scanner.Error{
@@ -195,10 +202,11 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
Msg: pkg.Error.Err,
}
if len(pkg.Error.ImportStack) != 0 {
return nil, Error{
pkgErrors = append(pkgErrors, Error{
ImportStack: pkg.Error.ImportStack,
Err: err,
}
})
continue
}
return nil, err
}
@@ -241,6 +249,13 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
p.Packages[pkg.ImportPath] = pkg
}
if len(pkgErrors) != 0 {
// TODO: use errors.Join in Go 1.20.
return nil, Errors{
Errs: pkgErrors,
}
}
if config.TestConfig.CompileTestBinary && !strings.HasSuffix(p.sorted[len(p.sorted)-1].ImportPath, ".test") {
// Trying to compile a test binary but there are no test files in this
// package.
+79 -93
View File
@@ -8,8 +8,6 @@ import (
"errors"
"flag"
"fmt"
"go/scanner"
"go/types"
"io"
"os"
"os/exec"
@@ -30,8 +28,8 @@ import (
"github.com/mattn/go-colorable"
"github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/diagnostics"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/loader"
"golang.org/x/tools/go/buildutil"
"tinygo.org/x/go-llvm"
@@ -306,16 +304,25 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
// reads any files.
//
// Ex. run --dir=.. --dir=../.. --dir=../../..
dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
var dirs []string
switch config.Options.Target {
case "wasip1":
dirs = dirsToModuleRootRel(result.MainDir, result.ModuleRoot)
case "wasip2":
dirs = dirsToModuleRootAbs(result.MainDir, result.ModuleRoot)
default:
return fmt.Errorf("unknown GOOS target: %v", config.Options.Target)
}
args := []string{"run"}
for _, d := range dirs[1:] {
for _, d := range dirs {
args = append(args, "--dir="+d)
}
// The below re-organizes the arguments so that the current
// directory is added last.
args = append(args, "--env=PWD="+cmd.Dir)
args = append(args, cmd.Args[1:]...)
cmd.Args = append(cmd.Args[:1:1], args...)
cmd.Args = args
}
// Run the test.
@@ -338,6 +345,11 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
}
return err
})
if testConfig.CompileOnly {
return true, nil
}
importPath := strings.TrimSuffix(result.ImportPath, ".test")
var w io.Writer = stdout
@@ -348,7 +360,7 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
fmt.Fprintf(w, "? \t%s\t[no test files]\n", err.ImportPath)
// Pretend the test passed - it at least didn't fail.
return true, nil
} else if passed && !testConfig.CompileOnly {
} else if passed {
fmt.Fprintf(w, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
} else {
fmt.Fprintf(w, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
@@ -356,8 +368,8 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
return passed, err
}
func dirsToModuleRoot(maindir, modroot string) []string {
var dirs = []string{"."}
func dirsToModuleRootRel(maindir, modroot string) []string {
var dirs []string
last := ".."
// strip off path elements until we hit the module root
// adding `..`, `../..`, `../../..` until we're done
@@ -366,6 +378,20 @@ func dirsToModuleRoot(maindir, modroot string) []string {
last = filepath.Join(last, "..")
maindir = filepath.Dir(maindir)
}
dirs = append(dirs, ".")
return dirs
}
func dirsToModuleRootAbs(maindir, modroot string) []string {
var dirs = []string{maindir}
last := filepath.Join(maindir, "..")
// strip off path elements until we hit the module root
// adding `..`, `../..`, `../../..` until we're done
for maindir != modroot {
dirs = append(dirs, last)
last = filepath.Join(last, "..")
maindir = filepath.Dir(maindir)
}
return dirs
}
@@ -785,6 +811,9 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
// passes command line arguments and evironment variables in a way appropriate
// for the given emulator.
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) (builder.BuildResult, error) {
isSingleFile := strings.HasSuffix(pkgName, ".go")
// Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment,
@@ -818,9 +847,6 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
}
}
} else if config.EmulatorName() == "wasmtime" {
// Wasmtime needs some special flags to pass environment variables
// and allow reading from the current directory.
emuArgs = append(emuArgs, "--dir=.")
for _, v := range environmentVars {
emuArgs = append(emuArgs, "--env", v)
}
@@ -876,7 +902,26 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
if err != nil {
return result, err
}
name = emulator[0]
if name == "wasmtime" {
// Wasmtime needs some special flags to pass environment variables
// and allow reading from the current directory.
switch config.Options.Target {
case "wasip1":
emuArgs = append(emuArgs, "--dir=.")
case "wasip2":
dir := result.MainDir
if isSingleFile {
cwd, _ := os.Getwd()
dir = cwd
}
emuArgs = append(emuArgs, "--dir="+dir)
emuArgs = append(emuArgs, "--env=PWD="+dir)
}
}
emuArgs = append(emuArgs, emulator[1:]...)
args = append(emuArgs, args...)
}
@@ -1030,7 +1075,8 @@ func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
continue
}
fstype := fields[2]
if fstype != "vfat" {
// chromeos bind mounts use 9p
if !(fstype == "vfat" || fstype == "9p") {
continue
}
fspath := strings.ReplaceAll(fields[1], "\\040", " ")
@@ -1244,83 +1290,13 @@ func usage(command string) {
}
}
// try to make the path relative to the current working directory. If any error
// occurs, this error is ignored and the absolute path is returned instead.
func tryToMakePathRelative(dir string) string {
wd, err := os.Getwd()
if err != nil {
return dir
}
relpath, err := filepath.Rel(wd, dir)
if err != nil {
return dir
}
return relpath
}
// printCompilerError prints compiler errors using the provided logger function
// (similar to fmt.Println).
//
// There is one exception: interp errors may print to stderr unconditionally due
// to limitations in the LLVM bindings.
func printCompilerError(logln func(...interface{}), err error) {
switch err := err.(type) {
case types.Error:
printCompilerError(logln, scanner.Error{
Pos: err.Fset.Position(err.Pos),
Msg: err.Msg,
})
case scanner.Error:
if !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
// This file is not from the standard library (either the GOROOT or
// the TINYGOROOT). Make the path relative, for easier reading.
// Ignore any errors in the process (falling back to the absolute
// path).
err.Pos.Filename = tryToMakePathRelative(err.Pos.Filename)
}
logln(err)
case scanner.ErrorList:
for _, scannerErr := range err {
printCompilerError(logln, *scannerErr)
}
case *interp.Error:
logln("#", err.ImportPath)
logln(err.Error())
if len(err.Inst) != 0 {
logln(err.Inst)
}
if len(err.Traceback) > 0 {
logln("\ntraceback:")
for _, line := range err.Traceback {
logln(line.Pos.String() + ":")
logln(line.Inst)
}
}
case loader.Errors:
logln("#", err.Pkg.ImportPath)
for _, err := range err.Errs {
printCompilerError(logln, err)
}
case loader.Error:
logln(err.Err.Error())
logln("package", err.ImportStack[0])
for _, pkgPath := range err.ImportStack[1:] {
logln("\timports", pkgPath)
}
case *builder.MultiError:
for _, err := range err.Errs {
printCompilerError(logln, err)
}
default:
logln("error:", err)
}
}
func handleCompilerError(err error) {
if err != nil {
printCompilerError(func(args ...interface{}) {
fmt.Fprintln(os.Stderr, args...)
}, err)
wd, getwdErr := os.Getwd()
if getwdErr != nil {
wd = ""
}
diagnostics.CreateDiagnostics(err).WriteTo(os.Stderr, wd)
os.Exit(1)
}
}
@@ -1465,6 +1441,12 @@ func main() {
flag.StringVar(&outpath, "o", "", "output filename")
}
var witPackage, witWorld string
if command == "help" || command == "build" || command == "test" || command == "run" {
flag.StringVar(&witPackage, "wit-package", "", "wit package for wasm component embedding")
flag.StringVar(&witWorld, "wit-world", "", "wit world for wasm component embedding")
}
var testConfig compileopts.TestConfig
if command == "help" || command == "test" {
flag.BoolVar(&testConfig.CompileOnly, "c", false, "compile the test binary but do not run it")
@@ -1544,6 +1526,8 @@ func main() {
Monitor: *monitor,
BaudRate: *baudrate,
Timeout: *timeout,
WITPackage: witPackage,
WITWorld: witWorld,
}
if *printCommands {
options.PrintCommands = printCommand
@@ -1714,9 +1698,11 @@ func main() {
stderr := (*testStderr)(buf)
passed, err := Test(pkgName, stdout, stderr, options, outpath)
if err != nil {
printCompilerError(func(args ...interface{}) {
fmt.Fprintln(stderr, args...)
}, err)
wd, getwdErr := os.Getwd()
if getwdErr != nil {
wd = ""
}
diagnostics.CreateDiagnostics(err).WriteTo(os.Stderr, wd)
}
if !passed {
select {
+30 -1
View File
@@ -24,6 +24,7 @@ import (
"github.com/aykevl/go-wasm"
"github.com/tinygo-org/tinygo/builder"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/diagnostics"
"github.com/tinygo-org/tinygo/goenv"
)
@@ -36,6 +37,7 @@ var supportedLinuxArches = map[string]string{
"X86Linux": "linux/386",
"ARMLinux": "linux/arm/6",
"ARM64Linux": "linux/arm64",
"MIPSLinux": "linux/mipsle",
"WASIp1": "wasip1/wasm",
}
@@ -181,6 +183,10 @@ func TestBuild(t *testing.T) {
t.Parallel()
runPlatTests(optionsFromTarget("wasip1", sema), tests, t)
})
t.Run("WASIp2", func(t *testing.T) {
t.Parallel()
runPlatTests(optionsFromTarget("wasip2", sema), tests, t)
})
}
}
@@ -206,6 +212,12 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
continue
}
}
if options.GOOS == "linux" && (options.GOARCH == "mips" || options.GOARCH == "mipsle") {
if name == "atomic.go" {
// 64-bit atomic operations aren't currently supported on MIPS.
continue
}
}
if options.Target == "simavr" {
// Not all tests are currently supported on AVR.
// Skip the ones that aren't.
@@ -235,6 +247,14 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
default:
}
}
if options.Target == "wasip2" {
switch name {
case "cgo/":
// waisp2 use our own libc; cgo tests fail
continue
}
}
name := name // redefine to avoid race condition
t.Run(name, func(t *testing.T) {
t.Parallel()
@@ -297,6 +317,10 @@ func emuCheck(t *testing.T, options compileopts.Options) {
}
func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
separators := strings.Count(target, "/")
if (separators == 1 || separators == 2) && !strings.HasSuffix(target, ".json") {
return optionsFromOSARCH(target, sema)
}
return compileopts.Options{
// GOOS/GOARCH are only used if target == ""
GOOS: goenv.Get("GOOS"),
@@ -364,7 +388,11 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
return cmd.Run()
})
if err != nil {
printCompilerError(t.Log, err)
w := &bytes.Buffer{}
diagnostics.CreateDiagnostics(err).WriteTo(w, "")
for _, line := range strings.Split(strings.TrimRight(w.String(), "\n"), "\n") {
t.Log(line)
}
t.Fail()
return
}
@@ -393,6 +421,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
fail = true
} else if !bytes.Equal(expected, actual) {
t.Logf("output did not match (expected %d bytes, got %d bytes):", len(expected), len(actual))
t.Logf(string(Diff("expected", expected, "actual", actual)))
fail = true
}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build darwin || tinygo.wasm
//go:build darwin || wasip1 || wasip2 || wasm
// This implementation of crypto/rand uses the arc4random_buf function
// (available on both MacOS and WASI) to generate random numbers.
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build linux && !baremetal && !wasip1
//go:build linux && !baremetal && !wasip1 && !wasip2
// This implementation of crypto/rand uses the /dev/urandom pseudo-file to
// generate random numbers.
+13
View File
@@ -10,6 +10,7 @@ import (
"context"
"crypto"
"crypto/x509"
"fmt"
"io"
"net"
"sync"
@@ -23,9 +24,21 @@ import (
// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7.
type CurveID uint16
// CipherSuiteName returns the standard name for the passed cipher suite ID
//
// Not Implemented.
func CipherSuiteName(id uint16) string {
return fmt.Sprintf("0x%04X", id)
}
// ConnectionState records basic TLS details about the connection.
type ConnectionState struct {
// TINYGO: empty; TLS connection offloaded to device
//
// Minimum (empty) fields for fortio.org/log http logging and others
// to compile and run.
PeerCertificates []*x509.Certificate
CipherSuite uint16
}
// ClientAuthType declares the policy the server will follow for
-5
View File
@@ -1,6 +1,3 @@
// We don't support RV32E at the moment.
#if !defined(__riscv_32e)
#ifdef __riscv_flen
#define NREG 48
#define LFREG flw
@@ -131,5 +128,3 @@ handleInterruptASM:
LREG ra, 0*REGSIZE(sp)
addi sp, sp, NREG*REGSIZE
mret
#endif
+4
View File
@@ -3,6 +3,10 @@
// tinygo build -size short -o hello-unknown.wasm -target wasm-unknown -gc=leaking -no-debug ./src/examples/hello-wasm-unknown/
package main
// Smoke test: make sure the fmt package can be imported (even if it isn't
// really useful for wasm-unknown).
import _ "os"
var x int32
//go:wasmimport hosted echo_i32
+118
View File
@@ -0,0 +1,118 @@
package cm
import "unsafe"
// Reinterpret reinterprets the bits of type From into type T.
// Will panic if the size of From is smaller than the size of To.
func Reinterpret[T, From any](from From) (to T) {
if unsafe.Sizeof(to) > unsafe.Sizeof(from) {
panic("reinterpret: size of to > from")
}
return *(*T)(unsafe.Pointer(&from))
}
// LowerString lowers a [string] into a pair of Core WebAssembly types.
//
// [string]: https://pkg.go.dev/builtin#string
func LowerString[S ~string](s S) (*byte, uint32) {
return unsafe.StringData(string(s)), uint32(len(s))
}
// LiftString lifts Core WebAssembly types into a [string].
func LiftString[T ~string, Data unsafe.Pointer | uintptr | *uint8, Len uint | uintptr | uint32 | uint64](data Data, len Len) T {
return T(unsafe.String((*uint8)(unsafe.Pointer(data)), int(len)))
}
// LowerList lowers a [List] into a pair of Core WebAssembly types.
func LowerList[L ~struct{ list[T] }, T any](list L) (*T, uint32) {
l := (*List[T])(unsafe.Pointer(&list))
return l.data, uint32(l.len)
}
// LiftList lifts Core WebAssembly types into a [List].
func LiftList[L List[T], T any, Data unsafe.Pointer | uintptr | *T, Len uint | uintptr | uint32 | uint64](data Data, len Len) L {
return L(NewList((*T)(unsafe.Pointer(data)), uint(len)))
}
// BoolToU32 converts a value whose underlying type is [bool] into a [uint32].
// Used to lower a [bool] into a Core WebAssembly i32 as specified in the [Canonical ABI].
//
// [bool]: https://pkg.go.dev/builtin#bool
// [uint32]: https://pkg.go.dev/builtin#uint32
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func BoolToU32[B ~bool](v B) uint32 { return uint32(*(*uint8)(unsafe.Pointer(&v))) }
// U32ToBool converts a [uint32] into a [bool].
// Used to lift a Core WebAssembly i32 into a [bool] as specified in the [Canonical ABI].
//
// [uint32]: https://pkg.go.dev/builtin#uint32
// [bool]: https://pkg.go.dev/builtin#bool
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func U32ToBool(v uint32) bool { tmp := uint8(v); return *(*bool)(unsafe.Pointer(&tmp)) }
// F32ToU32 maps the bits of a [float32] into a [uint32].
// Used to lower a [float32] into a Core WebAssembly i32 as specified in the [Canonical ABI].
//
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
// [float32]: https://pkg.go.dev/builtin#float32
// [uint32]: https://pkg.go.dev/builtin#uint32
func F32ToU32(v float32) uint32 { return *(*uint32)(unsafe.Pointer(&v)) }
// U32ToF32 maps the bits of a [uint32] into a [float32].
// Used to lift a Core WebAssembly i32 into a [float32] as specified in the [Canonical ABI].
//
// [uint32]: https://pkg.go.dev/builtin#uint32
// [float32]: https://pkg.go.dev/builtin#float32
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func U32ToF32(v uint32) float32 { return *(*float32)(unsafe.Pointer(&v)) }
// F64ToU64 maps the bits of a [float64] into a [uint64].
// Used to lower a [float64] into a Core WebAssembly i64 as specified in the [Canonical ABI].
//
// [float64]: https://pkg.go.dev/builtin#float64
// [uint64]: https://pkg.go.dev/builtin#uint64
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
//
// [uint32]: https://pkg.go.dev/builtin#uint32
func F64ToU64(v float64) uint64 { return *(*uint64)(unsafe.Pointer(&v)) }
// U64ToF64 maps the bits of a [uint64] into a [float64].
// Used to lift a Core WebAssembly i64 into a [float64] as specified in the [Canonical ABI].
//
// [uint64]: https://pkg.go.dev/builtin#uint64
// [float64]: https://pkg.go.dev/builtin#float64
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func U64ToF64(v uint64) float64 { return *(*float64)(unsafe.Pointer(&v)) }
// PointerToU32 converts a pointer of type *T into a [uint32].
// Used to lower a pointer into a Core WebAssembly i32 as specified in the [Canonical ABI].
//
// [uint32]: https://pkg.go.dev/builtin#uint32
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func PointerToU32[T any](v *T) uint32 { return uint32(uintptr(unsafe.Pointer(v))) }
// U32ToPointer converts a [uint32] into a pointer of type *T.
// Used to lift a Core WebAssembly i32 into a pointer as specified in the [Canonical ABI].
//
// [uint32]: https://pkg.go.dev/builtin#uint32
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func U32ToPointer[T any](v uint32) *T { return (*T)(unsafePointer(uintptr(v))) }
// PointerToU64 converts a pointer of type *T into a [uint64].
// Used to lower a pointer into a Core WebAssembly i64 as specified in the [Canonical ABI].
//
// [uint64]: https://pkg.go.dev/builtin#uint64
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func PointerToU64[T any](v *T) uint64 { return uint64(uintptr(unsafe.Pointer(v))) }
// U64ToPointer converts a [uint64] into a pointer of type *T.
// Used to lift a Core WebAssembly i64 into a pointer as specified in the [Canonical ABI].
//
// [uint64]: https://pkg.go.dev/builtin#uint64
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
func U64ToPointer[T any](v uint64) *T { return (*T)(unsafePointer(uintptr(v))) }
// Appease vet, see https://github.com/golang/go/issues/58625
func unsafePointer(p uintptr) unsafe.Pointer {
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
}
+8
View File
@@ -0,0 +1,8 @@
// Package cm contains types and functions for interfacing with the WebAssembly Component Model.
//
// The types in this package (such as [List], [Option], [Result], and [Variant]) are designed to match the memory layout
// of [Component Model] types as specified in the [Canonical ABI].
//
// [Component Model]: https://component-model.bytecodealliance.org/introduction.html
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#alignment
package cm
+48
View File
@@ -0,0 +1,48 @@
package cm
import "unsafe"
// List represents a Component Model list.
// The binary representation of list<T> is similar to a Go slice minus the cap field.
type List[T any] struct{ list[T] }
// NewList returns a List[T] from data and len.
func NewList[T any](data *T, len uint) List[T] {
return List[T]{
list[T]{
data: data,
len: len,
},
}
}
// ToList returns a List[T] equivalent to the Go slice s.
// The underlying slice data is not copied, and the resulting List points at the
// same array storage as the slice.
func ToList[S ~[]T, T any](s S) List[T] {
return NewList[T](unsafe.SliceData([]T(s)), uint(len(s)))
}
// list represents the internal representation of a Component Model list.
// It is intended to be embedded in a [List], so embedding types maintain
// the methods defined on this type.
type list[T any] struct {
data *T
len uint
}
// Slice returns a Go slice representing the List.
func (l list[T]) Slice() []T {
return unsafe.Slice(l.data, l.len)
}
// Data returns the data pointer for the list.
func (l list[T]) Data() *T {
return l.data
}
// Len returns the length of the list.
// TODO: should this return an int instead of a uint?
func (l list[T]) Len() uint {
return l.len
}
+44
View File
@@ -0,0 +1,44 @@
package cm
// Option represents a Component Model [option<T>] type.
//
// [option<T>]: https://component-model.bytecodealliance.org/design/wit.html#options
type Option[T any] struct{ option[T] }
// None returns an [Option] representing the none case,
// equivalent to the zero value.
func None[T any]() Option[T] {
return Option[T]{}
}
// Some returns an [Option] representing the some case.
func Some[T any](v T) Option[T] {
return Option[T]{
option[T]{
isSome: true,
some: v,
},
}
}
// option represents the internal representation of a Component Model option type.
// The first byte is a bool representing none or some,
// followed by storage for the associated type T.
type option[T any] struct {
isSome bool
some T
}
// None returns true if o represents the none case.
func (o *option[T]) None() bool {
return !o.isSome
}
// Some returns a non-nil *T if o represents the some case,
// or nil if o represents the none case.
func (o *option[T]) Some() *T {
if o.isSome {
return &o.some
}
return nil
}
+21
View File
@@ -0,0 +1,21 @@
package cm
// Resource represents an opaque Component Model [resource handle].
// It is represented in the [Canonical ABI] as an 32-bit integer.
//
// [resource handle]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md#handle-types
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
type Resource uint32
// Rep represents a Component Model [resource rep], the core representation type of a resource.
// It is represented in the [Canonical ABI] as an 32-bit integer.
//
// [resource rep]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#canon-resourcerep
// [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
type Rep uint32
// ResourceNone is a sentinel value indicating a null or uninitialized resource.
// This is a reserved value specified in the [Canonical ABI runtime state].
//
// [Canonical ABI runtime state]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md#runtime-state
const ResourceNone = 0
+107
View File
@@ -0,0 +1,107 @@
package cm
import "unsafe"
const (
// ResultOK represents the OK case of a result.
ResultOK = false
// ResultErr represents the error case of a result.
ResultErr = true
)
// BoolResult represents a result with no OK or error type.
// False represents the OK case and true represents the error case.
type BoolResult bool
// Result represents a result sized to hold the Shape type.
// The size of the Shape type must be greater than or equal to the size of OK and Err types.
// For results with two zero-length types, use [BoolResult].
type Result[Shape, OK, Err any] struct{ result[Shape, OK, Err] }
// result represents the internal representation of a Component Model result type.
type result[Shape, OK, Err any] struct {
isErr bool
_ [0]OK
_ [0]Err
data Shape // [unsafe.Sizeof(*(*Shape)(unsafe.Pointer(nil)))]byte
}
// IsOK returns true if r represents the OK case.
func (r *result[Shape, OK, Err]) IsOK() bool {
r.validate()
return !r.isErr
}
// IsErr returns true if r represents the error case.
func (r *result[Shape, OK, Err]) IsErr() bool {
r.validate()
return r.isErr
}
// OK returns a non-nil *OK pointer if r represents the OK case.
// If r represents an error, then it returns nil.
func (r *result[Shape, OK, Err]) OK() *OK {
r.validate()
if r.isErr {
return nil
}
return (*OK)(unsafe.Pointer(&r.data))
}
// Err returns a non-nil *Err pointer if r represents the error case.
// If r represents the OK case, then it returns nil.
func (r *result[Shape, OK, Err]) Err() *Err {
r.validate()
if !r.isErr {
return nil
}
return (*Err)(unsafe.Pointer(&r.data))
}
// This function is sized so it can be inlined and optimized away.
func (r *result[Shape, OK, Err]) validate() {
var shape Shape
var ok OK
var err Err
// Check if size of Shape is greater than both OK and Err
if unsafe.Sizeof(shape) > unsafe.Sizeof(ok) && unsafe.Sizeof(shape) > unsafe.Sizeof(err) {
panic("result: size of data type > OK and Err types")
}
// Check if size of OK is greater than Shape
if unsafe.Sizeof(ok) > unsafe.Sizeof(shape) {
panic("result: size of OK type > data type")
}
// Check if size of Err is greater than Shape
if unsafe.Sizeof(err) > unsafe.Sizeof(shape) {
panic("result: size of Err type > data type")
}
// Check if Shape is zero-sized, but size of result != 1
if unsafe.Sizeof(shape) == 0 && unsafe.Sizeof(*r) != 1 {
panic("result: size of data type == 0, but result size != 1")
}
}
// OK returns an OK result with shape Shape and type OK and Err.
// Pass Result[OK, OK, Err] or Result[Err, OK, Err] as the first type argument.
func OK[R ~struct{ result[Shape, OK, Err] }, Shape, OK, Err any](ok OK) R {
var r struct{ result[Shape, OK, Err] }
r.validate()
r.isErr = ResultOK
*((*OK)(unsafe.Pointer(&r.data))) = ok
return R(r)
}
// Err returns an error result with shape Shape and type OK and Err.
// Pass Result[OK, OK, Err] or Result[Err, OK, Err] as the first type argument.
func Err[R ~struct{ result[Shape, OK, Err] }, Shape, OK, Err any](err Err) R {
var r struct{ result[Shape, OK, Err] }
r.validate()
r.isErr = ResultErr
*((*Err)(unsafe.Pointer(&r.data))) = err
return R(r)
}
+230
View File
@@ -0,0 +1,230 @@
package cm
// Tuple represents a [Component Model tuple] with 2 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple[T0, T1 any] struct {
F0 T0
F1 T1
}
// Tuple3 represents a [Component Model tuple] with 3 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple3[T0, T1, T2 any] struct {
F0 T0
F1 T1
F2 T2
}
// Tuple4 represents a [Component Model tuple] with 4 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple4[T0, T1, T2, T3 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
}
// Tuple5 represents a [Component Model tuple] with 5 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple5[T0, T1, T2, T3, T4 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
}
// Tuple6 represents a [Component Model tuple] with 6 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple6[T0, T1, T2, T3, T4, T5 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
}
// Tuple7 represents a [Component Model tuple] with 7 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple7[T0, T1, T2, T3, T4, T5, T6 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
}
// Tuple8 represents a [Component Model tuple] with 8 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple8[T0, T1, T2, T3, T4, T5, T6, T7 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
}
// Tuple9 represents a [Component Model tuple] with 9 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple9[T0, T1, T2, T3, T4, T5, T6, T7, T8 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
F8 T8
}
// Tuple10 represents a [Component Model tuple] with 10 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
F8 T8
F9 T9
}
// Tuple11 represents a [Component Model tuple] with 11 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
F8 T8
F9 T9
F10 T10
}
// Tuple12 represents a [Component Model tuple] with 12 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
F8 T8
F9 T9
F10 T10
F11 T11
}
// Tuple13 represents a [Component Model tuple] with 13 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
F8 T8
F9 T9
F10 T10
F11 T11
F12 T12
}
// Tuple14 represents a [Component Model tuple] with 14 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
F8 T8
F9 T9
F10 T10
F11 T11
F12 T12
F13 T13
}
// Tuple15 represents a [Component Model tuple] with 15 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
F8 T8
F9 T9
F10 T10
F11 T11
F12 T12
F13 T13
F14 T14
}
// Tuple16 represents a [Component Model tuple] with 16 fields.
//
// [Component Model tuple]: https://component-model.bytecodealliance.org/design/wit.html#tuples
type Tuple16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any] struct {
F0 T0
F1 T1
F2 T2
F3 T3
F4 T4
F5 T5
F6 T6
F7 T7
F8 T8
F9 T9
F10 T10
F11 T11
F12 T12
F13 T13
F14 T14
F15 T15
}
// MaxTuple specifies the maximum number of fields in a Tuple* type, currently [Tuple16].
// See https://github.com/WebAssembly/component-model/issues/373 for more information.
const MaxTuple = 16
+74
View File
@@ -0,0 +1,74 @@
package cm
import "unsafe"
// Discriminant is the set of types that can represent the tag or discriminator of a variant.
// Use bool for 2-case variant types, result<T>, or option<T> types, uint8 where there are 256 or
// fewer cases, uint16 for up to 65,536 cases, or uint32 for anything greater.
type Discriminant interface {
bool | uint8 | uint16 | uint32
}
// Variant represents a loosely-typed Component Model variant.
// Shape and Align must be non-zero sized types. To create a variant with no associated
// types, use an enum.
type Variant[Tag Discriminant, Shape, Align any] struct{ variant[Tag, Shape, Align] }
// NewVariant returns a [Variant] with tag of type Disc, storage and GC shape of type Shape,
// aligned to type Align, with a value of type T.
func NewVariant[Tag Discriminant, Shape, Align any, T any](tag Tag, data T) Variant[Tag, Shape, Align] {
validateVariant[Tag, Shape, Align, T]()
var v Variant[Tag, Shape, Align]
v.tag = tag
*(*T)(unsafe.Pointer(&v.data)) = data
return v
}
// New returns a [Variant] with tag of type Disc, storage and GC shape of type Shape,
// aligned to type Align, with a value of type T.
func New[V ~struct{ variant[Tag, Shape, Align] }, Tag Discriminant, Shape, Align any, T any](tag Tag, data T) V {
validateVariant[Tag, Shape, Align, T]()
var v variant[Tag, Shape, Align]
v.tag = tag
*(*T)(unsafe.Pointer(&v.data)) = data
return *(*V)(unsafe.Pointer(&v))
}
// Case returns a non-nil *T if the [Variant] case is equal to tag, otherwise it returns nil.
func Case[T any, V ~struct{ variant[Tag, Shape, Align] }, Tag Discriminant, Shape, Align any](v *V, tag Tag) *T {
validateVariant[Tag, Shape, Align, T]()
v2 := (*variant[Tag, Shape, Align])(unsafe.Pointer(v))
if v2.tag == tag {
return (*T)(unsafe.Pointer(&v2.data))
}
return nil
}
// variant is the internal representation of a Component Model variant.
// Shape and Align must be non-zero sized types.
type variant[Tag Discriminant, Shape, Align any] struct {
tag Tag
_ [0]Align
data Shape // [unsafe.Sizeof(*(*Shape)(unsafe.Pointer(nil)))]byte
}
// Tag returns the tag (discriminant) of variant v.
func (v *variant[Tag, Shape, Align]) Tag() Tag {
return v.tag
}
// This function is sized so it can be inlined and optimized away.
func validateVariant[Disc Discriminant, Shape, Align any, T any]() {
var v variant[Disc, Shape, Align]
var t T
// Check if size of T is greater than Shape
if unsafe.Sizeof(t) > unsafe.Sizeof(v.data) {
panic("variant: size of requested type > data type")
}
// Check if Shape is zero-sized, but size of result != 1
if unsafe.Sizeof(v.data) == 0 && unsafe.Sizeof(v) != 1 {
panic("variant: size of data type == 0, but variant size != 1")
}
}
+66
View File
@@ -0,0 +1,66 @@
.section .text.tinygo_startTask
.global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask:
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
// Most importantly, s0 contains the pc of the to-be-started function and s1
// contains the only argument it is given. Multiple arguments are packed
// into one by storing them in a new allocation.
// Set the first argument of the goroutine start wrapper, which contains all
// the arguments.
move $a0, $s1
// Branch to the "goroutine start" function. Use jalr to write the return
// address to ra so we'll return here after the goroutine exits.
jalr $s0
nop
// After return, exit this goroutine. This is a tail call.
j tinygo_pause
nop
.section .text.tinygo_swapTask
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
// This function gets the following parameters:
// a0 = newStack uintptr
// a1 = oldStack *uintptr
// Push all callee-saved registers.
addiu $sp, $sp, -40
sw $ra, 36($sp)
sw $s8, 32($sp)
sw $s7, 28($sp)
sw $s6, 24($sp)
sw $s5, 20($sp)
sw $s4, 16($sp)
sw $s3, 12($sp)
sw $s2, 8($sp)
sw $s1, 4($sp)
sw $s0, ($sp)
// Save the current stack pointer in oldStack.
sw $sp, 0($a1)
// Switch to the new stack pointer.
move $sp, $a0
// Pop all saved registers from this new stack.
lw $ra, 36($sp)
lw $s8, 32($sp)
lw $s7, 28($sp)
lw $s6, 24($sp)
lw $s5, 20($sp)
lw $s4, 16($sp)
lw $s3, 12($sp)
lw $s2, 8($sp)
lw $s1, 4($sp)
lw $s0, ($sp)
addiu $sp, $sp, 40
// Return into the task.
jalr $ra
nop
+61
View File
@@ -0,0 +1,61 @@
//go:build scheduler.tasks && (mips || mipsle)
package task
import "unsafe"
var systemStack uintptr
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see task_stack_mips.S that relies on the exact
// layout of this struct.
type calleeSavedRegs struct {
s0 uintptr
s1 uintptr
s2 uintptr
s3 uintptr
s4 uintptr
s5 uintptr
s6 uintptr
s7 uintptr
s8 uintptr
ra uintptr
}
// archInit runs architecture-specific setup for the goroutine startup.
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
// Store the initial sp for the startTask function (implemented in assembly).
s.sp = uintptr(unsafe.Pointer(r))
// Initialize the registers.
// These will be popped off of the stack on the first resume of the goroutine.
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_mipsle.S).
// This assembly code calls a function (passed in s0) with a single argument
// (passed in s1). After the function returns, it calls Pause().
r.ra = uintptr(unsafe.Pointer(&startTask))
// Pass the function to call in s0.
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
r.s0 = fn
// Pass the pointer to the arguments struct in s1.
r.s1 = uintptr(args)
}
func (s *state) resume() {
swapTask(s.sp, &systemStack)
}
func (s *state) pause() {
newStack := systemStack
systemStack = 0
swapTask(newStack, &s.sp)
}
// SystemStack returns the system stack pointer when called from a task stack.
// When called from the system stack, it returns 0.
func SystemStack() uintptr {
return systemStack
}
@@ -1,8 +1,5 @@
//go:build tinygo
// We don't support RV32E at the moment.
#if !defined(__riscv_32e)
.section .text.tinygo_startTask
.global tinygo_startTask
.type tinygo_startTask, %function
@@ -72,5 +69,3 @@ tinygo_swapTask:
// Return into the task.
ret
#endif
@@ -0,0 +1,6 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package command represents the world "wasi:cli/command@0.2.0".
package command
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,66 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package environment represents the imported interface "wasi:cli/environment@0.2.0".
package environment
import (
"internal/cm"
)
// GetEnvironment represents the imported function "get-environment".
//
// Get the POSIX-style environment variables.
//
// Each environment variable is provided as a pair of string variable names
// and string value.
//
// Morally, these are a value import, but until value imports are available
// in the component model, this import function should return the same
// values each time it is called.
//
// get-environment: func() -> list<tuple<string, string>>
//
//go:nosplit
func GetEnvironment() (result cm.List[[2]string]) {
wasmimport_GetEnvironment(&result)
return
}
//go:wasmimport wasi:cli/environment@0.2.0 get-environment
//go:noescape
func wasmimport_GetEnvironment(result *cm.List[[2]string])
// GetArguments represents the imported function "get-arguments".
//
// Get the POSIX-style arguments to the program.
//
// get-arguments: func() -> list<string>
//
//go:nosplit
func GetArguments() (result cm.List[string]) {
wasmimport_GetArguments(&result)
return
}
//go:wasmimport wasi:cli/environment@0.2.0 get-arguments
//go:noescape
func wasmimport_GetArguments(result *cm.List[string])
// InitialCWD represents the imported function "initial-cwd".
//
// Return a path that programs should use as their initial current working
// directory, interpreting `.` as shorthand for this.
//
// initial-cwd: func() -> option<string>
//
//go:nosplit
func InitialCWD() (result cm.Option[string]) {
wasmimport_InitialCWD(&result)
return
}
//go:wasmimport wasi:cli/environment@0.2.0 initial-cwd
//go:noescape
func wasmimport_InitialCWD(result *cm.Option[string])
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,27 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package exit represents the imported interface "wasi:cli/exit@0.2.0".
package exit
import (
"internal/cm"
)
// Exit represents the imported function "exit".
//
// Exit the current instance and any linked instances.
//
// exit: func(status: result)
//
//go:nosplit
func Exit(status cm.BoolResult) {
status0 := cm.BoolToU32(status)
wasmimport_Exit((uint32)(status0))
return
}
//go:wasmimport wasi:cli/exit@0.2.0 exit
//go:noescape
func wasmimport_Exit(status0 uint32)
+3
View File
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,19 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
package run
import (
"internal/cm"
)
// Exports represents the caller-defined exports from "wasi:cli/run@0.2.0".
var Exports struct {
// Run represents the caller-defined, exported function "run".
//
// Run the program.
//
// run: func() -> result
Run func() (result cm.BoolResult)
}
@@ -0,0 +1,18 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package run represents the exported interface "wasi:cli/run@0.2.0".
package run
import (
"internal/cm"
)
//go:wasmexport wasi:cli/run@0.2.0#run
//export wasi:cli/run@0.2.0#run
func wasmexport_Run() (result0 uint32) {
result := Exports.Run()
result0 = cm.BoolToU32(result)
return
}
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,26 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package stderr represents the imported interface "wasi:cli/stderr@0.2.0".
package stderr
import (
"internal/cm"
"internal/wasi/io/v0.2.0/streams"
)
// GetStderr represents the imported function "get-stderr".
//
// get-stderr: func() -> output-stream
//
//go:nosplit
func GetStderr() (result streams.OutputStream) {
result0 := wasmimport_GetStderr()
result = cm.Reinterpret[streams.OutputStream]((uint32)(result0))
return
}
//go:wasmimport wasi:cli/stderr@0.2.0 get-stderr
//go:noescape
func wasmimport_GetStderr() (result0 uint32)
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,26 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package stdin represents the imported interface "wasi:cli/stdin@0.2.0".
package stdin
import (
"internal/cm"
"internal/wasi/io/v0.2.0/streams"
)
// GetStdin represents the imported function "get-stdin".
//
// get-stdin: func() -> input-stream
//
//go:nosplit
func GetStdin() (result streams.InputStream) {
result0 := wasmimport_GetStdin()
result = cm.Reinterpret[streams.InputStream]((uint32)(result0))
return
}
//go:wasmimport wasi:cli/stdin@0.2.0 get-stdin
//go:noescape
func wasmimport_GetStdin() (result0 uint32)
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,26 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package stdout represents the imported interface "wasi:cli/stdout@0.2.0".
package stdout
import (
"internal/cm"
"internal/wasi/io/v0.2.0/streams"
)
// GetStdout represents the imported function "get-stdout".
//
// get-stdout: func() -> output-stream
//
//go:nosplit
func GetStdout() (result streams.OutputStream) {
result0 := wasmimport_GetStdout()
result = cm.Reinterpret[streams.OutputStream]((uint32)(result0))
return
}
//go:wasmimport wasi:cli/stdout@0.2.0 get-stdout
//go:noescape
func wasmimport_GetStdout() (result0 uint32)
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,38 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package terminalinput represents the imported interface "wasi:cli/terminal-input@0.2.0".
//
// Terminal input.
//
// In the future, this may include functions for disabling echoing,
// disabling input buffering so that keyboard events are sent through
// immediately, querying supported features, and so on.
package terminalinput
import (
"internal/cm"
)
// TerminalInput represents the imported resource "wasi:cli/terminal-input@0.2.0#terminal-input".
//
// The input side of a terminal.
//
// resource terminal-input
type TerminalInput cm.Resource
// ResourceDrop represents the imported resource-drop for resource "terminal-input".
//
// Drops a resource handle.
//
//go:nosplit
func (self TerminalInput) ResourceDrop() {
self0 := cm.Reinterpret[uint32](self)
wasmimport_TerminalInputResourceDrop((uint32)(self0))
return
}
//go:wasmimport wasi:cli/terminal-input@0.2.0 [resource-drop]terminal-input
//go:noescape
func wasmimport_TerminalInputResourceDrop(self0 uint32)
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,38 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package terminaloutput represents the imported interface "wasi:cli/terminal-output@0.2.0".
//
// Terminal output.
//
// In the future, this may include functions for querying the terminal
// size, being notified of terminal size changes, querying supported
// features, and so on.
package terminaloutput
import (
"internal/cm"
)
// TerminalOutput represents the imported resource "wasi:cli/terminal-output@0.2.0#terminal-output".
//
// The output side of a terminal.
//
// resource terminal-output
type TerminalOutput cm.Resource
// ResourceDrop represents the imported resource-drop for resource "terminal-output".
//
// Drops a resource handle.
//
//go:nosplit
func (self TerminalOutput) ResourceDrop() {
self0 := cm.Reinterpret[uint32](self)
wasmimport_TerminalOutputResourceDrop((uint32)(self0))
return
}
//go:wasmimport wasi:cli/terminal-output@0.2.0 [resource-drop]terminal-output
//go:noescape
func wasmimport_TerminalOutputResourceDrop(self0 uint32)
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,31 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package terminalstderr represents the imported interface "wasi:cli/terminal-stderr@0.2.0".
//
// An interface providing an optional `terminal-output` for stderr as a
// link-time authority.
package terminalstderr
import (
"internal/cm"
terminaloutput "internal/wasi/cli/v0.2.0/terminal-output"
)
// GetTerminalStderr represents the imported function "get-terminal-stderr".
//
// If stderr is connected to a terminal, return a `terminal-output` handle
// allowing further interaction with it.
//
// get-terminal-stderr: func() -> option<terminal-output>
//
//go:nosplit
func GetTerminalStderr() (result cm.Option[terminaloutput.TerminalOutput]) {
wasmimport_GetTerminalStderr(&result)
return
}
//go:wasmimport wasi:cli/terminal-stderr@0.2.0 get-terminal-stderr
//go:noescape
func wasmimport_GetTerminalStderr(result *cm.Option[terminaloutput.TerminalOutput])
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,31 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package terminalstdin represents the imported interface "wasi:cli/terminal-stdin@0.2.0".
//
// An interface providing an optional `terminal-input` for stdin as a
// link-time authority.
package terminalstdin
import (
"internal/cm"
terminalinput "internal/wasi/cli/v0.2.0/terminal-input"
)
// GetTerminalStdin represents the imported function "get-terminal-stdin".
//
// If stdin is connected to a terminal, return a `terminal-input` handle
// allowing further interaction with it.
//
// get-terminal-stdin: func() -> option<terminal-input>
//
//go:nosplit
func GetTerminalStdin() (result cm.Option[terminalinput.TerminalInput]) {
wasmimport_GetTerminalStdin(&result)
return
}
//go:wasmimport wasi:cli/terminal-stdin@0.2.0 get-terminal-stdin
//go:noescape
func wasmimport_GetTerminalStdin(result *cm.Option[terminalinput.TerminalInput])
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,31 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package terminalstdout represents the imported interface "wasi:cli/terminal-stdout@0.2.0".
//
// An interface providing an optional `terminal-output` for stdout as a
// link-time authority.
package terminalstdout
import (
"internal/cm"
terminaloutput "internal/wasi/cli/v0.2.0/terminal-output"
)
// GetTerminalStdout represents the imported function "get-terminal-stdout".
//
// If stdout is connected to a terminal, return a `terminal-output` handle
// allowing further interaction with it.
//
// get-terminal-stdout: func() -> option<terminal-output>
//
//go:nosplit
func GetTerminalStdout() (result cm.Option[terminaloutput.TerminalOutput]) {
wasmimport_GetTerminalStdout(&result)
return
}
//go:wasmimport wasi:cli/terminal-stdout@0.2.0 get-terminal-stdout
//go:noescape
func wasmimport_GetTerminalStdout(result *cm.Option[terminaloutput.TerminalOutput])
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,115 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package monotonicclock represents the imported interface "wasi:clocks/monotonic-clock@0.2.0".
//
// WASI Monotonic Clock is a clock API intended to let users measure elapsed
// time.
//
// It is intended to be portable at least between Unix-family platforms and
// Windows.
//
// A monotonic clock is a clock which has an unspecified initial value, and
// successive reads of the clock will produce non-decreasing values.
//
// It is intended for measuring elapsed time.
package monotonicclock
import (
"internal/cm"
"internal/wasi/io/v0.2.0/poll"
)
// Instant represents the u64 "wasi:clocks/monotonic-clock@0.2.0#instant".
//
// An instant in time, in nanoseconds. An instant is relative to an
// unspecified initial value, and can only be compared to instances from
// the same monotonic-clock.
//
// type instant = u64
type Instant uint64
// Duration represents the u64 "wasi:clocks/monotonic-clock@0.2.0#duration".
//
// A duration of time, in nanoseconds.
//
// type duration = u64
type Duration uint64
// Now represents the imported function "now".
//
// Read the current value of the clock.
//
// The clock is monotonic, therefore calling this function repeatedly will
// produce a sequence of non-decreasing values.
//
// now: func() -> instant
//
//go:nosplit
func Now() (result Instant) {
result0 := wasmimport_Now()
result = (Instant)((uint64)(result0))
return
}
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 now
//go:noescape
func wasmimport_Now() (result0 uint64)
// Resolution represents the imported function "resolution".
//
// Query the resolution of the clock. Returns the duration of time
// corresponding to a clock tick.
//
// resolution: func() -> duration
//
//go:nosplit
func Resolution() (result Duration) {
result0 := wasmimport_Resolution()
result = (Duration)((uint64)(result0))
return
}
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 resolution
//go:noescape
func wasmimport_Resolution() (result0 uint64)
// SubscribeInstant represents the imported function "subscribe-instant".
//
// Create a `pollable` which will resolve once the specified instant
// occured.
//
// subscribe-instant: func(when: instant) -> pollable
//
//go:nosplit
func SubscribeInstant(when Instant) (result poll.Pollable) {
when0 := (uint64)(when)
result0 := wasmimport_SubscribeInstant((uint64)(when0))
result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
return
}
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 subscribe-instant
//go:noescape
func wasmimport_SubscribeInstant(when0 uint64) (result0 uint32)
// SubscribeDuration represents the imported function "subscribe-duration".
//
// Create a `pollable` which will resolve once the given duration has
// elapsed, starting at the time at which this function was called.
// occured.
//
// subscribe-duration: func(when: duration) -> pollable
//
//go:nosplit
func SubscribeDuration(when Duration) (result poll.Pollable) {
when0 := (uint64)(when)
result0 := wasmimport_SubscribeDuration((uint64)(when0))
result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
return
}
//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 subscribe-duration
//go:noescape
func wasmimport_SubscribeDuration(when0 uint64) (result0 uint32)
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,80 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package wallclock represents the imported interface "wasi:clocks/wall-clock@0.2.0".
//
// WASI Wall Clock is a clock API intended to let users query the current
// time. The name "wall" makes an analogy to a "clock on the wall", which
// is not necessarily monotonic as it may be reset.
//
// It is intended to be portable at least between Unix-family platforms and
// Windows.
//
// A wall clock is a clock which measures the date and time according to
// some external reference.
//
// External references may be reset, so this clock is not necessarily
// monotonic, making it unsuitable for measuring elapsed time.
//
// It is intended for reporting the current date and time for humans.
package wallclock
// DateTime represents the record "wasi:clocks/wall-clock@0.2.0#datetime".
//
// A time and date in seconds plus nanoseconds.
//
// record datetime {
// seconds: u64,
// nanoseconds: u32,
// }
type DateTime struct {
Seconds uint64
Nanoseconds uint32
}
// Now represents the imported function "now".
//
// Read the current value of the clock.
//
// This clock is not monotonic, therefore calling this function repeatedly
// will not necessarily produce a sequence of non-decreasing values.
//
// The returned timestamps represent the number of seconds since
// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch],
// also known as [Unix Time].
//
// The nanoseconds field of the output is always less than 1000000000.
//
// now: func() -> datetime
//
// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16
// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time
//
//go:nosplit
func Now() (result DateTime) {
wasmimport_Now(&result)
return
}
//go:wasmimport wasi:clocks/wall-clock@0.2.0 now
//go:noescape
func wasmimport_Now(result *DateTime)
// Resolution represents the imported function "resolution".
//
// Query the resolution of the clock.
//
// The nanoseconds field of the output is always less than 1000000000.
//
// resolution: func() -> datetime
//
//go:nosplit
func Resolution() (result DateTime) {
wasmimport_Resolution(&result)
return
}
//go:wasmimport wasi:clocks/wall-clock@0.2.0 resolution
//go:noescape
func wasmimport_Resolution(result *DateTime)
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,27 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package preopens represents the imported interface "wasi:filesystem/preopens@0.2.0".
package preopens
import (
"internal/cm"
"internal/wasi/filesystem/v0.2.0/types"
)
// GetDirectories represents the imported function "get-directories".
//
// Return the set of preopened directories, and their path.
//
// get-directories: func() -> list<tuple<descriptor, string>>
//
//go:nosplit
func GetDirectories() (result cm.List[cm.Tuple[types.Descriptor, string]]) {
wasmimport_GetDirectories(&result)
return
}
//go:wasmimport wasi:filesystem/preopens@0.2.0 get-directories
//go:noescape
func wasmimport_GetDirectories(result *cm.List[cm.Tuple[types.Descriptor, string]])
@@ -0,0 +1,53 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
package types
import (
"internal/cm"
wallclock "internal/wasi/clocks/v0.2.0/wall-clock"
"unsafe"
)
// DateTimeShape is used for storage in variant or result types.
type DateTimeShape struct {
shape [unsafe.Sizeof(wallclock.DateTime{})]byte
}
// MetadataHashValueShape is used for storage in variant or result types.
type MetadataHashValueShape struct {
shape [unsafe.Sizeof(MetadataHashValue{})]byte
}
// TupleListU8BoolShape is used for storage in variant or result types.
type TupleListU8BoolShape struct {
shape [unsafe.Sizeof(cm.Tuple[cm.List[uint8], bool]{})]byte
}
func lower_DateTime(v wallclock.DateTime) (f0 uint64, f1 uint32) {
f0 = (uint64)(v.Seconds)
f1 = (uint32)(v.Nanoseconds)
return
}
func lower_NewTimestamp(v NewTimestamp) (f0 uint32, f1 uint64, f2 uint32) {
f0 = (uint32)(v.Tag())
switch f0 {
case 2: // timestamp
v1, v2 := lower_DateTime(*v.Timestamp())
f1 = (uint64)(v1)
f2 = (uint32)(v2)
}
return
}
// DescriptorStatShape is used for storage in variant or result types.
type DescriptorStatShape struct {
shape [unsafe.Sizeof(DescriptorStat{})]byte
}
// OptionDirectoryEntryShape is used for storage in variant or result types.
type OptionDirectoryEntryShape struct {
shape [unsafe.Sizeof(cm.Option[DirectoryEntry]{})]byte
}
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.
@@ -0,0 +1,73 @@
// Code generated by wit-bindgen-go. DO NOT EDIT.
//go:build !wasip1
// Package ioerror represents the imported interface "wasi:io/error@0.2.0".
package ioerror
import (
"internal/cm"
)
// Error represents the imported resource "wasi:io/error@0.2.0#error".
//
// A resource which represents some error information.
//
// The only method provided by this resource is `to-debug-string`,
// which provides some human-readable information about the error.
//
// In the `wasi:io` package, this resource is returned through the
// `wasi:io/streams/stream-error` type.
//
// To provide more specific error information, other interfaces may
// provide functions to further "downcast" this error into more specific
// error information. For example, `error`s returned in streams derived
// from filesystem types to be described using the filesystem's own
// error-code type, using the function
// `wasi:filesystem/types/filesystem-error-code`, which takes a parameter
// `borrow<error>` and returns
// `option<wasi:filesystem/types/error-code>`.
//
// The set of functions which can "downcast" an `error` into a more
// concrete type is open.
//
// resource error
type Error cm.Resource
// ResourceDrop represents the imported resource-drop for resource "error".
//
// Drops a resource handle.
//
//go:nosplit
func (self Error) ResourceDrop() {
self0 := cm.Reinterpret[uint32](self)
wasmimport_ErrorResourceDrop((uint32)(self0))
return
}
//go:wasmimport wasi:io/error@0.2.0 [resource-drop]error
//go:noescape
func wasmimport_ErrorResourceDrop(self0 uint32)
// ToDebugString represents the imported method "to-debug-string".
//
// Returns a string that is suitable to assist humans in debugging
// this error.
//
// WARNING: The returned string should not be consumed mechanically!
// It may change across platforms, hosts, or other implementation
// details. Parsing this string is a major platform-compatibility
// hazard.
//
// to-debug-string: func() -> string
//
//go:nosplit
func (self Error) ToDebugString() (result string) {
self0 := cm.Reinterpret[uint32](self)
wasmimport_ErrorToDebugString((uint32)(self0), &result)
return
}
//go:wasmimport wasi:io/error@0.2.0 [method]error.to-debug-string
//go:noescape
func wasmimport_ErrorToDebugString(self0 uint32, result *string)
+3
View File
@@ -0,0 +1,3 @@
// This file exists for testing this package without WebAssembly,
// allowing empty function bodies with a //go:wasmimport directive.
// See https://pkg.go.dev/cmd/compile for more information.

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