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);
#define OVMI_BREAK 0x4d
+#define OVMI_MEM_SIZE 0x4e // %r = <size in bytes of memory>
+#define OVMI_MEM_GROW 0x4f // %r = <grow memory, return new size in bytes>
+
//
// OVM_TYPED_INSTR(OVMI_ADD, OVM_TYPE_I32) == instruction for adding i32s
//
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
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);
+#define _GNU_SOURCE
+
#include "vm.h"
#include <sys/mman.h>
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;
}
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);
} 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); \
\
#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});
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
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;
}
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);
}
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;
}