Skip to content

Commit ae25bc5

Browse files
Updates TinyGo to use Go 1.20 features (#26)
TinyGo 0.27 doesn't fully support Go 1.20, but it supports what we need. Particularly, `unsafe.SliceData` and `unsafe.StringData` were added to TinyGo 0.27 ahead of the more complete Go 1.20 version: 0.28 (not yet out). Using the approach elaborated in wazero [1.2.0](https://github.com/tetratelabs/wazero/releases/tag/v1.2.0) TinyGo examples resulted in significant performance benefits, benefits far more than just the part around the runtime. Signed-off-by: Adrian Cole <adrian@tetrate.io>
1 parent d3dc43a commit ae25bc5

File tree

32 files changed

+111
-88
lines changed

32 files changed

+111
-88
lines changed

.github/workflows/commit.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
strategy:
2626
matrix:
2727
go-version: # Note: Go only supports 2 versions: https://go.dev/doc/devel/release#policy
28-
- "1.19"
28+
- "1.20"
2929

3030
steps:
3131
- uses: actions/checkout@v3
@@ -50,17 +50,16 @@ jobs:
5050
strategy:
5151
matrix:
5252
go-version: # Note: Go only supports 2 versions: https://go.dev/doc/devel/release#policy
53-
- "1.19"
53+
- "1.20"
5454
tinygo-version: # Note: TinyGo only supports latest: https://github.com/tinygo-org/tinygo/releases
5555
- "0.27.0" # Latest
5656

5757
steps:
5858
- uses: actions/checkout@v3
5959

60-
- uses: actions/setup-go@v3
60+
- uses: actions/setup-go@v4
6161
with:
6262
go-version: ${{ matrix.go-version }}
63-
cache: true
6463

6564
- name: "Set up TinyGo"
6665
run: | # Installing via curl so commands are similar on OS/x

.github/workflows/testdata.yaml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,16 @@ jobs:
2727
strategy:
2828
matrix:
2929
go-version: # Note: Go only supports 2 versions: https://go.dev/doc/devel/release#policy
30-
- "1.19"
30+
- "1.20"
3131
tinygo-version: # Note: TinyGo only supports latest: https://github.com/tinygo-org/tinygo/releases
3232
- "0.27.0" # Latest
3333

3434
steps:
3535
- uses: actions/checkout@v3
3636

37-
- uses: actions/setup-go@v3
38-
with: # Not cache: true because we also cache go-build and golangci-lint
37+
- uses: actions/setup-go@v4
38+
with:
3939
go-version: ${{ matrix.go-version }}
40-
cache: true
4140

4241
- name: "Set up TinyGo"
4342
run: | # Installing via curl so commands are similar on OS/x
@@ -48,7 +47,7 @@ jobs:
4847
4948
- name: "Set up wat2wasm"
5049
run: | # Needed for testdata. wabt includes wat2wasm.
51-
wabt_version=1.0.32
50+
wabt_version=1.0.33
5251
wabt_url=https://github.com/WebAssembly/wabt/releases/download/${wabt_version}/wabt-${wabt_version}-ubuntu.tar.gz
5352
curl -sSL ${wabt_url} | tar --strip-components 2 -C /usr/local/bin -xzf - wabt-${wabt_version}/bin/wat2wasm
5453

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This is built using - https://golangci-lint.run/usage/configuration/
22
run:
3-
go: '1.19'
3+
go: '1.20'
44
modules-download-mode: readonly
55
tests: false
66

Makefile

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
gosimports := github.com/rinchsan/gosimports/cmd/gosimports@v0.3.7
2-
golangci_lint := github.com/golangci/golangci-lint/cmd/golangci-lint@v1.51.2
1+
gofumpt := mvdan.cc/gofumpt@v0.5.0
2+
gosimports := github.com/rinchsan/gosimports/cmd/gosimports@v0.3.8
3+
golangci_lint := github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2
34

45
.PHONY: testdata
56
testdata:
@@ -43,12 +44,8 @@ lint: $(golangci_lint_path)
4344

4445
.PHONY: format
4546
format:
46-
@find . -type f -name '*.go' | xargs gofmt -s -w
47-
@for f in `find . -name '*.go'`; do \
48-
awk '/^import \($$/,/^\)$$/{if($$0=="")next}{print}' $$f > /tmp/fmt; \
49-
mv /tmp/fmt $$f; \
50-
done
51-
@go run $(gosimports) -local github.com/http-wasm/http-wasm-guest-tinygo -w $(shell find . -name '*.go' -type f)
47+
@go run $(gofumpt) -l -w .
48+
@go run $(gosimports) -local github.com/http-wasm/ -w $(shell find . -name '*.go' -type f)
5249

5350
.PHONY: check
5451
check:

examples/router/main.wasm

-312 Bytes
Binary file not shown.

examples/wasi/main.wasm

-31 Bytes
Binary file not shown.

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
module github.com/http-wasm/http-wasm-guest-tinygo
22

3-
go 1.19
3+
// TinyGo 0.27 doesn't fully support Go 1.20, but it supports what we need.
4+
// Particularly, unsafe.SliceData, unsafe.StringData were added to TinyGo 0.27.
5+
// See https://github.com/tinygo-org/tinygo/commit/c43958972c3ffcd51e65414a346e53779edb9f97
6+
go 1.20

handler/body.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package handler
22

33
import (
44
"io"
5-
"unsafe"
5+
"runtime"
66

77
"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
88
"github.com/http-wasm/http-wasm-guest-tinygo/handler/internal/imports"
@@ -40,13 +40,14 @@ func (b wasmBody) WriteTo(w io.Writer) (written uint64, err error) {
4040

4141
// ReadN implements the same method as documented on api.Body.
4242
func (b wasmBody) Read(bytes []byte) (size uint32, eof bool) {
43-
limit := uint32(len(bytes))
44-
if limit == 0 { // invalid, but prevent crashing.
45-
return 0, false
43+
ptr, limit := mem.SliceToPtr(bytes)
44+
if limit == 0 {
45+
return // invalid, but prevent crashing.
4646
}
4747

48-
ptr := uintptr(unsafe.Pointer(&bytes[0])) // TODO: mem.SliceToPtr
49-
return read(b, ptr, limit)
48+
size, eof = read(b, uintptr(ptr), limit)
49+
runtime.KeepAlive(bytes) // keep bytes alive until ptr is no longer needed.
50+
return
5051
}
5152

5253
func read(b wasmBody, ptr uintptr, limit imports.BufLimit) (size uint32, eof bool) {
@@ -58,21 +59,22 @@ func read(b wasmBody, ptr uintptr, limit imports.BufLimit) (size uint32, eof boo
5859

5960
// Write implements the same method as documented on api.Body.
6061
func (b wasmBody) Write(bytes []byte) {
61-
size := uint32(len(bytes))
62-
if size == 0 { // invalid, but prevent crashing.
62+
ptr, size := mem.SliceToPtr(bytes)
63+
if size == 0 {
6364
return
6465
}
6566

66-
ptr := uintptr(unsafe.Pointer(&bytes[0])) // TODO: mem.SliceToPtr
67-
imports.WriteBody(imports.BodyKind(b), ptr, size)
67+
imports.WriteBody(imports.BodyKind(b), uintptr(ptr), size)
68+
runtime.KeepAlive(bytes) // keep bytes alive until ptr is no longer needed.
6869
}
6970

7071
// WriteString implements the same method as documented on api.Body.
7172
func (b wasmBody) WriteString(s string) {
7273
ptr, size := mem.StringToPtr(s)
73-
if size == 0 { // invalid, but prevent crashing.
74+
if size == 0 {
7475
return
7576
}
7677

77-
imports.WriteBody(imports.BodyKind(b), ptr, size)
78+
imports.WriteBody(imports.BodyKind(b), uintptr(ptr), size)
79+
runtime.KeepAlive(s) // keep s alive until ptr is no longer needed.
7880
}

handler/header.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package handler
22

33
import (
4+
"runtime"
45
"unsafe"
56

67
"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
@@ -30,9 +31,11 @@ func (w wasmHeader) Names() (names []string) {
3031
}
3132
// Otherwise, we have to allocate a new buffer for the large entry.
3233
buf := make([]byte, size)
33-
ptr := uintptr(unsafe.Pointer(&buf[0]))
34-
_ = imports.GetHeaderNames(imports.HeaderKind(w), ptr, size)
35-
return mem.GetNULTerminated(buf)
34+
ptr := unsafe.Pointer(unsafe.SliceData(buf))
35+
_ = imports.GetHeaderNames(imports.HeaderKind(w), uintptr(ptr), size)
36+
names = mem.GetNULTerminated(buf)
37+
runtime.KeepAlive(buf) // keep buf alive until ptr is no longer needed.
38+
return
3639
}
3740

3841
// Get implements the same method as documented on api.Request.
@@ -48,10 +51,12 @@ func (w wasmHeader) Get(name string) (value string, ok bool) {
4851
// GetAll implements the same method as documented on api.Request.
4952
func (w wasmHeader) GetAll(name string) (names []string) {
5053
namePtr, nameSize := mem.StringToPtr(name)
51-
countLen := imports.GetHeaderValues(imports.HeaderKind(w), namePtr, nameSize, mem.ReadBufPtr, mem.ReadBufLimit)
54+
countLen := imports.GetHeaderValues(imports.HeaderKind(w), uintptr(namePtr), nameSize, mem.ReadBufPtr, mem.ReadBufLimit)
55+
runtime.KeepAlive(name) // keep name alive until ptr is no longer needed.
5256
if countLen == 0 {
5357
return
5458
}
59+
5560
size := uint32(countLen)
5661
if size == 0 {
5762
return
@@ -61,27 +66,35 @@ func (w wasmHeader) GetAll(name string) (names []string) {
6166
}
6267
// Otherwise, we have to allocate a new buffer for the large entry.
6368
buf := make([]byte, size)
64-
ptr := uintptr(unsafe.Pointer(&buf[0]))
65-
_ = imports.GetHeaderValues(imports.HeaderKind(w), namePtr, nameSize, ptr, size)
66-
return mem.GetNULTerminated(buf)
69+
ptr := unsafe.Pointer(unsafe.SliceData(buf))
70+
_ = imports.GetHeaderValues(imports.HeaderKind(w), uintptr(namePtr), nameSize, uintptr(ptr), size)
71+
names = mem.GetNULTerminated(buf)
72+
runtime.KeepAlive(name) // keep name alive until ptr is no longer needed.
73+
runtime.KeepAlive(buf) // keep buf alive until ptr is no longer needed.
74+
return
6775
}
6876

6977
// Set implements the same method as documented on api.Request.
7078
func (w wasmHeader) Set(name, value string) {
7179
namePtr, nameSize := mem.StringToPtr(name)
7280
valuePtr, valueSize := mem.StringToPtr(value)
73-
imports.SetHeaderValue(imports.HeaderKind(w), namePtr, nameSize, valuePtr, valueSize)
81+
imports.SetHeaderValue(imports.HeaderKind(w), uintptr(namePtr), nameSize, uintptr(valuePtr), valueSize)
82+
runtime.KeepAlive(name) // keep name alive until ptr is no longer needed.
83+
runtime.KeepAlive(value) // keep value alive until ptr is no longer needed.
7484
}
7585

7686
// Add implements the same method as documented on api.Request.
7787
func (w wasmHeader) Add(name, value string) {
7888
namePtr, nameSize := mem.StringToPtr(name)
7989
valuePtr, valueSize := mem.StringToPtr(value)
80-
imports.AddHeaderValue(imports.HeaderKind(w), namePtr, nameSize, valuePtr, valueSize)
90+
imports.AddHeaderValue(imports.HeaderKind(w), uintptr(namePtr), nameSize, uintptr(valuePtr), valueSize)
91+
runtime.KeepAlive(name) // keep name alive until ptr is no longer needed.
92+
runtime.KeepAlive(value) // keep value alive until ptr is no longer needed.
8193
}
8294

8395
// Remove implements the same method as documented on api.Request.
8496
func (w wasmHeader) Remove(name string) {
8597
namePtr, nameSize := mem.StringToPtr(name)
86-
imports.RemoveHeader(imports.HeaderKind(w), namePtr, nameSize)
98+
imports.RemoveHeader(imports.HeaderKind(w), uintptr(namePtr), nameSize)
99+
runtime.KeepAlive(name) // keep name alive until ptr is no longer needed.
87100
}

handler/host.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package handler
22

33
import (
4+
"runtime"
5+
46
"github.com/http-wasm/http-wasm-guest-tinygo/handler/api"
57
"github.com/http-wasm/http-wasm-guest-tinygo/handler/internal/imports"
68
"github.com/http-wasm/http-wasm-guest-tinygo/handler/internal/mem"
@@ -36,5 +38,6 @@ func (wasmHost) Log(level api.LogLevel, message string) {
3638
return // don't incur host call overhead
3739
}
3840
ptr, size := mem.StringToPtr(message)
39-
imports.Log(level, ptr, size)
41+
imports.Log(level, uintptr(ptr), size)
42+
runtime.KeepAlive(message) // keep message alive until ptr is no longer needed.
4043
}

0 commit comments

Comments
 (0)