Skip to content

Commit 5a9aeef

Browse files
committed
cmd/compile: allow more types for wasmimport/wasmexport parameters and results
As proposed on #66984, this CL allows more types to be used as wasmimport/wasmexport function parameters and results. Specifically, bool, string, and uintptr are now allowed, and also pointer types that point to allowed element types. Allowed element types includes sized integer and floating point types (including small integer types like uint8 which are not directly allowed as a parameter type), bool, array whose element type is allowed, and struct whose fields are allowed element type and also include a struct.HostLayout field. For #66984. Change-Id: Ie5452a1eda21c089780dfb4d4246de6008655c84 Reviewed-on: https://go-review.googlesource.com/c/go/+/626615 Reviewed-by: David Chase <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 583d750 commit 5a9aeef

File tree

5 files changed

+127
-31
lines changed

5 files changed

+127
-31
lines changed

src/cmd/compile/internal/ssagen/abi.go

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -412,24 +412,39 @@ func GenWasmExportWrapper(wrapped *ir.Func) {
412412
}
413413

414414
func paramsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
415-
wfs := make([]obj.WasmField, len(abiParams))
416-
for i, p := range abiParams {
415+
wfs := make([]obj.WasmField, 0, len(abiParams))
416+
for _, p := range abiParams {
417417
t := p.Type
418+
var wt obj.WasmFieldType
418419
switch t.Kind() {
419420
case types.TINT32, types.TUINT32:
420-
wfs[i].Type = obj.WasmI32
421+
wt = obj.WasmI32
421422
case types.TINT64, types.TUINT64:
422-
wfs[i].Type = obj.WasmI64
423+
wt = obj.WasmI64
423424
case types.TFLOAT32:
424-
wfs[i].Type = obj.WasmF32
425+
wt = obj.WasmF32
425426
case types.TFLOAT64:
426-
wfs[i].Type = obj.WasmF64
427-
case types.TUNSAFEPTR:
428-
wfs[i].Type = obj.WasmPtr
427+
wt = obj.WasmF64
428+
case types.TUNSAFEPTR, types.TUINTPTR:
429+
wt = obj.WasmPtr
430+
case types.TBOOL:
431+
wt = obj.WasmBool
432+
case types.TSTRING:
433+
// Two parts, (ptr, len)
434+
wt = obj.WasmPtr
435+
wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
436+
wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result) + int64(types.PtrSize)})
437+
continue
438+
case types.TPTR:
439+
if wasmElemTypeAllowed(t.Elem()) {
440+
wt = obj.WasmPtr
441+
break
442+
}
443+
fallthrough
429444
default:
430445
base.ErrorfAt(f.Pos(), 0, "%s: unsupported parameter type %s", pragma, t.String())
431446
}
432-
wfs[i].Offset = p.FrameOffset(result)
447+
wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
433448
}
434449
return wfs
435450
}
@@ -451,8 +466,16 @@ func resultsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultIn
451466
wfs[i].Type = obj.WasmF32
452467
case types.TFLOAT64:
453468
wfs[i].Type = obj.WasmF64
454-
case types.TUNSAFEPTR:
469+
case types.TUNSAFEPTR, types.TUINTPTR:
455470
wfs[i].Type = obj.WasmPtr
471+
case types.TBOOL:
472+
wfs[i].Type = obj.WasmBool
473+
case types.TPTR:
474+
if wasmElemTypeAllowed(t.Elem()) {
475+
wfs[i].Type = obj.WasmPtr
476+
break
477+
}
478+
fallthrough
456479
default:
457480
base.ErrorfAt(f.Pos(), 0, "%s: unsupported result type %s", pragma, t.String())
458481
}
@@ -461,6 +484,40 @@ func resultsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultIn
461484
return wfs
462485
}
463486

