From b2f6694a0facee23e4f94a2f0ad2ec2d0c1f4fc0 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 19 May 2023 15:34:05 +0200 Subject: [PATCH] compiler: disallow most types in //go:wasmimport This is for compatibility with upstream Go. See https://github.com/golang/go/issues/59149 for more context. --- compiler/symbol.go | 47 ++++++++++++++++++++++++++++++++++--- compiler/testdata/errors.go | 22 +++++++++++++++++ compiler/testdata/pragma.go | 4 ---- compiler/testdata/pragma.ll | 6 ----- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/compiler/symbol.go b/compiler/symbol.go index 5662b0c5e6..ad8db09b86 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -4,6 +4,7 @@ package compiler // pragmas, determines the link name, etc. import ( + "fmt" "go/ast" "go/token" "go/types" @@ -254,7 +255,7 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { // parsePragmas is used by getFunctionInfo to parse function pragmas such as // //export or //go:noinline. -func (info *functionInfo) parsePragmas(f *ssa.Function) { +func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) { if f.Syntax() == nil { return } @@ -294,10 +295,12 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { info.module = parts[1] case "//go:wasmimport": // Import a WebAssembly function, for example a WASI function. - // For details, see: https://github.com/golang/go/issues/38248 - if len(parts) != 3 || len(f.Blocks) != 0 { + // Original proposal: https://github.com/golang/go/issues/38248 + // Allow globally: https://github.com/golang/go/issues/59149 + if len(parts) != 3 { continue } + c.checkWasmImport(f, comment.Text) info.exported = true info.module = parts[1] info.importName = parts[2] @@ -358,6 +361,44 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { } } +// Check whether this function cannot be used in //go:wasmimport. It will add an +// error if this is the case. +// +// 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" { + // The runtime is a special case. Allow all kinds of parameters + // (importantly, including pointers). + return + } + if f.Blocks != nil { + // Defined functions cannot be exported. + c.addError(f.Pos(), fmt.Sprintf("can only use //go:wasmimport on declarations")) + return + } + for _, param := range f.Params { + typ := param.Type().Underlying() + // Check whether the type is allowed. + // Only a very limited number of types can be mapped to WebAssembly. + allowedType := false + switch typ := typ.(type) { + case *types.Basic: + switch typ.Kind() { + case types.Int32, types.Uint32, types.Int64, types.Uint64: + allowedType = true + case types.Float32, types.Float64: + allowedType = true + case types.UnsafePointer: + allowedType = true + } + } + if !allowedType { + c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String())) + } + } +} + // 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. diff --git a/compiler/testdata/errors.go b/compiler/testdata/errors.go index 06ab7d0f9a..f7fc433594 100644 --- a/compiler/testdata/errors.go +++ b/compiler/testdata/errors.go @@ -1 +1,23 @@ package main + +import "unsafe" + +//go:wasmimport modulename empty +func empty() + +// ERROR: can only use //go:wasmimport on declarations +// +//go:wasmimport modulename implementation +func implementation() { +} + +//go:wasmimport modulename validparam +func validparam(a int32, b uint64, c float64, d unsafe.Pointer) + +// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int +// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type string +// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte +// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type *int32 +// +//go:wasmimport modulename invalidparam +func invalidparam(a int, b string, c []byte, d *int32) diff --git a/compiler/testdata/pragma.go b/compiler/testdata/pragma.go index 0ff9688781..0a4be11aea 100644 --- a/compiler/testdata/pragma.go +++ b/compiler/testdata/pragma.go @@ -62,10 +62,6 @@ func exportedFunctionInSection() { //go:wasmimport modulename import1 func declaredImport() -//go:wasmimport modulename import2 -func definedImport() { -} - // This function should not: it's only a declaration and not a definition. // //go:section .special_function_section diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index f2a279a0cd..78cc29457d 100644 --- a/compiler/testdata/pragma.ll +++ b/compiler/testdata/pragma.ll @@ -62,12 +62,6 @@ entry: declare void @main.declaredImport() #7 -; Function Attrs: nounwind -define hidden void @main.definedImport(ptr %context) unnamed_addr #2 { -entry: - ret void -} - declare void @main.undefinedFunctionNotInSection(ptr) #1 attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }