wasm: use wasm pages for wasm memory

The memory.grow and memory.size wasm methods return
the memory size in pages, and memory.size takes its
argument in the number of pages. A WebAssembly page
has a size of 64KiB, so during memory allocation
we have to divide our desired size in bytes by page
size and round up. Similarly, when reading memory
size we need to multiply the result by 64KiB to
get the size in bytes.

The change affects current naive allocator for
arguments when calling wasm UDFs and the examples
in wasm_test.py - both commented code and compiled
wasm in text representation.

Signed-off-by: Wojciech Mitros <wojciech.mitros@scylladb.com>
This commit is contained in:
Wojciech Mitros
2022-03-17 01:18:21 +01:00
parent 0c3d4091a4
commit 62761a7cf3
2 changed files with 267 additions and 317 deletions

View File

@@ -25,6 +25,8 @@ namespace wasm {
context::context(wasm::engine* engine_ptr, std::string name) : engine_ptr(engine_ptr), function_name(name) {
}
static constexpr size_t WASM_PAGE_SIZE = 64 * 1024;
static std::pair<wasmtime::Instance, wasmtime::Func> create_instance_and_func(context& ctx, wasmtime::Store& store) {
auto instance_res = wasmtime::Instance::create(store, *ctx.module, {});
if (!instance_res) {
@@ -105,7 +107,7 @@ struct init_arg_visitor {
}
auto memory = std::get<wasmtime::Memory>(*memory_export);
uint8_t* data = memory.data(store).data();
size_t mem_size = memory.size(store);
size_t mem_size = memory.size(store) * WASM_PAGE_SIZE;
if (!param) {
on_internal_error(wasm_logger, "init_arg_visitor does not accept null values");
}
@@ -113,7 +115,7 @@ struct init_arg_visitor {
if (serialized_size > std::numeric_limits<int32_t>::max()) {
throw wasm::exception(format("Serialized parameter is too large: {} > {}", serialized_size, std::numeric_limits<int32_t>::max()));
}
auto grown = memory.grow(store, sizeof(int32_t) + serialized_size); // for fitting serialized size + the buffer itself
auto grown = memory.grow(store, 1 + (sizeof(int32_t) + serialized_size - 1) / WASM_PAGE_SIZE); // for fitting serialized size + the buffer itself
if (!grown) {
throw wasm::exception(format("Failed to grow wasm memory to {}: {}", serialized_size, grown.err().message()));
}
@@ -142,12 +144,12 @@ struct init_nullable_arg_visitor {
}
auto memory = std::get<wasmtime::Memory>(*memory_export);
uint8_t* data = memory.data(store).data();
size_t mem_size = memory.size(store);
size_t mem_size = memory.size(store) * WASM_PAGE_SIZE;
const int32_t serialized_size = param ? param->size() : 0;
if (serialized_size > std::numeric_limits<int32_t>::max()) {
throw wasm::exception(format("Serialized parameter is too large: {} > {}", param->size(), std::numeric_limits<int32_t>::max()));
}
auto grown = memory.grow(store, sizeof(int32_t) + serialized_size); // for fitting the serialized size + the buffer itself
auto grown = memory.grow(store, 1 + (sizeof(int32_t) + serialized_size - 1) / WASM_PAGE_SIZE); // for fitting the serialized size + the buffer itself
if (!grown) {
throw wasm::exception(format("Failed to grow wasm memory to {}: {}", serialized_size, grown.err().message()));
}

View File

@@ -81,6 +81,8 @@ def test_fib(cql, test_keyspace, table1, scylla_with_wasm_only):
# WebAssembly should convert to host endianness (assumed little endian here)
# before operating on its native types.
# Compiled from:
# const int WASM_PAGE_SIZE = 64 * 1024;
#
# struct __attribute__((packed)) nullable_bigint {
# int size;
# long long v;
@@ -99,10 +101,10 @@ def test_fib(cql, test_keyspace, table1, scylla_with_wasm_only):
# return fib_aux(n-1) + fib_aux(n-2);
# }
#
# struct nullable_bigint* fib(struct nullable_bigint* p) {
# int fib(struct nullable_bigint* p) {
# // Initialize memory for the return struct
# struct nullable_bigint* ret = (struct nullable_bigint*)__builtin_wasm_memory_size(0);
# __builtin_wasm_memory_grow(0, sizeof(struct nullable_bigint));
# struct nullable_bigint* ret = (struct nullable_bigint*)(__builtin_wasm_memory_size(0) * WASM_PAGE_SIZE);
# __builtin_wasm_memory_grow(0, 1 + (sizeof(struct nullable_bigint) - 1) / WASM_PAGE_SIZE); // round up
#
# ret->size = sizeof(long long);
# if (p->size == -1) {
@@ -110,7 +112,7 @@ def test_fib(cql, test_keyspace, table1, scylla_with_wasm_only):
# } else {
# ret->v = swap_int64(fib_aux(swap_int64(p->v)));
# }
# return ret;
# return (int)ret;
# }
#
# with:
@@ -124,26 +126,22 @@ def test_fib_called_on_null(cql, test_keyspace, table1, scylla_with_wasm_only):
(type (;0;) (func))
(type (;1;) (func (param i64) (result i64)))
(type (;2;) (func (param i32) (result i32)))
(func $__wasm_call_ctors (type 0))
(func $fib_aux (type 1) (param i64) (result i64)
(local i64 i32 i64)
block ;; label = @1
(func (;0;) (type 0)
nop)
(func (;1;) (type 1) (param i64) (result i64)
(local i64 i32)
local.get 0
i64.const 2
i64.lt_s
if ;; label = @1
local.get 0
i64.const 2
i64.ge_s
br_if 0 (;@1;)
local.get 0
i64.const 0
i64.add
return
end
i64.const 0
local.set 1
loop ;; label = @1
local.get 0
i64.const -1
i64.add
call $fib_aux
i64.const 1
i64.sub
call 1
local.get 1
i64.add
local.set 1
@@ -152,140 +150,141 @@ def test_fib_called_on_null(cql, test_keyspace, table1, scylla_with_wasm_only):
i64.gt_s
local.set 2
local.get 0
i64.const -2
i64.add
local.tee 3
i64.const 2
i64.sub
local.set 0
local.get 2
br_if 0 (;@1;)
end
local.get 3
local.get 0
local.get 1
i64.add)
(func $fib (type 2) (param i32) (result i32)
(local i32 i64)
(func (;2;) (type 2) (param i32) (result i32)
(local i64 i32)
memory.size
local.set 1
i32.const 12
local.set 2
i32.const 1
memory.grow
drop
local.get 1
local.get 2
i32.const 16
i32.shl
local.tee 2
i32.const 8
i32.store align=1
block ;; label = @1
local.get 0
i32.load align=1
i32.const -1
i32.ne
br_if 0 (;@1;)
local.get 1
i32.store
local.get 0
i32.load align=1
i32.const -1
i32.eq
if ;; label = @1
local.get 2
i64.const 3026418949592973312
i64.store offset=4 align=1
local.get 1
i64.store offset=4 align=4
local.get 2
return
end
local.get 1
local.get 2
local.get 0
i64.load offset=4 align=1
local.tee 2
local.tee 1
i64.const 56
i64.shl
local.get 2
local.get 1
i64.const 40
i64.shl
i64.const 71776119061217280
i64.and
i64.or
local.get 2
local.get 1
i64.const 24
i64.shl
i64.const 280375465082880
i64.and
local.get 2
local.get 1
i64.const 8
i64.shl
i64.const 1095216660480
i64.and
i64.or
i64.or
local.get 2
local.get 1
i64.const 8
i64.shr_u
i64.const 4278190080
i64.and
local.get 2
local.get 1
i64.const 24
i64.shr_u
i64.const 16711680
i64.and
i64.or
local.get 2
local.get 1
i64.const 40
i64.shr_u
i64.const 65280
i64.and
local.get 2
local.get 1
i64.const 56
i64.shr_u
i64.or
i64.or
i64.or
call $fib_aux
local.tee 2
call 1
local.tee 1
i64.const 56
i64.shl
local.get 2
local.get 1
i64.const 40
i64.shl
i64.const 71776119061217280
i64.and
i64.or
local.get 2
local.get 1
i64.const 24
i64.shl
i64.const 280375465082880
i64.and
local.get 2
local.get 1
i64.const 8
i64.shl
i64.const 1095216660480
i64.and
i64.or
i64.or
local.get 2
local.get 1
i64.const 8
i64.shr_u
i64.const 4278190080
i64.and
local.get 2
local.get 1
i64.const 24
i64.shr_u
i64.const 16711680
i64.and
i64.or
local.get 2
local.get 1
i64.const 40
i64.shr_u
i64.const 65280
i64.and
local.get 2
local.get 1
i64.const 56
i64.shr_u
i64.or
i64.or
i64.or
i64.store offset=4 align=1
local.get 1)
i64.store offset=4 align=4
local.get 2)
(memory (;0;) 2)
(global (;0;) (mut i32) (i32.const 66560))
(global (;0;) i32 (i32.const 1024))
(global (;1;) i32 (i32.const 1024))
(global (;2;) i32 (i32.const 1024))
(global (;2;) i32 (i32.const 1028))
(global (;3;) i32 (i32.const 1024))
(global (;4;) i32 (i32.const 66560))
(global (;4;) i32 (i32.const 66576))
(global (;5;) i32 (i32.const 0))
(global (;6;) i32 (i32.const 1))
(export "memory" (memory 0))
(export "{fib_name}" (func $fib)))
(export "{fib_name}" (func 2)))
"""
src = f"(input bigint) CALLED ON NULL INPUT RETURNS bigint LANGUAGE xwasm AS '{fib_source}'"
with new_function(cql, test_keyspace, src, fib_name):
@@ -631,28 +630,27 @@ def test_validate_params(cql, test_keyspace, table1, scylla_with_wasm_only):
# Test that calling a wasm-based function on a string works.
# The function doubles the string: dog -> dogdog.
# Created with:
# const int WASM_PAGE_SIZE = 64 * 1024;
# struct __attribute__((packed)) param {
# int size;
# char buf[0];
#};
#
# int dbl(struct param* par, int ret_pos) {
# };
# int dbl(struct param* par) {
# int size = par->size;
# int position = (int)par->buf;
# int orig_size = __builtin_wasm_memory_size(0);
# __builtin_wasm_memory_grow(0, 2*size);
# int orig_size = __builtin_wasm_memory_size(0) * WASM_PAGE_SIZE;
# __builtin_wasm_memory_grow(0, 1 + (2 * size - 1) / WASM_PAGE_SIZE);
# char* p = (char*)0;
# for (int i = 0; i < size; ++i) {
# p[orig_size + i] = p[position + i];
# p[orig_size + size + i] = p[position + i];
# }
# int ret_val = 2*size;
# char* ret = (char*)ret_pos;
# for (int i = 0; i < 4; ++i) {
# *ret = (char)(ret_val >> 8*(3-i));
# p[orig_size + sizeof(int) + i] = p[position + i];
# p[orig_size + size + sizeof(int) + i] = p[position + i];
# }
# int* ret = (int*)orig_size;
# *ret = 2*size;
# return orig_size;
#}
# }
# ... and compiled with
# clang --target=wasm32 --no-standard-libraries -Wl,--export-all -Wl,--no-entry demo.c -o demo.wasm
# wasm2wat demo.wasm > demo.wat
@@ -666,10 +664,10 @@ def test_word_double(cql, test_keyspace, table1, scylla_with_wasm_only):
(type (;1;) (func (param i32) (result i32)))
(func $__wasm_call_ctors (type 0))
(func $dbl (type 1) (param i32) (result i32)
(local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
global.get 0
(local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
global.get $__stack_pointer
local.set 1
i32.const 48
i32.const 32
local.set 2
local.get 1
local.get 2
@@ -677,18 +675,18 @@ def test_word_double(cql, test_keyspace, table1, scylla_with_wasm_only):
local.set 3
local.get 3
local.get 0
i32.store offset=44
i32.store offset=28
local.get 3
i32.load offset=44
i32.load offset=28
local.set 4
local.get 4
i32.load
i32.load align=1
local.set 5
local.get 3
local.get 5
i32.store offset=40
i32.store offset=24
local.get 3
i32.load offset=44
i32.load offset=28
local.set 6
i32.const 4
local.set 7
@@ -698,282 +696,232 @@ def test_word_double(cql, test_keyspace, table1, scylla_with_wasm_only):
local.set 8
local.get 3
local.get 8
i32.store offset=36
i32.store offset=20
memory.size
local.set 9
local.get 3
local.get 9
i32.store offset=32
local.get 3
i32.load offset=32
i32.const 16
local.set 10
i32.const 4
local.set 11
local.get 9
local.get 10
local.get 11
i32.add
local.set 12
local.get 3
local.get 12
i32.store offset=28
local.get 3
i32.load offset=40
local.set 13
i32.const 1
local.set 14
local.get 13
local.get 14
i32.shl
local.set 11
local.get 3
local.get 11
i32.store offset=16
local.get 3
i32.load offset=24
local.set 12
i32.const 1
local.set 13
local.get 12
local.get 13
i32.shl
local.set 14
i32.const 1
local.set 15
i32.const 4
local.set 16
local.get 14
local.get 15
local.get 16
i32.add
i32.sub
local.set 16
i32.const 65536
local.set 17
local.get 16
local.get 17
i32.div_s
local.set 18
i32.const 1
local.set 19
local.get 18
local.get 19
i32.add
local.set 20
local.get 20
memory.grow
drop
i32.const 0
local.set 18
local.set 21
local.get 3
local.get 18
i32.store offset=24
i32.const 0
local.set 19
local.get 3
local.get 19
i32.store offset=20
block ;; label = @1
loop ;; label = @2
local.get 3
i32.load offset=20
local.set 20
local.get 3
i32.load offset=40
local.set 21
local.get 20
local.set 22
local.get 21
local.set 23
local.get 22
local.get 23
i32.lt_s
local.set 24
i32.const 1
local.set 25
local.get 24
local.get 25
i32.and
local.set 26
local.get 26
i32.eqz
br_if 1 (;@1;)
local.get 3
i32.load offset=24
local.set 27
local.get 3
i32.load offset=36
local.set 28
local.get 3
i32.load offset=20
local.set 29
local.get 28
local.get 29
i32.add
local.set 30
local.get 27
local.get 30
i32.add
local.set 31
local.get 31
i32.load8_u
local.set 32
local.get 3
i32.load offset=24
local.set 33
local.get 3
i32.load offset=28
local.set 34
local.get 3
i32.load offset=20
local.set 35
local.get 34
local.get 35
i32.add
local.set 36
local.get 33
local.get 36
i32.add
local.set 37
local.get 37
local.get 32
i32.store8
local.get 3
i32.load offset=24
local.set 38
local.get 3
i32.load offset=36
local.set 39
local.get 3
i32.load offset=20
local.set 40
local.get 39
local.get 40
i32.add
local.set 41
local.get 38
local.get 41
i32.add
local.set 42
local.get 42
i32.load8_u
local.set 43
local.get 3
i32.load offset=24
local.set 44
local.get 3
i32.load offset=28
local.set 45
local.get 3
i32.load offset=40
local.set 46
local.get 45
local.get 46
i32.add
local.set 47
local.get 3
i32.load offset=20
local.set 48
local.get 47
local.get 48
i32.add
local.set 49
local.get 44
local.get 49
i32.add
local.set 50
local.get 50
local.get 43
i32.store8
local.get 3
i32.load offset=20
local.set 51
i32.const 1
local.set 52
local.get 51
local.get 52
i32.add
local.set 53
local.get 3
local.get 53
i32.store offset=20
br 0 (;@2;)
end
end
local.get 3
i32.load offset=40
local.set 54
i32.const 1
local.set 55
local.get 54
local.get 55
i32.shl
local.set 56
local.get 3
local.get 56
i32.store offset=16
local.get 3
i32.load offset=32
local.set 57
local.get 3
local.get 57
local.get 21
i32.store offset=12
i32.const 0
local.set 58
local.set 22
local.get 3
local.get 58
local.get 22
i32.store offset=8
block ;; label = @1
loop ;; label = @2
local.get 3
i32.load offset=8
local.set 59
i32.const 4
local.set 60
local.get 59
local.set 61
local.get 60
local.set 62
local.get 61
local.get 62
local.set 23
local.get 3
i32.load offset=24
local.set 24
local.get 23
local.set 25
local.get 24
local.set 26
local.get 25
local.get 26
i32.lt_s
local.set 63
local.set 27
i32.const 1
local.set 64
local.get 63
local.get 64
local.set 28
local.get 27
local.get 28
i32.and
local.set 65
local.get 65
local.set 29
local.get 29
i32.eqz
br_if 1 (;@1;)
local.get 3
i32.load offset=16
local.set 66
i32.load offset=12
local.set 30
local.get 3
i32.load offset=20
local.set 31
local.get 3
i32.load offset=8
local.set 67
i32.const 3
local.set 68
local.get 68
local.get 67
i32.sub
local.set 69
i32.const 3
local.set 70
local.get 69
local.get 70
i32.shl
local.set 71
local.get 66
local.get 71
i32.shr_s
local.set 72
local.set 32
local.get 31
local.get 32
i32.add
local.set 33
local.get 30
local.get 33
i32.add
local.set 34
local.get 34
i32.load8_u
local.set 35
local.get 3
i32.load offset=12
local.set 73
local.get 73
local.get 72
local.set 36
local.get 3
i32.load offset=16
local.set 37
i32.const 4
local.set 38
local.get 37
local.get 38
i32.add
local.set 39
local.get 3
i32.load offset=8
local.set 40
local.get 39
local.get 40
i32.add
local.set 41
local.get 36
local.get 41
i32.add
local.set 42
local.get 42
local.get 35
i32.store8
local.get 3
i32.load offset=12
local.set 43
local.get 3
i32.load offset=20
local.set 44
local.get 3
i32.load offset=8
local.set 45
local.get 44
local.get 45
i32.add
local.set 46
local.get 43
local.get 46
i32.add
local.set 47
local.get 47
i32.load8_u
local.set 48
local.get 3
i32.load offset=12
local.set 49
local.get 3
i32.load offset=16
local.set 50
local.get 3
i32.load offset=24
local.set 51
local.get 50
local.get 51
i32.add
local.set 52
i32.const 4
local.set 53
local.get 52
local.get 53
i32.add
local.set 54
local.get 3
i32.load offset=8
local.set 55
local.get 54
local.get 55
i32.add
local.set 56
local.get 49
local.get 56
i32.add
local.set 57
local.get 57
local.get 48
i32.store8
local.get 3
i32.load offset=8
local.set 74
local.set 58
i32.const 1
local.set 75
local.get 74
local.get 75
local.set 59
local.get 58
local.get 59
i32.add
local.set 76
local.set 60
local.get 3
local.get 76
local.get 60
i32.store offset=8
br 0 (;@2;)
end
end
local.get 3
i32.load offset=32
local.set 77
local.get 77
i32.load offset=16
local.set 61
local.get 3
local.get 61
i32.store offset=4
local.get 3
i32.load offset=24
local.set 62
i32.const 1
local.set 63
local.get 62
local.get 63
i32.shl
local.set 64
local.get 3
i32.load offset=4
local.set 65
local.get 65
local.get 64
i32.store
local.get 3
i32.load offset=16
local.set 66
local.get 66
return)
(memory (;0;) 2)
(global (;0;) (mut i32) (i32.const 66560))
(global $__stack_pointer (mut i32) (i32.const 66576))
(global (;1;) i32 (i32.const 1024))
(global (;2;) i32 (i32.const 1024))
(global (;3;) i32 (i32.const 1024))
(global (;4;) i32 (i32.const 66560))
(global (;5;) i32 (i32.const 0))
(global (;6;) i32 (i32.const 1))
(global (;3;) i32 (i32.const 1028))
(global (;4;) i32 (i32.const 1024))
(global (;5;) i32 (i32.const 66576))
(global (;6;) i32 (i32.const 0))
(global (;7;) i32 (i32.const 1))
(export "memory" (memory 0))
(export "{dbl_name}" (func $dbl)))
"""