487+
// wasmElemTypeAllowed reports whether t is allowed to be passed in memory
488+
// (as a pointer's element type, a field of it, etc.) between the Go wasm
489+
// module and the host.
490+
func wasmElemTypeAllowed(t *types.Type) bool {
491+
switch t.Kind() {
492+
case types.TINT8, types.TUINT8, types.TINT16, types.TUINT16,
493+
types.TINT32, types.TUINT32, types.TINT64, types.TUINT64,
494+
types.TFLOAT32, types.TFLOAT64, types.TBOOL:
495+
return true
496+
case types.TARRAY:
497+
return wasmElemTypeAllowed(t.Elem())
498+
case types.TSTRUCT:
499+
if len(t.Fields()) == 0 {
500+
return true
501+
}
502+
seenHostLayout := false
503+
for _, f := range t.Fields() {
504+
sym := f.Type.Sym()
505+
if sym != nil && sym.Name == "HostLayout" && sym.Pkg.Path == "structs" {
506+
seenHostLayout = true
507+
continue
508+
}
509+
if !wasmElemTypeAllowed(f.Type) {
510+
return false
511+
}
512+
}
513+
return seenHostLayout
514+
}
515+
// Pointer, and all pointerful types are not allowed, as pointers have
516+
// different width on the Go side and the host side. (It will be allowed
517+
// on GOARCH=wasm32.)
518+
return false
519+
}
520+
464521
// setupWasmImport calculates the params and results in terms of WebAssembly values for the given function,
465522
// and sets up the wasmimport metadata.
466523
func setupWasmImport(f *ir.Func) {

src/cmd/internal/obj/link.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,10 @@ const (
762762
WasmF32
763763
WasmF64
764764
WasmPtr
765+
766+
// bool is not really a wasm type, but we allow it on wasmimport/wasmexport
767+
// function parameters/results. 32-bit on Wasm side, 8-bit on Go side.
768+
WasmBool
765769
)
766770

767771
type InlMark struct {

src/cmd/internal/obj/wasm/wasmobj.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -853,8 +853,9 @@ func genWasmImportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args
853853
case obj.WasmF64:
854854
p = appendp(p, AF64Load, constAddr(loadOffset))
855855
case obj.WasmPtr:
856-
p = appendp(p, AI64Load, constAddr(loadOffset))
857-
p = appendp(p, AI32WrapI64)
856+
p = appendp(p, AI32Load, constAddr(loadOffset))
857+
case obj.WasmBool:
858+
p = appendp(p, AI32Load8U, constAddr(loadOffset))
858859
default:
859860
panic("bad param type")
860861
}
@@ -906,6 +907,12 @@ func genWasmImportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args
906907
p = appendp(p, AGet, regAddr(REG_SP))
907908
p = appendp(p, AGet, regAddr(REG_R0))
908909
p = appendp(p, AI64Store, constAddr(storeOffset))
910+
case obj.WasmBool:
911+
p = appendp(p, AI64ExtendI32U)
912+
p = appendp(p, ASet, regAddr(REG_R0))
913+
p = appendp(p, AGet, regAddr(REG_SP))
914+
p = appendp(p, AGet, regAddr(REG_R0))
915+
p = appendp(p, AI64Store8, constAddr(storeOffset))
909916
default:
910917
panic("bad result type")
911918
}
@@ -944,6 +951,8 @@ func genWasmExportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args
944951
case obj.WasmPtr:
945952
p = appendp(p, AI64ExtendI32U)
946953
p = appendp(p, AI64Store, constAddr(f.Offset))
954+
case obj.WasmBool:
955+
p = appendp(p, AI32Store8, constAddr(f.Offset))
947956
default:
948957
panic("bad param type")
949958
}
@@ -996,8 +1005,9 @@ func genWasmExportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args
9961005
case obj.WasmF64:
9971006
p = appendp(p, AF64Load, constAddr(f.Offset))
9981007
case obj.WasmPtr:
999-
p = appendp(p, AI64Load, constAddr(f.Offset))
1000-
p = appendp(p, AI32WrapI64)
1008+
p = appendp(p, AI32Load, constAddr(f.Offset))
1009+
case obj.WasmBool:
1010+
p = appendp(p, AI32Load8U, constAddr(f.Offset))
10011011
default:
10021012
panic("bad result type")
10031013
}

