blob: f7a4c4900271cd12543e1a010bee088c2c7a4fe1 [file]
From: rive
Subject: [PATCH 0041] map shared memories as private ones and grow them the same way
A shared memory reserved its whole maximum up front. It now maps and grows
as a private memory does, extending or moving its map when growth passes it,
which is safe because it grows only while no parallel call runs: the host
freezes it for the length of each call and a frozen memory refuses to grow.
Compiled code reloads a shared memory's base at the same points as a private
one's.
diff --git a/core/iwasm/common/wasm_memory.c b/core/iwasm/common/wasm_memory.c
index 2f72446..00c9dc7 100644
--- a/core/iwasm/common/wasm_memory.c
+++ b/core/iwasm/common/wasm_memory.c
@@ -999,9 +999,9 @@ wasm_runtime_get_mem_alloc_info(mem_alloc_info_t *mem_alloc_info)
return false;
}
-/* rive: a shared memory never moves and only grows, so a check racing a
- growth at worst refuses bytes just added; with atomic 64 bit sizes the
- process wide mutex was only overhead on every host call with a pointer */
+/* rive: a shared memory grows only while frozen is clear, when no parallel
+ call runs and only the growing thread runs its module, so no check races
+ a growth; the process wide mutex was only overhead on every pointer */
#if WASM_ENABLE_SHARED_MEMORY != 0 && BH_ATOMIC_64_IS_ATOMIC != 0
#define VALIDATE_LOCK(memory) (void)0
#define VALIDATE_UNLOCK(memory) (void)0
@@ -1593,9 +1593,8 @@ wasm_enlarge_memory_internal(WASMModuleInstanceCommon *module,
#ifdef OS_ENABLE_HW_BOUND_CHECK
full_size_mmaped = true;
-#elif WASM_ENABLE_SHARED_MEMORY != 0
- full_size_mmaped = shared_memory_is_shared(memory);
#else
+ /* rive: a shared memory maps and moves as a private one does */
full_size_mmaped = false;
#endif
@@ -1615,6 +1614,12 @@ wasm_enlarge_memory_internal(WASMModuleInstanceCommon *module,
/* No need to enlarge memory */
return true;
+ /* rive: a parallel call's members read the memory where it lies */
+ if (RIVE_MEMORY_FROZEN(memory)) {
+ ret = false;
+ goto return_func;
+ }
+
if (total_page_count < cur_page_count) { /* integer overflow */
ret = false;
goto return_func;
@@ -1955,18 +1960,9 @@ wasm_deallocate_linear_memory(WASMMemoryInstance *memory_inst)
bh_assert(memory_inst->memory_data);
#ifndef OS_ENABLE_HW_BOUND_CHECK
-#if WASM_ENABLE_SHARED_MEMORY != 0
- if (shared_memory_is_shared(memory_inst)) {
- map_size = (uint64)memory_inst->num_bytes_per_page
- * memory_inst->max_page_count;
- }
- else
-#endif
- {
- map_size = linear_memory_map_size(memory_inst->num_bytes_per_page,
- memory_inst->cur_page_count,
- memory_inst->max_page_count);
- }
+ map_size = linear_memory_map_size(memory_inst->num_bytes_per_page,
+ memory_inst->cur_page_count,
+ memory_inst->max_page_count);
#else
map_size = 8 * (uint64)BH_GB;
#endif
@@ -1999,20 +1995,14 @@ wasm_allocate_linear_memory(uint8 **data, bool is_shared_memory,
bh_assert(memory_data_size);
#ifndef OS_ENABLE_HW_BOUND_CHECK
-#if WASM_ENABLE_SHARED_MEMORY != 0
- if (is_shared_memory) {
- /* Allocate maximum memory size when memory is shared */
- map_size = max_page_count * num_bytes_per_page;
- }
- else
-#endif
- {
- map_size = linear_memory_map_size(num_bytes_per_page, init_page_count,
- max_page_count);
- /* rive patch: growth inside the map reuses it without committing,
- while hw bounds rely on the uncommitted rest to trap */
- commit_map = true;
- }
+ /* rive: a shared memory too, which may move when it grows as only the
+ thread growing it runs its module then */
+ (void)is_shared_memory;
+ map_size = linear_memory_map_size(num_bytes_per_page, init_page_count,
+ max_page_count);
+ /* rive patch: growth inside the map reuses it without committing,
+ while hw bounds rely on the uncommitted rest to trap */
+ commit_map = true;
#else /* else of OS_ENABLE_HW_BOUND_CHECK */
/* Totally 8G is mapped, the opcode load/store address range is 0 to 8G:
* ea = i + memarg.offset
diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c
index 0940558..8998fae 100644
--- a/core/iwasm/common/wasm_runtime_common.c
+++ b/core/iwasm/common/wasm_runtime_common.c
@@ -3314,6 +3314,17 @@ wasm_runtime_instantiate_parallel_member(WASMModuleInstanceCommon *parent,
return NULL;
}
+void
+wasm_runtime_rive_freeze_memory(WASMModuleInstanceCommon *module_inst,
+ bool frozen)
+{
+ WASMMemoryInstance *memory =
+ wasm_get_default_memory((WASMModuleInstance *)module_inst);
+
+ if (memory)
+ RIVE_SET_MEMORY_FROZEN(memory, frozen ? 1 : 0);
+}
+
void
wasm_runtime_copy_globals(WASMModuleInstanceCommon *dst,
WASMModuleInstanceCommon *src)
diff --git a/core/iwasm/compilation/aot_emit_memory.c b/core/iwasm/compilation/aot_emit_memory.c
index 0f42df5..1c4b7c8 100644
--- a/core/iwasm/compilation/aot_emit_memory.c
+++ b/core/iwasm/compilation/aot_emit_memory.c
@@ -122,11 +122,7 @@ get_memory_check_bound(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
aot_set_last_error("llvm build load failed.");
return NULL;
}
-#if WASM_ENABLE_SHARED_MEMORY != 0
- /* rive patch: another thread may grow a shared memory at any time */
- if (!(comp_ctx->comp_data->memories[0].flags & SHARED_MEMORY_FLAG))
-#endif
- aot_set_tbaa(comp_ctx, mem_check_bound, comp_ctx->tbaa_mem_info);
+ aot_set_tbaa(comp_ctx, mem_check_bound, comp_ctx->tbaa_mem_info);
return mem_check_bound;
}
@@ -578,10 +574,6 @@ aot_check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint64 const_value;
bool is_target_64bit, is_local_of_aot_value = false;
bool is_const = false;
-#if WASM_ENABLE_SHARED_MEMORY != 0
- bool is_shared_memory =
- comp_ctx->comp_data->memories[0].flags & SHARED_MEMORY_FLAG;
-#endif
#if WASM_ENABLE_MEMORY64 == 0
bool is_memory64 = false;
#else
@@ -619,11 +611,7 @@ aot_check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
/* Get memory base address and memory data size */
- if (func_ctx->mem_space_unchanged
-#if WASM_ENABLE_SHARED_MEMORY != 0
- || is_shared_memory
-#endif
- ) {
+ if (func_ctx->mem_space_unchanged) {
mem_base_addr = func_ctx->mem_info[0].mem_base_addr;
}
else {
@@ -1513,13 +1501,7 @@ check_bulk_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
is_target_64bit = (comp_ctx->pointer_size == sizeof(uint64)) ? true : false;
/* Get memory base address and memory data size */
-#if WASM_ENABLE_SHARED_MEMORY != 0
- bool is_shared_memory = comp_ctx->comp_data->memories[0].flags & 0x02;
-
- if (func_ctx->mem_space_unchanged || is_shared_memory) {
-#else
if (func_ctx->mem_space_unchanged) {
-#endif
mem_base_addr = func_ctx->mem_info[0].mem_base_addr;
}
else {
diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c
index f419073..d21875e 100644
--- a/core/iwasm/compilation/aot_llvm.c
+++ b/core/iwasm/compilation/aot_llvm.c
@@ -1470,18 +1470,8 @@ create_memory_info(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
return false;
}
}
-#if WASM_ENABLE_SHARED_MEMORY != 0
- else if (is_shared_memory) {
- /* The base address for shared memory will never changed,
- we can load the value here */
- if (!(func_ctx->mem_info[0].mem_base_addr = LLVMBuildLoad2(
- comp_ctx->builder, OPQ_PTR_TYPE,
- func_ctx->mem_info[0].mem_base_addr, "mem_base_addr"))) {
- aot_set_last_error("llvm build load failed");
- return false;
- }
- }
-#endif
+ /* rive: a shared memory moves when it grows, as a private one does,
+ never under a running parallel call, so it reloads at the same points */
bound_check_type = (comp_ctx->pointer_size == sizeof(uint64))
? INT64_PTR_TYPE
@@ -1618,10 +1608,6 @@ create_memory_info(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
}
-#if WASM_ENABLE_SHARED_MEMORY != 0
- if (is_shared_memory)
- return true;
-#endif
/* rive patch: unoptimized code keeps the locals in memory, a cost with no
gain */
if (!mem_space_unchanged && comp_ctx->opt_level > 0)
diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h
index 6007561..4c9cd06 100644
--- a/core/iwasm/include/wasm_export.h
+++ b/core/iwasm/include/wasm_export.h
@@ -1359,6 +1359,13 @@ wasm_runtime_instantiate_parallel_member(wasm_module_inst_t parent,
uint32_t stack_size, char *error_buf,
uint32_t error_buf_size);
+/**
+ * rive: while frozen, module_inst's default memory refuses to grow, as
+ * growing may move it under the parallel call members that share it.
+ */
+WASM_RUNTIME_API_EXTERN void
+wasm_runtime_rive_freeze_memory(wasm_module_inst_t module_inst, bool frozen);
+
/**
* rive: copy src's global values into dst, an instance of the same module.
*/
diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h
index 6d2e96f..423e5ec 100644
--- a/core/iwasm/interpreter/wasm_runtime.h
+++ b/core/iwasm/interpreter/wasm_runtime.h
@@ -124,9 +124,14 @@ struct WASMMemoryInstance {
0: non-shared memory, > 0: shared memory */
bh_atomic_16_t ref_count;
+ /* rive: set while a parallel call runs, when growing could move the
+ memory under its members; a padding byte, so compiled code's offsets
+ stay */
+ uint8 rive_frozen;
+
/* Four-byte paddings to ensure the layout of WASMMemoryInstance is the same
* in both 64-bit and 32-bit */
- uint8 _paddings[4];
+ uint8 _paddings[3];
/* Number bytes per page */
uint32 num_bytes_per_page;
@@ -165,6 +170,19 @@ struct WASMMemoryInstance {
#endif
};
+/* rive: MSVC lacks the GCC builtins, and it only builds wamrc, which never
+ runs a parallel call */
+#if defined(CLANG_GCC_HAS_ATOMIC_BUILTIN)
+#define RIVE_MEMORY_FROZEN(memory) \
+ __atomic_load_n(&(memory)->rive_frozen, __ATOMIC_SEQ_CST)
+#define RIVE_SET_MEMORY_FROZEN(memory, frozen) \
+ __atomic_store_n(&(memory)->rive_frozen, (frozen), __ATOMIC_SEQ_CST)
+#else
+#define RIVE_MEMORY_FROZEN(memory) ((memory)->rive_frozen)
+#define RIVE_SET_MEMORY_FROZEN(memory, frozen) \
+ ((memory)->rive_frozen = (frozen))
+#endif
+
/* WASMTableInstance is used to represent table instance in
* runtime, to compute the table element address with index
* we need to know the element type and the element ref type.