Skip to content

Commit

Permalink
Implement support for memory64 (bytecodealliance#96)
Browse files Browse the repository at this point in the history
* Implement support for memory64

Like the Rust crate this deletes the old `Limits` type in favor of
specific types/methods where relevant.

* Fix bazel build

* Remove `*.dll.a` file for windows
  • Loading branch information
alexcrichton authored Aug 16, 2021
1 parent a7020bf commit 92d338f
Show file tree
Hide file tree
Showing 17 changed files with 144 additions and 89 deletions.
1 change: 0 additions & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ go_library(
"importtype.go",
"instance.go",
"instancetype.go",
"limits.go",
"linker.go",
"maybe_gc_no.go",
"memory.go",
Expand Down
2 changes: 2 additions & 0 deletions ci/download-wasmtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@

for dylib in glob.glob("build/**/*.dll"):
os.remove(dylib)
for dylib in glob.glob("build/**/*.dll.a"):
os.remove(dylib)
for dylib in glob.glob("build/**/*.dylib"):
os.remove(dylib)
for dylib in glob.glob("build/**/*.so"):
Expand Down
6 changes: 6 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ func (cfg *Config) SetWasmMultiMemory(enabled bool) {
runtime.KeepAlive(cfg)
}

// SetWasmMemory64 configures whether the wasm memory64 proposal is enabled
func (cfg *Config) SetWasmMemory64(enabled bool) {
C.wasmtime_config_wasm_memory64_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}

// SetStrategy configures what compilation strategy is used to compile wasm code
func (cfg *Config) SetStrategy(strat Strategy) error {
err := C.wasmtime_config_strategy_set(cfg.ptr(), C.wasmtime_strategy_t(strat))
Expand Down
2 changes: 1 addition & 1 deletion doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func Example_memory() {

// Finally we can also create standalone memories to get imported by
// wasm modules too.
memorytype := NewMemoryType(Limits{Min: 5, Max: 5})
memorytype := NewMemoryType(5, true, 5)
memory2, err := NewMemory(store, memorytype)
assert(err == nil)
assert(memory2.Size(store) == 5)
Expand Down
2 changes: 1 addition & 1 deletion exporttype_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package wasmtime
import "testing"

func TestExportType(t *testing.T) {
et := NewExportType("x", NewMemoryType(Limits{}))
et := NewExportType("x", NewMemoryType(0, false, 0))
if et.Name() != "x" {
panic("bad name")
}
Expand Down
4 changes: 2 additions & 2 deletions instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ func TestInstance(t *testing.T) {
if exports[2].Type(store).TableType().Element().Kind() != KindFuncref {
panic("bad table type")
}
if m.Type(store).Limits().Min != 1 {
if m.Type(store).Minimum() != 1 {
panic("bad memory type")
}
if exports[3].Type(store).MemoryType().Limits().Min != 1 {
if exports[3].Type(store).MemoryType().Minimum() != 1 {
panic("bad memory type")
}
}
Expand Down
45 changes: 0 additions & 45 deletions limits.go

This file was deleted.

2 changes: 1 addition & 1 deletion linker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestLinker(t *testing.T) {
g, err := NewGlobal(store, NewGlobalType(NewValType(KindI32), false), ValI32(0))
assertNoError(err)
assertNoError(linker.Define("", "g", g))
m, err := NewMemory(store, NewMemoryType(Limits{Min: 1, Max: 0xffffffff}))
m, err := NewMemory(store, NewMemoryType(1, true, 300))
assertNoError(err)
assertNoError(linker.Define("", "m", m))
assertNoError(linker.Define("other", "m", m))
Expand Down
12 changes: 6 additions & 6 deletions memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,21 @@ func (mem *Memory) DataSize(store Storelike) uintptr {
}

// Size returns the size, in wasm pages, of this memory
func (mem *Memory) Size(store Storelike) uint32 {
ret := uint32(C.wasmtime_memory_size(store.Context(), &mem.val))
func (mem *Memory) Size(store Storelike) uint64 {
ret := uint64(C.wasmtime_memory_size(store.Context(), &mem.val))
runtime.KeepAlive(store)
return ret
}

// Grow grows this memory by `delta` pages
func (mem *Memory) Grow(store Storelike, delta uint) (uint32, error) {
prev := C.uint32_t(0)
err := C.wasmtime_memory_grow(store.Context(), &mem.val, C.wasm_memory_pages_t(delta), &prev)
func (mem *Memory) Grow(store Storelike, delta uint64) (uint64, error) {
prev := C.uint64_t(0)
err := C.wasmtime_memory_grow(store.Context(), &mem.val, C.uint64_t(delta), &prev)
runtime.KeepAlive(store)
if err != nil {
return 0, mkError(err)
}
return uint32(prev), nil
return uint64(prev), nil
}

func (mem *Memory) AsExtern() C.wasmtime_extern_t {
Expand Down
62 changes: 53 additions & 9 deletions memorytype.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package wasmtime

// #include <wasm.h>
// #include <wasmtime.h>
import "C"
import "runtime"

Expand All @@ -11,10 +11,34 @@ type MemoryType struct {
_owner interface{}
}

// NewMemoryType creates a new `MemoryType` with the `limits` on size provided
func NewMemoryType(limits Limits) *MemoryType {
limitsFFI := limits.ffi()
ptr := C.wasm_memorytype_new(&limitsFFI)
// NewMemoryType creates a new `MemoryType` with the limits on size provided
//
// The `min` value is the minimum size, in WebAssembly pages, of this memory.
// The `has_max` boolean indicates whether a maximum size is present, and if so
// `max` is used as the maximum size of memory, in wasm pages.
//
// Note that this will create a 32-bit memory type, the default outside of the
// memory64 proposal.
func NewMemoryType(min uint32, has_max bool, max uint32) *MemoryType {
if min > (1<<16) || max > (1<<16) {
panic("provided sizes are too large")
}
ptr := C.wasmtime_memorytype_new(C.uint64_t(min), C._Bool(has_max), C.uint64_t(max), false)
return mkMemoryType(ptr, nil)
}

// NewMemoryType64 creates a new 64-bit `MemoryType` with the provided limits
//
// The `min` value is the minimum size, in WebAssembly pages, of this memory.
// The `has_max` boolean indicates whether a maximum size is present, and if so
// `max` is used as the maximum size of memory, in wasm pages.
//
// Note that 64-bit memories are part of the memory64 WebAssembly proposal.
func NewMemoryType64(min uint64, has_max bool, max uint64) *MemoryType {
if min > (1<<48) || max > (1<<48) {
panic("provided sizes are too large")
}
ptr := C.wasmtime_memorytype_new(C.uint64_t(min), C._Bool(has_max), C.uint64_t(max), true)
return mkMemoryType(ptr, nil)
}

Expand All @@ -41,10 +65,30 @@ func (ty *MemoryType) owner() interface{} {
return ty
}

// Limits returns the limits on the size of this memory type
func (ty *MemoryType) Limits() Limits {
ptr := C.wasm_memorytype_limits(ty.ptr())
return mkLimits(ptr, ty.owner())
// Minimum returns the minimum size of this memory, in WebAssembly pages
func (ty *MemoryType) Minimum() uint64 {
ret := C.wasmtime_memorytype_minimum(ty.ptr())
runtime.KeepAlive(ty)
return uint64(ret)
}

// Maximum returns the maximum size of this memory, in WebAssembly pages, if
// specified.
//
// If the maximum size is not specified then `(false, 0)` is returned, otherwise
// `(true, N)` is returned where `N` is the listed maximum size of this memory.
func (ty *MemoryType) Maximum() (bool, uint64) {
size := C.uint64_t(0)
present := C.wasmtime_memorytype_maximum(ty.ptr(), &size)
runtime.KeepAlive(ty)
return bool(present), uint64(size)
}

// Is64 returns whether this is a 64-bit memory or not.
func (ty *MemoryType) Is64() bool {
ok := C.wasmtime_memorytype_is64(ty.ptr())
runtime.KeepAlive(ty)
return bool(ok)
}

// AsExternType converts this type to an instance of `ExternType`
Expand Down
16 changes: 14 additions & 2 deletions memorytype_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package wasmtime
import "testing"

func TestMemoryType(t *testing.T) {
ty := NewMemoryType(Limits{Min: 0, Max: 100})
ty.Limits()
ty := NewMemoryType(0, true, 100)
ty.Minimum()
ty.Maximum()

ty2 := ty.AsExternType().MemoryType()
if ty2 == nil {
Expand All @@ -20,3 +21,14 @@ func TestMemoryType(t *testing.T) {
panic("working cast")
}
}

func TestMemoryType64(t *testing.T) {
ty := NewMemoryType64(0x100000000, true, 0x100000001)
if ty.Minimum() != 0x100000000 {
panic("bad limits")
}
present, max := ty.Maximum()
if !present || max != 0x100000001 {
panic("bad limits")
}
}
4 changes: 2 additions & 2 deletions module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestModuleImports(t *testing.T) {
if imports[3].Type().MemoryType() == nil {
panic("wrong import type")
}
if imports[3].Type().MemoryType().Limits().Min != 1 {
if imports[3].Type().MemoryType().Minimum() != 1 {
panic("wrong import type")
}
}
Expand Down Expand Up @@ -164,7 +164,7 @@ func TestModuleExports(t *testing.T) {
if exports[3].Type().MemoryType() == nil {
panic("wrong export type")
}
if exports[3].Type().MemoryType().Limits().Min != 1 {
if exports[3].Type().MemoryType().Minimum() != 1 {
panic("wrong export type")
}
}
Expand Down
16 changes: 12 additions & 4 deletions multi_memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,21 @@ func TestMultiMemoryExported(t *testing.T) {
if exports[0].Type().MemoryType() == nil {
panic("wrong export type")
}
if (exports[0].Type().MemoryType().Limits() != Limits{Min: 2, Max: 3}) {
if exports[0].Type().MemoryType().Minimum() != 2 {
panic("wrong memory limits")
}
present, max := exports[0].Type().MemoryType().Maximum()
if !present || max != 3 {
panic("wrong memory limits")
}
if exports[1].Type().MemoryType() == nil {
panic("wrong export type")
}
if (exports[1].Type().MemoryType().Limits() != Limits{Min: 2, Max: 4}) {
if exports[1].Type().MemoryType().Minimum() != 2 {
panic("wrong memory limits")
}
present, max = exports[1].Type().MemoryType().Maximum()
if !present || max != 4 {
panic("wrong memory limits")
}

Expand All @@ -63,11 +71,11 @@ func TestMultiMemoryImported(t *testing.T) {
}
store := multiMemoryStore()

mem0, err := NewMemory(store, NewMemoryType(Limits{Min: 1, Max: 3}))
mem0, err := NewMemory(store, NewMemoryType(1, true, 3))
if err != nil {
panic(err)
}
mem1, err := NewMemory(store, NewMemoryType(Limits{Min: 2, Max: 4}))
mem1, err := NewMemory(store, NewMemoryType(2, true, 4))
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion reftypes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestRefTypesTable(t *testing.T) {
store := refTypesStore()
table, err := NewTable(
store,
NewTableType(NewValType(KindExternref), Limits{Min: 10, Max: LimitsMaxNone}),
NewTableType(NewValType(KindExternref), 10, false, 0),
ValExternref("init"),
)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "testing"

func TestTable(t *testing.T) {
store := NewStore(NewEngine())
ty := NewTableType(NewValType(KindFuncref), Limits{Min: 1, Max: 3})
ty := NewTableType(NewValType(KindFuncref), 1, true, 3)
table, err := NewTable(store, ty, ValFuncref(nil))
if err != nil {
panic(err)
Expand Down
41 changes: 34 additions & 7 deletions tabletype.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@ type TableType struct {
_owner interface{}
}

// NewTableType creates a new `TableType` with the `element` type provided as well as
// `limits` on its size.
func NewTableType(element *ValType, limits Limits) *TableType {
// NewTableType creates a new `TableType` with the `element` type provided as
// well as limits on its size.
//
// The `min` value is the minimum size, in elements, of this table. The
// `has_max` boolean indicates whether a maximum size is present, and if so
// `max` is used as the maximum size of the table, in elements.
func NewTableType(element *ValType, min uint32, has_max bool, max uint32) *TableType {
valptr := C.wasm_valtype_new(C.wasm_valtype_kind(element.ptr()))
runtime.KeepAlive(element)
limitsFFI := limits.ffi()
if !has_max {
max = 0xffffffff
}
limitsFFI := C.wasm_limits_t{
min: C.uint32_t(min),
max: C.uint32_t(max),
}
ptr := C.wasm_tabletype_new(valptr, &limitsFFI)

return mkTableType(ptr, nil)
Expand Down Expand Up @@ -50,10 +60,27 @@ func (ty *TableType) Element() *ValType {
return mkValType(ptr, ty.owner())
}

// Limits returns limits on the size of this table type
func (ty *TableType) Limits() Limits {
// Minimum returns the minimum size, in elements, of this table.
func (ty *TableType) Minimum() uint32 {
ptr := C.wasm_tabletype_limits(ty.ptr())
return mkLimits(ptr, ty.owner())
ret := uint32(ptr.min)
runtime.KeepAlive(ty)
return ret
}

// Maximum returns the maximum size, in elements, of this table.
//
// If no maximum size is listed then `(false, 0)` is returned, otherwise
// `(true, N)` is returned where `N` is the maximum size.
func (ty *TableType) Maximum() (bool, uint32) {
ptr := C.wasm_tabletype_limits(ty.ptr())
ret := uint32(ptr.max)
runtime.KeepAlive(ty)
if ret == 0xffffffff {
return false, 0
} else {
return true, ret
}
}

// AsExternType converts this type to an instance of `ExternType`
Expand Down
Loading

0 comments on commit 92d338f

Please sign in to comment.