From 7dabd72369ccfa0c29eeaccf20a9262708b39ba4 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Sat, 5 Oct 2024 15:06:35 -0700 Subject: [PATCH 1/4] compiler: align with current wasm types proposal https://github.com/golang/go/issues/66984 - Remove int and uint as allowed types in params, results, pointers, or struct fields - Only allow small integers in pointers, arrays, or struct fields --- compiler/symbol.go | 4 +++- compiler/testdata/errors.go | 29 +++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/compiler/symbol.go b/compiler/symbol.go index e175bdd78d..7cb3750648 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -448,7 +448,9 @@ func isValidWasmType(typ types.Type, site wasmSite) bool { switch typ.Kind() { 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: + case types.Int8, types.Uint8, types.Int16, types.Uint16: + return site == siteIndirect + case types.Int32, types.Uint32, types.Int64, types.Uint64: return true case types.Float32, types.Float64: return true diff --git a/compiler/testdata/errors.go b/compiler/testdata/errors.go index 81e8a76a18..0a76aa07a1 100644 --- a/compiler/testdata/errors.go +++ b/compiler/testdata/errors.go @@ -16,29 +16,28 @@ type Uint uint32 type S struct { a [4]uint32 b uintptr - c int d float32 e float64 } //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) +func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S, j *[8]uint8) // 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 struct{a int} // ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type chan struct{} // ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type func() +// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int +// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type uint +// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [8]int // //go:wasmimport modulename invalidparam -func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func()) +func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func(), f int, g uint, h [8]int) //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 @@ -48,6 +47,9 @@ func validreturn_ptr_string() *string //go:wasmimport modulename validreturn_ptr_struct func validreturn_ptr_struct() *S +//go:wasmimport modulename validreturn_ptr_array +func validreturn_ptr_array() *[8]uint8 + //go:wasmimport modulename validreturn_unsafe_pointer func validreturn_unsafe_pointer() unsafe.Pointer @@ -56,11 +58,26 @@ func validreturn_unsafe_pointer() unsafe.Pointer //go:wasmimport modulename manyreturns func manyreturns() (int32, int32) +// ERROR: //go:wasmimport modulename invalidreturn_int: unsupported result type int +// +//go:wasmimport modulename invalidreturn_int +func invalidreturn_int() int + +// ERROR: //go:wasmimport modulename invalidreturn_int: unsupported result type uint +// +//go:wasmimport modulename invalidreturn_int +func invalidreturn_uint() uint + // ERROR: //go:wasmimport modulename invalidreturn_func: unsupported result type func() // //go:wasmimport modulename invalidreturn_func func invalidreturn_func() func() +// ERROR: //go:wasmimport modulename invalidreturn_pointer_array_int: unsupported result type *[8]int +// +//go:wasmimport modulename invalidreturn_pointer_array_int +func invalidreturn_pointer_array_int() *[8]int + // ERROR: //go:wasmimport modulename invalidreturn_slice_byte: unsupported result type []byte // //go:wasmimport modulename invalidreturn_slice_byte From f43df3ac766bfba9f3e7c9d6087190b47bc36497 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Sun, 6 Oct 2024 13:32:43 -0700 Subject: [PATCH 2/4] compiler: enforce structs.HostLayout usage per wasm types proposal https://github.com/golang/go/issues/66984 TODO: regenerate WASI syscall packages for GC shape types including structs.HostLayout compiler: require go1.23 for structs.HostLayout compiler: use an interface to check if GoVersion() exists This permits TinyGo to compile with Go 1.21. compiler: use goenv.Compare instead of WantGoVersion --- compiler/symbol.go | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/compiler/symbol.go b/compiler/symbol.go index 7cb3750648..9b9b1d10e8 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/tinygo-org/tinygo/compiler/llvmutil" + "github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/loader" "golang.org/x/tools/go/ssa" "tinygo.org/x/go-llvm" @@ -422,14 +423,14 @@ func (c *compilerContext) checkWasmImportExport(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(), siteResult) { + if !c.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(), siteParam) { + if !c.isValidWasmType(param.Type(), siteParam) { c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String())) } } @@ -442,7 +443,7 @@ func (c *compilerContext) checkWasmImportExport(f *ssa.Function, pragma string) // // This previously reflected the additional restrictions documented here: // https://github.com/golang/go/issues/59149 -func isValidWasmType(typ types.Type, site wasmSite) bool { +func (c *compilerContext) isValidWasmType(typ types.Type, site wasmSite) bool { switch typ := typ.Underlying().(type) { case *types.Basic: switch typ.Kind() { @@ -461,19 +462,35 @@ func isValidWasmType(typ types.Type, site wasmSite) bool { return site == siteParam || site == siteIndirect } case *types.Array: - return site == siteIndirect && isValidWasmType(typ.Elem(), siteIndirect) + return site == siteIndirect && c.isValidWasmType(typ.Elem(), siteIndirect) case *types.Struct: if site != siteIndirect { return false } + // Structs with no fields do not need structs.HostLayout + if typ.NumFields() == 0 { + return true + } + hasHostLayout := true // default to true before detecting Go version + // (*types.Package).GoVersion added in go1.21 + if gv, ok := any(c.pkg).(interface{ GoVersion() string }); ok { + if goenv.Compare(gv.GoVersion(), "go1.23") >= 0 { + hasHostLayout = false // package structs added in go1.23 + } + } for i := 0; i < typ.NumFields(); i++ { - if !isValidWasmType(typ.Field(i).Type(), siteIndirect) { + ftyp := typ.Field(i).Type() + if ftyp.String() == "structs.HostLayout" { + hasHostLayout = true + continue + } + if !c.isValidWasmType(ftyp, siteIndirect) { return false } } - return true + return hasHostLayout case *types.Pointer: - return isValidWasmType(typ.Elem(), siteIndirect) + return c.isValidWasmType(typ.Elem(), siteIndirect) } return false } From 4ebecef9a89620363db8fe5682a332ca5259fe6f Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Sat, 5 Oct 2024 16:48:24 -0700 Subject: [PATCH 3/4] testdata/wasmexport: use int32 instead of int --- testdata/wasmexport-noscheduler.go | 6 +++--- testdata/wasmexport.go | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/testdata/wasmexport-noscheduler.go b/testdata/wasmexport-noscheduler.go index 844c8d1588..b996b2faa0 100644 --- a/testdata/wasmexport-noscheduler.go +++ b/testdata/wasmexport-noscheduler.go @@ -18,16 +18,16 @@ func hello() { } //go:wasmexport add -func add(a, b int) int { +func add(a, b int32) int32 { println("called add:", a, b) return a + b } //go:wasmimport tester callOutside -func callOutside(a, b int) int +func callOutside(a, b int32) int32 //go:wasmexport reentrantCall -func reentrantCall(a, b int) int { +func reentrantCall(a, b int32) int32 { println("reentrantCall:", a, b) result := callOutside(a, b) println("reentrantCall result:", result) diff --git a/testdata/wasmexport.go b/testdata/wasmexport.go index 9065d6e926..a215761273 100644 --- a/testdata/wasmexport.go +++ b/testdata/wasmexport.go @@ -21,15 +21,15 @@ func hello() { } //go:wasmexport add -func add(a, b int) int { +func add(a, b int32) int32 { println("called add:", a, b) addInputs <- a addInputs <- b return <-addOutput } -var addInputs = make(chan int) -var addOutput = make(chan int) +var addInputs = make(chan int32) +var addOutput = make(chan int32) func adder() { for { @@ -41,10 +41,10 @@ func adder() { } //go:wasmimport tester callOutside -func callOutside(a, b int) int +func callOutside(a, b int32) int32 //go:wasmexport reentrantCall -func reentrantCall(a, b int) int { +func reentrantCall(a, b int32) int32 { println("reentrantCall:", a, b) result := callOutside(a, b) println("reentrantCall result:", result) From 9d2bf5f8153791a0db86b5360a77e27ecf2a6898 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 7 Oct 2024 15:16:34 -0700 Subject: [PATCH 4/4] compiler/testdata: add structs.HostLayout compiler/testdata: improve tests for structs.HostLayout --- compiler/testdata/errors.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/testdata/errors.go b/compiler/testdata/errors.go index 0a76aa07a1..ae95b75234 100644 --- a/compiler/testdata/errors.go +++ b/compiler/testdata/errors.go @@ -1,6 +1,9 @@ package main -import "unsafe" +import ( + "structs" + "unsafe" +) //go:wasmimport modulename empty func empty() @@ -14,6 +17,7 @@ func implementation() { type Uint uint32 type S struct { + _ structs.HostLayout a [4]uint32 b uintptr d float32 @@ -21,7 +25,7 @@ type S struct { } //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, j *[8]uint8) +func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S, j *struct{}, k *[8]uint8) // ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [4]uint32 // ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte @@ -35,6 +39,12 @@ func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintpt //go:wasmimport modulename invalidparam func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func(), f int, g uint, h [8]int) +// ERROR: //go:wasmimport modulename invalidparam_no_hostlayout: unsupported parameter type *struct{int} +// ERROR: //go:wasmimport modulename invalidparam_no_hostlayout: unsupported parameter type *struct{string} +// +//go:wasmimport modulename invalidparam_no_hostlayout +func invalidparam_no_hostlayout(a *struct{ int }, b *struct{ string }) + //go:wasmimport modulename validreturn_int32 func validreturn_int32() int32 @@ -47,6 +57,9 @@ func validreturn_ptr_string() *string //go:wasmimport modulename validreturn_ptr_struct func validreturn_ptr_struct() *S +//go:wasmimport modulename validreturn_ptr_struct +func validreturn_ptr_empty_struct() *struct{} + //go:wasmimport modulename validreturn_ptr_array func validreturn_ptr_array() *[8]uint8