Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/iwasm/common/wasm_exec_env.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,

#if WASM_ENABLE_INSTRUCTION_METERING != 0
exec_env->instructions_to_execute = -1;
exec_env->metering_suspended = false;
exec_env->metering_suspend_frame = NULL;
exec_env->metering_suspend_function = NULL;
exec_env->metering_suspend_argc = 0;
exec_env->metering_suspend_argv = NULL;
#endif

return exec_env;
Expand Down
15 changes: 15 additions & 0 deletions core/iwasm/common/wasm_exec_env.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,21 @@ typedef struct WASMExecEnv {
#if WASM_ENABLE_INSTRUCTION_METERING != 0
/* instructions to execute */
int instructions_to_execute;

/* true when classic interpreter suspended by instruction metering */
bool metering_suspended;

/* top frame to resume from when metering_suspended is true */
struct WASMInterpFrame *metering_suspend_frame;

/* function associated with metering suspended frame */
struct WASMFunctionInstance *metering_suspend_function;

/* argc captured for metering suspended function */
uint32 metering_suspend_argc;

/* argv captured for metering suspended function */
uint32 *metering_suspend_argv;
#endif

#if WASM_ENABLE_FAST_JIT != 0
Expand Down
24 changes: 24 additions & 0 deletions core/iwasm/common/wasm_runtime_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -2469,6 +2469,30 @@ wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
{
exec_env->instructions_to_execute = instructions_to_execute;
}

bool
wasm_runtime_resume_wasm(WASMExecEnv *exec_env)
{
WASMFunctionInstanceCommon *function;

if (!wasm_runtime_exec_env_check(exec_env)) {
LOG_ERROR("Invalid exec env stack info.");
return false;
}

if (!exec_env->metering_suspended || !exec_env->metering_suspend_function
|| !exec_env->metering_suspend_argv) {
wasm_runtime_set_exception(exec_env->module_inst,
"no metering resume is pending");
return false;
}

function =
(WASMFunctionInstanceCommon *)exec_env->metering_suspend_function;
return wasm_runtime_call_wasm(exec_env, function,
exec_env->metering_suspend_argc,
exec_env->metering_suspend_argv);
}
#endif

WASMFuncType *
Expand Down
4 changes: 4 additions & 0 deletions core/iwasm/common/wasm_runtime_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,10 @@ wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env,
WASM_RUNTIME_API_EXTERN void
wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
int instructions_to_execute);

/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_resume_wasm(WASMExecEnv *exec_env);
#endif

#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
Expand Down
20 changes: 20 additions & 0 deletions core/iwasm/include/wasm_export.h
Original file line number Diff line number Diff line change
Expand Up @@ -1928,6 +1928,26 @@ WASM_RUNTIME_API_EXTERN void
wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env,
int instruction_count);

/**
* Resume wasm execution after an instruction metering trap.
*
* When instruction metering is enabled and execution stops with
* `instruction limit exceeded`, the runtime may preserve interpreter frame
* state in the exec env. This API resumes from that preserved state without
* requiring the host to call a specific exported function again.
*
* The caller should set a new instruction budget with
* `wasm_runtime_set_instruction_count_limit(...)` before resuming.
*
* @param exec_env the execution environment
*
* @return true if resumed execution succeeds, false otherwise and exception
* will be thrown, the caller can call wasm_runtime_get_exception to get
* exception info.
*/
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_resume_wasm(wasm_exec_env_t exec_env);

