Skip to content

Commit 68ba9a4

Browse files
committed
Grow memory dynamically as-needed when loading wasm language modules
1 parent f2285b4 commit 68ba9a4

File tree

4 files changed

+2020
-1980
lines changed

4 files changed

+2020
-1980
lines changed

lib/src/alloc.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,3 @@ void ts_set_allocator(
4545
ts_current_realloc = new_realloc ? new_realloc : ts_realloc_default;
4646
ts_current_free = new_free ? new_free : free;
4747
}
48-

lib/src/wasm.c

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ struct TSWasmStore {
108108
LanguageWasmInstance *current_instance;
109109
Array(LanguageWasmInstance) language_instances;
110110
uint32_t current_memory_offset;
111+
uint32_t current_memory_size;
111112
uint32_t current_function_table_offset;
112113
uint16_t fn_indices[STDLIB_SYMBOL_COUNT];
113114
wasm_globaltype_t *const_i32_type;
@@ -172,20 +173,22 @@ typedef struct {
172173
static volatile uint32_t NEXT_LANGUAGE_ID;
173174

174175
// Linear memory layout:
175-
// [ <-- stack grows down | fixed data | heap grows up --> | per-language static data ]
176+
// [ <-- stack | built-in data | heap --> | static data ]
176177
#define STACK_SIZE (64 * 1024)
177178
#define HEAP_SIZE (1024 * 1024)
178-
#define SERIALIZATION_BUFFER_ADDRESS (STACK_SIZE - TREE_SITTER_SERIALIZATION_BUFFER_SIZE)
179-
#define LEXER_ADDRESS (SERIALIZATION_BUFFER_ADDRESS - sizeof(LexerInWasmMemory))
180-
#define INITIAL_STACK_POINTER_ADDRESS (LEXER_ADDRESS)
181-
#define HEAP_START_ADDRESS (STACK_SIZE)
182-
#define DATA_START_ADDRESS (STACK_SIZE + HEAP_SIZE)
179+
#define INITIAL_MEMORY_SIZE (4 * 1024 * 1024 / MEMORY_PAGE_SIZE)
180+
#define MAX_MEMORY_SIZE 32768
181+
#define SERIALIZATION_BUFFER_ADDRESS (STACK_SIZE)
182+
#define LEXER_ADDRESS (SERIALIZATION_BUFFER_ADDRESS + TREE_SITTER_SERIALIZATION_BUFFER_SIZE)
183+
#define HEAP_START_ADDRESS (LEXER_ADDRESS + sizeof(LexerInWasmMemory))
184+
#define DATA_START_ADDRESS (HEAP_START_ADDRESS + HEAP_SIZE)
183185

184186
enum FunctionIx {
185187
NULL_IX = 0,
186188
PROC_EXIT_IX,
187189
ABORT_IX,
188190
ASSERT_FAIL_IX,
191+
NOTIFY_MEMORY_GROWTH_IX,
189192
AT_EXIT_IX,
190193
LEXER_ADVANCE_IX,
191194
LEXER_MARK_END_IX,
@@ -280,6 +283,16 @@ static bool wasm_dylink_info__parse(
280283
abort();
281284
}
282285

286+
static wasm_trap_t *callback__notify_memory_growth(
287+
void *env,
288+
wasmtime_caller_t* caller,
289+
wasmtime_val_raw_t *args_and_results,
290+
size_t args_and_results_len
291+
) {
292+
fprintf(stderr, "wasm module called exit");
293+
abort();
294+
}
295+
283296
static wasm_trap_t *callback__at_exit(
284297
void *env,
285298
wasmtime_caller_t* caller,
@@ -486,7 +499,7 @@ static bool ts_wasm_store__provide_builtin_import(
486499
assert(!error);
487500
*import = (wasmtime_extern_t) {.kind = WASMTIME_EXTERN_GLOBAL, .of.global = global};
488501
} else if (name_eq(import_name, "__stack_pointer")) {
489-
wasmtime_val_t value = WASM_I32_VAL(INITIAL_STACK_POINTER_ADDRESS);
502+
wasmtime_val_t value = WASM_I32_VAL(STACK_SIZE);
490503
wasmtime_global_t global;
491504
error = wasmtime_global_new(context, self->var_i32_type, &value, &global);
492505
assert(!error);
@@ -506,6 +519,8 @@ static bool ts_wasm_store__provide_builtin_import(
506519
*import = get_builtin_func_extern(context, &self->function_table, ABORT_IX);
507520
} else if (name_eq(import_name, "proc_exit")) {
508521
*import = get_builtin_func_extern(context, &self->function_table, PROC_EXIT_IX);
522+
} else if (name_eq(import_name, "emscripten_notify_memory_growth")) {
523+
*import = get_builtin_func_extern(context, &self->function_table, NOTIFY_MEMORY_GROWTH_IX);
509524
} else {
510525
return false;
511526
}
@@ -543,7 +558,7 @@ TSWasmStore *ts_wasm_store_new(TSWasmEngine *engine, TSWasmError *wasm_error) {
543558
wasm_exporttype_vec_t export_types = WASM_EMPTY_VEC;
544559

545560
// Initialize store's memory
546-
wasm_limits_t memory_limits = {.min = 256, .max = 256};
561+
wasm_limits_t memory_limits = {.min = INITIAL_MEMORY_SIZE, .max = MAX_MEMORY_SIZE};
547562
wasm_memorytype_t *memory_type = wasm_memorytype_new(&memory_limits);
548563
wasmtime_memory_t memory;
549564
error = wasmtime_memory_new(context, memory_type, &memory);
@@ -578,6 +593,7 @@ TSWasmStore *ts_wasm_store_new(TSWasmEngine *engine, TSWasmError *wasm_error) {
578593
[PROC_EXIT_IX] = {callback__exit, wasm_functype_new_1_0(wasm_valtype_new_i32())},
579594
[ABORT_IX] = {callback__exit, wasm_functype_new_0_0()},
580595
[ASSERT_FAIL_IX] = {callback__exit, wasm_functype_new_4_0(wasm_valtype_new_i32(), wasm_valtype_new_i32(), wasm_valtype_new_i32(), wasm_valtype_new_i32())},
596+
[NOTIFY_MEMORY_GROWTH_IX] = {callback__notify_memory_growth, wasm_functype_new_1_0(wasm_valtype_new_i32())},
581597
[AT_EXIT_IX] = {callback__at_exit, wasm_functype_new_3_1(wasm_valtype_new_i32(), wasm_valtype_new_i32(), wasm_valtype_new_i32(), wasm_valtype_new_i32())},
582598
[LEXER_ADVANCE_IX] = {callback__lexer_advance, wasm_functype_new_2_0(wasm_valtype_new_i32(), wasm_valtype_new_i32())},
583599
[LEXER_MARK_END_IX] = {callback__lexer_mark_end, wasm_functype_new_1_0(wasm_valtype_new_i32())},
@@ -634,7 +650,8 @@ TSWasmStore *ts_wasm_store_new(TSWasmEngine *engine, TSWasmError *wasm_error) {
634650
.memory = memory,
635651
.language_instances = array_new(),
636652
.function_table = function_table,
637-
.current_memory_offset = DATA_START_ADDRESS,
653+
.current_memory_offset = 0,
654+
.current_memory_size = 64 * MEMORY_PAGE_SIZE,
638655
.current_function_table_offset = definitions_len,
639656
.const_i32_type = wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST),
640657
.var_i32_type = wasm_globaltype_new(wasm_valtype_new_i32(), WASM_VAR),
@@ -703,7 +720,7 @@ TSWasmStore *ts_wasm_store_new(TSWasmEngine *engine, TSWasmError *wasm_error) {
703720
}
704721
wasm_importtype_vec_delete(&import_types);
705722

706-
self->current_memory_offset += dylink_info.memory_size;
723+
self->current_memory_offset = DATA_START_ADDRESS + dylink_info.memory_size;
707724
self->current_function_table_offset += dylink_info.table_size;
708725

709726
for (unsigned i = 0; i < STDLIB_SYMBOL_COUNT; i++) {
@@ -794,17 +811,32 @@ static bool ts_wasm_store__instantiate(
794811
wasm_trap_t *trap = NULL;
795812
wasm_message_t message = WASM_EMPTY_VEC;
796813
char *language_function_name = NULL;
814+
wasmtime_context_t *context = wasmtime_store_context(self->store);
797815

798816
// Grow the function table to make room for the new functions.
799-
wasmtime_context_t *context = wasmtime_store_context(self->store);
800817
wasmtime_val_t initializer = {.kind = WASMTIME_FUNCREF};
801-
uint32_t prev_size;
802-
error = wasmtime_table_grow(context, &self->function_table, dylink_info->table_size, &initializer, &prev_size);
818+
uint32_t prev_table_size;
819+
error = wasmtime_table_grow(context, &self->function_table, dylink_info->table_size, &initializer, &prev_table_size);
803820
if (error) {
804821
format(error_message, "invalid function table size %u", dylink_info->table_size);
805822
goto error;
806823
}
807824

825+
// Grow the memory to make room for the new data.
826+
uint32_t needed_memory_size = self->current_memory_offset + dylink_info->memory_size;
827+
if (needed_memory_size > self->current_memory_size) {
828+
uint32_t pages_to_grow = (
829+
needed_memory_size - self->current_memory_size + MEMORY_PAGE_SIZE - 1) /
830+
MEMORY_PAGE_SIZE;
831+
uint64_t prev_memory_size;
832+
error = wasmtime_memory_grow(context, &self->memory, pages_to_grow, &prev_memory_size);
833+
if (error) {
834+
format(error_message, "invalid memory size %u", dylink_info->memory_size);
835+
goto error;
836+
}
837+
self->current_memory_size += pages_to_grow * MEMORY_PAGE_SIZE;
838+
}
839+
808840
// Construct the language function name as string.
809841
format(&language_function_name, "tree_sitter_%s", language_name);
810842

0 commit comments

Comments
 (0)