From: Brendan Hansen Date: Sun, 12 Feb 2023 02:09:28 +0000 (-0600) Subject: better support for memory_grow and memory_size in OVM X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=4480727c7dad79ee0e4a09ad780e4f4644ecfad8;p=onyx.git better support for memory_grow and memory_size in OVM --- diff --git a/interpreter/include/vm.h b/interpreter/include/vm.h index d3664a8b..85a75789 100644 --- a/interpreter/include/vm.h +++ b/interpreter/include/vm.h @@ -119,6 +119,7 @@ struct ovm_engine_t { ovm_engine_t *ovm_engine_new(ovm_store_t *store); void ovm_engine_delete(ovm_engine_t *engine); +bool ovm_engine_memory_ensure_capacity(ovm_engine_t *engine, i64 minimum_size); void ovm_engine_memory_copy(ovm_engine_t *engine, i64 target, void *data, i64 size); bool ovm_program_load_from_file(ovm_program_t *program, ovm_engine_t *engine, char *filename); @@ -352,6 +353,9 @@ struct ovm_instr_t { #define OVMI_BREAK 0x4d +#define OVMI_MEM_SIZE 0x4e // %r = +#define OVMI_MEM_GROW 0x4f // %r = + // // OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32) == instruction for adding i32s // diff --git a/interpreter/include/vm_codebuilder.h b/interpreter/include/vm_codebuilder.h index 347ee5a0..c4b0f454 100644 --- a/interpreter/include/vm_codebuilder.h +++ b/interpreter/include/vm_codebuilder.h @@ -87,5 +87,7 @@ void ovm_code_builder_add_atomic_store(ovm_code_builder_t *builder void ovm_code_builder_add_cmpxchg(ovm_code_builder_t *builder, u32 ovm_type, i32 offset); void ovm_code_builder_add_memory_copy(ovm_code_builder_t *builder); void ovm_code_builder_add_memory_fill(ovm_code_builder_t *builder); +void ovm_code_builder_add_memory_size(ovm_code_builder_t *builder); +void ovm_code_builder_add_memory_grow(ovm_code_builder_t *builder); #endif diff --git a/interpreter/src/vm/code_builder.c b/interpreter/src/vm/code_builder.c index 59da1e86..d7725152 100644 --- a/interpreter/src/vm/code_builder.c +++ b/interpreter/src/vm/code_builder.c @@ -564,6 +564,29 @@ void ovm_code_builder_add_cmpxchg(ovm_code_builder_t *builder, u32 ovm_type, i32 PUSH_VALUE(builder, instrs[2].r); } +void ovm_code_builder_add_memory_size(ovm_code_builder_t *builder) { + ovm_instr_t instr = {0}; + instr.full_instr = OVM_TYPED_INSTR(OVMI_MEM_SIZE, OVM_TYPE_NONE); + instr.r = NEXT_VALUE(builder); + + debug_info_builder_emit_location(builder->debug_builder); + ovm_program_add_instructions(builder->program, 1, &instr); + + PUSH_VALUE(builder, instr.r); +} + +void ovm_code_builder_add_memory_grow(ovm_code_builder_t *builder) { + ovm_instr_t instr = {0}; + instr.full_instr = OVM_TYPED_INSTR(OVMI_MEM_GROW, OVM_TYPE_NONE); + instr.a = POP_VALUE(builder); + instr.r = NEXT_VALUE(builder); + + debug_info_builder_emit_location(builder->debug_builder); + ovm_program_add_instructions(builder->program, 1, &instr); + + PUSH_VALUE(builder, instr.r); +} + void ovm_code_builder_add_memory_copy(ovm_code_builder_t *builder) { ovm_instr_t instr = {0}; instr.full_instr = OVM_TYPED_INSTR(OVMI_COPY, OVM_TYPE_NONE); diff --git a/interpreter/src/vm/vm.c b/interpreter/src/vm/vm.c index 500e69d8..ecf4f095 100644 --- a/interpreter/src/vm/vm.c +++ b/interpreter/src/vm/vm.c @@ -1,3 +1,5 @@ +#define _GNU_SOURCE + #include "vm.h" #include @@ -141,11 +143,21 @@ ovm_engine_t *ovm_engine_new(ovm_store_t *store) { ovm_engine_t *engine = bh_alloc_item(store->heap_allocator, ovm_engine_t); engine->store = store; - engine->memory_size = 1ull << 32; - engine->memory = mmap(NULL, engine->memory_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + engine->memory_size = 0; + engine->memory = NULL; + engine->debug = NULL; pthread_mutex_init(&engine->atomic_mutex, NULL); - engine->debug = NULL; + // + // HACK: This should not be necessary, but because moving the memory around + // causes issues with other standard libraries (like STBTT and STBI), it is + // better to allocate a large amount here and avoid needing to reallocate. + // A solution should be possible, because other runtimes like Wasmer support + // this. + i64 attempt_to_allocate = 1ull << 32; + while (!ovm_engine_memory_ensure_capacity(engine, attempt_to_allocate)) { + attempt_to_allocate /= 2; + } return engine; } @@ -153,10 +165,37 @@ ovm_engine_t *ovm_engine_new(ovm_store_t *store) { void ovm_engine_delete(ovm_engine_t *engine) { ovm_store_t *store = engine->store; - munmap(engine->memory, engine->memory_size); + if (engine->memory) { + munmap(engine->memory, engine->memory_size); + } + bh_free(store->heap_allocator, engine); } +bool ovm_engine_memory_ensure_capacity(ovm_engine_t *engine, i64 minimum_size) { + if (engine->memory_size >= minimum_size) return true; + + void *new_addr; + + if (engine->memory == NULL) { + new_addr = mmap(NULL, minimum_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + } else { + new_addr = mremap(engine->memory, engine->memory_size, minimum_size, MREMAP_MAYMOVE); + } + + if (new_addr == MAP_FAILED) { + // Something went wrong... + // At this point the program should probably horifically crash. + + return false; + } + + engine->memory_size = minimum_size; + engine->memory = new_addr; + + return true; +} + void ovm_engine_memory_copy(ovm_engine_t *engine, i64 target, void *data, i64 size) { ovm_assert(engine); ovm_assert(engine->memory); diff --git a/interpreter/src/vm/vm_instrs.h b/interpreter/src/vm/vm_instrs.h index 08dd165c..c1ecfdea 100644 --- a/interpreter/src/vm/vm_instrs.h +++ b/interpreter/src/vm/vm_instrs.h @@ -317,6 +317,7 @@ OVMI_INSTR_EXEC(return) { } else { \ ovm_external_func_t external_func = state->external_funcs[func->external_func_idx]; \ external_func.native_func(external_func.userdata, &state->param_buf[extra_params], &state->__tmp_value); \ + memory = state->engine->memory; \ \ ovm__func_teardown_stack_frame(state); \ \ @@ -461,6 +462,31 @@ CMPXCHG(OVM_TYPE_I64, i64) #undef CMPXCHG +// +// Memory +// + +OVMI_INSTR_EXEC(mem_size) { + VAL(instr->r).u32 = (u32) state->engine->memory_size / 65536; + VAL(instr->r).type = OVM_TYPE_I32; + NEXT_OP; +} + +OVMI_INSTR_EXEC(mem_grow) { + ovm_assert(VAL(instr->a).type == OVM_TYPE_I32); + VAL(instr->r).type = OVM_TYPE_I32; + VAL(instr->r).u32 = (u32) state->engine->memory_size / 65536; + + if (!ovm_engine_memory_ensure_capacity(state->engine, + state->engine->memory_size + VAL(instr->a).u32 * 65536)) { + VAL(instr->r).i32 = -1; + } + + memory = state->engine->memory; + NEXT_OP; +} + + OVMI_INSTR_EXEC(illegal) { OVMI_EXCEPTION_HOOK; return ((ovm_value_t) {0}); @@ -558,6 +584,8 @@ static ovmi_instr_exec_t OVMI_DISPATCH_NAME[] = { NULL, NULL, NULL, NULL, NULL, NULL, D(transmute_f64_i64), NULL, IROW_INT(cmpxchg) IROW_SAME(illegal) + IROW_UNTYPED(mem_size) + IROW_UNTYPED(mem_grow) }; #undef D diff --git a/interpreter/src/wasm/instance.c b/interpreter/src/wasm/instance.c index 1ffbb5f7..2b04f91b 100644 --- a/interpreter/src/wasm/instance.c +++ b/interpreter/src/wasm/instance.c @@ -333,6 +333,10 @@ wasm_instance_t *wasm_instance_new(wasm_store_t *store, const wasm_module_t *mod prepare_instance(instance, imports); + assert(bh_arr_length(instance->memories) == 1); + u32 memory_size = (instance->memories[0]->inner.type->memory.limits.min) * MEMORY_PAGE_SIZE; + ovm_engine_memory_ensure_capacity(store->engine->engine, memory_size); + if (trap) *trap = NULL; return instance; diff --git a/interpreter/src/wasm/memory.c b/interpreter/src/wasm/memory.c index 38445793..284cdc9b 100644 --- a/interpreter/src/wasm/memory.c +++ b/interpreter/src/wasm/memory.c @@ -31,12 +31,5 @@ wasm_memory_pages_t wasm_memory_size(const wasm_memory_t *memory) { } bool wasm_memory_grow(wasm_memory_t *memory, wasm_memory_pages_t pages) { - // - // This will always fail, as initially the VM is created with - // a 4GiB mmap, so growing it will not be an option. If that - // changes and a dynamically allocated solution is used, then - // this can change. I don't see that changing however, as I will - // never need to use this on 32-bit systems, and that would be the - // only case that I would not like to try to mmap 4 gigs. - return false; + return ovm_engine_memory_ensure_capacity(memory->inner.memory.engine, pages * MEMORY_PAGE_SIZE); } diff --git a/interpreter/src/wasm/module_parsing.h b/interpreter/src/wasm/module_parsing.h index ef9e9dd7..6d927b8a 100644 --- a/interpreter/src/wasm/module_parsing.h +++ b/interpreter/src/wasm/module_parsing.h @@ -712,18 +712,13 @@ static void parse_instruction(build_context *ctx) { case 0x3F: { assert(CONSUME_BYTE(ctx) == 0x00); - - int memory_size = 65535; - ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I32, &memory_size); + ovm_code_builder_add_memory_size(&ctx->builder); break; } case 0x40: { assert(CONSUME_BYTE(ctx) == 0x00); - ovm_code_builder_drop_value(&ctx->builder); - - int value = -1; - ovm_code_builder_add_imm(&ctx->builder, OVM_TYPE_I32, &value); + ovm_code_builder_add_memory_grow(&ctx->builder); break; }