/**
* Dump runtime memory consumption, including:
* Exec env memory consumption
Expand Down
110 changes: 108 additions & 2 deletions core/iwasm/interpreter/wasm_interp_classic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,37 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
#define CHECK_INSTRUCTION_LIMIT() (void)0
#endif

#if WASM_ENABLE_INSTRUCTION_METERING != 0
static inline bool
is_instruction_metering_exception(WASMModuleInstance *module_inst)
{
const char *exception = wasm_get_exception(module_inst);
return exception && strstr(exception, "instruction limit exceeded");
}

static inline void
clear_metering_suspend_state(WASMExecEnv *exec_env)
{
exec_env->metering_suspended = false;
exec_env->metering_suspend_frame = NULL;
exec_env->metering_suspend_function = NULL;
exec_env->metering_suspend_argc = 0;
exec_env->metering_suspend_argv = NULL;
}

static inline WASMRuntimeFrame *
find_metering_resume_call_boundary(WASMRuntimeFrame *suspended_frame)
{
WASMRuntimeFrame *frame = suspended_frame;

while (frame && frame->prev_frame && frame->prev_frame->function) {
frame = frame->prev_frame;
}

return frame ? frame->prev_frame : NULL;
}
#endif

static void
wasm_interp_call_func_bytecode(WASMModuleInstance *module,
WASMExecEnv *exec_env,
Expand Down Expand Up @@ -1671,6 +1702,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
#undef HANDLE_OPCODE
#endif

#if WASM_ENABLE_INSTRUCTION_METERING != 0
if (prev_frame && prev_frame->function == cur_func && prev_frame->ip) {
RECOVER_CONTEXT(prev_frame);
#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0
is_return_call = false;
#endif
goto resume_func;
}
#endif

#if WASM_ENABLE_LABELS_AS_VALUES == 0
while (frame_ip < frame_ip_end) {
opcode = *frame_ip++;
Expand Down Expand Up @@ -6857,6 +6898,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,

wasm_exec_env_set_cur_frame(exec_env, frame);
}
#if WASM_ENABLE_INSTRUCTION_METERING != 0
resume_func:
#endif
#if WASM_ENABLE_THREAD_MGR != 0
CHECK_SUSPEND_FLAGS();
#endif
Expand Down Expand Up @@ -7413,6 +7457,10 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
wasm_runtime_get_running_mode((WASMModuleInstanceCommon *)module_inst);
/* Allocate sufficient cells for all kinds of return values. */
bool alloc_frame = true;
#if WASM_ENABLE_INSTRUCTION_METERING != 0
bool resume_metering = false;
WASMRuntimeFrame *suspended_frame = NULL;
#endif

if (argc < function->param_cell_num) {
char buf[128];
Expand Down Expand Up @@ -7456,7 +7504,34 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
#endif
}

if (alloc_frame) {
#if WASM_ENABLE_INSTRUCTION_METERING != 0
if (running_mode == Mode_Interp && exec_env->metering_suspended) {
suspended_frame = exec_env->metering_suspend_frame;
if (!suspended_frame || suspended_frame->function != function) {
wasm_set_exception(module_inst,
"cannot call different function while metering "
"resume is pending");
return;
}
frame = find_metering_resume_call_boundary(suspended_frame);
if (!frame) {
wasm_set_exception(module_inst,
"invalid metering resume frame state");
clear_metering_suspend_state(exec_env);
return;
}

resume_metering = true;
prev_frame = frame->prev_frame;
wasm_exec_env_set_cur_frame(exec_env, suspended_frame);
}
#endif

if (alloc_frame
#if WASM_ENABLE_INSTRUCTION_METERING != 0
&& !resume_metering
#endif
) {
unsigned all_cell_num =
function->ret_cell_num > 2 ? function->ret_cell_num : 2;
unsigned frame_size;
Expand Down Expand Up @@ -7513,7 +7588,13 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
else {
if (running_mode == Mode_Interp) {
wasm_interp_call_func_bytecode(module_inst, exec_env, function,
frame);
#if WASM_ENABLE_INSTRUCTION_METERING != 0
resume_metering ? suspended_frame
: frame
#else
frame
#endif
);
}
#if WASM_ENABLE_FAST_JIT != 0
else if (running_mode == Mode_Fast_JIT) {
Expand Down Expand Up @@ -7554,6 +7635,27 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
#endif
}

#if WASM_ENABLE_INSTRUCTION_METERING != 0
if ((running_mode == Mode_Interp)
&& is_instruction_metering_exception(module_inst)) {
exec_env->metering_suspended = true;
exec_env->metering_suspend_frame =
wasm_exec_env_get_cur_frame(exec_env);
if (exec_env->metering_suspend_frame) {
exec_env->metering_suspend_function =
exec_env->metering_suspend_frame->function;
exec_env->metering_suspend_argc = argc;
exec_env->metering_suspend_argv = argv;
}
else {
exec_env->metering_suspend_function = NULL;
exec_env->metering_suspend_argc = 0;
exec_env->metering_suspend_argv = NULL;
}
return;
}
#endif

/* Output the return value to the caller */
if (!wasm_copy_exception(module_inst, NULL)) {
if (alloc_frame) {
Expand All @@ -7575,4 +7677,8 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
FREE_FRAME(exec_env, frame);
}

#if WASM_ENABLE_INSTRUCTION_METERING != 0
clear_metering_suspend_state(exec_env);
#endif
}
Loading
Loading