src/cmd/link/internal/wasm/asm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ func fieldsToTypes(fields []obj.WasmField) []byte {
689689
b := make([]byte, len(fields))
690690
for i, f := range fields {
691691
switch f.Type {
692-
case obj.WasmI32, obj.WasmPtr:
692+
case obj.WasmI32, obj.WasmPtr, obj.WasmBool:
693693
b[i] = I32
694694
case obj.WasmI64:
695695
b[i] = I64

test/wasmexport2.go

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111

1212
package p
1313

14-
import "unsafe"
14+
import (
15+
"structs"
16+
"unsafe"
17+
)
1518

1619
//go:wasmexport good1
1720
func good1(int32, uint32, int64, uint64, float32, float64, unsafe.Pointer) {} // allowed types
@@ -27,41 +30,63 @@ func good3() int32 { return 0 } // one result is ok
2730
//go:wasmexport good4
2831
func good4() unsafe.Pointer { return nil } // one result is ok
2932

33+
//go:wasmexport good5
34+
func good5(string, uintptr) bool { return false } // bool, string, and uintptr are allowed
35+
3036
//go:wasmexport bad1
31-
func bad1(string) {} // ERROR "go:wasmexport: unsupported parameter type"
37+
func bad1(any) {} // ERROR "go:wasmexport: unsupported parameter type"
3238

3339
//go:wasmexport bad2
34-
func bad2(any) {} // ERROR "go:wasmexport: unsupported parameter type"
40+
func bad2(func()) {} // ERROR "go:wasmexport: unsupported parameter type"
3541

3642
//go:wasmexport bad3
37-
func bad3(func()) {} // ERROR "go:wasmexport: unsupported parameter type"
43+
func bad3(uint8) {} // ERROR "go:wasmexport: unsupported parameter type"
3844

3945
//go:wasmexport bad4
40-
func bad4(uint8) {} // ERROR "go:wasmexport: unsupported parameter type"
46+
func bad4(int) {} // ERROR "go:wasmexport: unsupported parameter type"
4147

42-
// Pointer types are not allowed, except unsafe.Pointer.
4348
// Struct and array types are also not allowed.
44-
// If proposal 66984 is accepted and implemented, we may allow them.
45-
46-
//go:wasmexport bad5
47-
func bad5(*int32) {} // ERROR "go:wasmexport: unsupported parameter type"
4849

4950
type S struct { x, y int32 }
5051

52+
type H struct { _ structs.HostLayout; x, y int32 }
53+
54+
type A = structs.HostLayout
55+
56+
type AH struct { _ A; x, y int32 }
57+
58+
//go:wasmexport bad5
59+
func bad5(S) {} // ERROR "go:wasmexport: unsupported parameter type"
60+
5161
//go:wasmexport bad6
52-
func bad6(S) {} // ERROR "go:wasmexport: unsupported parameter type"
62+
func bad6(H) {} // ERROR "go:wasmexport: unsupported parameter type"
5363

5464
//go:wasmexport bad7
55-
func bad7(*S) {} // ERROR "go:wasmexport: unsupported parameter type"
65+
func bad7([4]int32) {} // ERROR "go:wasmexport: unsupported parameter type"
66+
67+
// Pointer types are not allowed, with resitrictions on
68+
// the element type.
69+
70+
//go:wasmexport good6
71+
func good6(*int32, *uint8, *bool) {}
5672

5773
//go:wasmexport bad8
58-
func bad8([4]int32) {} // ERROR "go:wasmexport: unsupported parameter type"
74+
func bad8(*S) {} // ERROR "go:wasmexport: unsupported parameter type" // without HostLayout, not allowed
5975

6076
//go:wasmexport bad9
61-
func bad9() bool { return false } // ERROR "go:wasmexport: unsupported result type"
77+
func bad9() *S { return nil } // ERROR "go:wasmexport: unsupported result type"
6278

63-
//go:wasmexport bad10
64-
func bad10() *byte { return nil } // ERROR "go:wasmexport: unsupported result type"
79+
//go:wasmexport good7
80+
func good7(*H, *AH) {} // pointer to struct with HostLayout is allowed
81+
82+
//go:wasmexport good8
83+
func good8(*struct{}) {} // pointer to empty struct is allowed
84+
85+
//go:wasmexport good9
86+
func good9(*[4]int32, *[2]H) {} // pointer to array is allowed, if the element type is okay
6587

6688
//go:wasmexport toomanyresults
6789
func toomanyresults() (int32, int32) { return 0, 0 } // ERROR "go:wasmexport: too many return values"
90+
91+
//go:wasmexport bad10
92+
func bad10() string { return "" } // ERROR "go:wasmexport: unsupported result type" // string cannot be a result

0 commit comments

Comments
 (0)