Skip to content

Commit 41dd8a7

Browse files
committed
feat(instruction-metering): implement instruction metering resume functionality and add tests
1 parent f0aa4e8 commit 41dd8a7

File tree

14 files changed

+652
-28
lines changed

14 files changed

+652
-28
lines changed

core/iwasm/common/wasm_exec_env.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
8787

8888
#if WASM_ENABLE_INSTRUCTION_METERING != 0
8989
exec_env->instructions_to_execute = -1;
90+
exec_env->metering_suspended = false;
91+
exec_env->metering_suspend_frame = NULL;
92+
exec_env->metering_suspend_function = NULL;
93+
exec_env->metering_suspend_argc = 0;
94+
exec_env->metering_suspend_argv = NULL;
9095
#endif
9196

9297
return exec_env;

core/iwasm/common/wasm_exec_env.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ typedef struct WASMExecEnv {
9090
#if WASM_ENABLE_INSTRUCTION_METERING != 0
9191
/* instructions to execute */
9292
int instructions_to_execute;
93+
94+
/* true when classic interpreter suspended by instruction metering */
95+
bool metering_suspended;
96+
97+
/* top frame to resume from when metering_suspended is true */
98+
struct WASMInterpFrame *metering_suspend_frame;
99+
100+
/* function associated with metering suspended frame */
101+
struct WASMFunctionInstance *metering_suspend_function;
102+
103+
/* argc captured for metering suspended function */
104+
uint32 metering_suspend_argc;
105+
106+
/* argv captured for metering suspended function */
107+
uint32 *metering_suspend_argv;
93108
#endif
94109

95110
#if WASM_ENABLE_FAST_JIT != 0

core/iwasm/common/wasm_runtime_common.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,6 +2469,30 @@ wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
24692469
{
24702470
exec_env->instructions_to_execute = instructions_to_execute;
24712471
}
2472+
2473+
bool
2474+
wasm_runtime_resume_wasm(WASMExecEnv *exec_env)
2475+
{
2476+
WASMFunctionInstanceCommon *function;
2477+
2478+
if (!wasm_runtime_exec_env_check(exec_env)) {
2479+
LOG_ERROR("Invalid exec env stack info.");
2480+
return false;
2481+
}
2482+
2483+
if (!exec_env->metering_suspended
2484+
|| !exec_env->metering_suspend_function
2485+
|| !exec_env->metering_suspend_argv) {
2486+
wasm_runtime_set_exception(exec_env->module_inst,
2487+
"no metering resume is pending");
2488+
return false;
2489+
}
2490+
2491+
function = (WASMFunctionInstanceCommon *)exec_env->metering_suspend_function;
2492+
return wasm_runtime_call_wasm(exec_env, function,
2493+
exec_env->metering_suspend_argc,
2494+
exec_env->metering_suspend_argv);
2495+
}
24722496
#endif
24732497

24742498
WASMFuncType *

core/iwasm/common/wasm_runtime_common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,10 @@ wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env,
877877
WASM_RUNTIME_API_EXTERN void
878878
wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
879879
int instructions_to_execute);
880+
881+
/* See wasm_export.h for description */
882+
WASM_RUNTIME_API_EXTERN bool
883+
wasm_runtime_resume_wasm(WASMExecEnv *exec_env);
880884
#endif
881885

882886
#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0

core/iwasm/include/wasm_export.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,26 @@ WASM_RUNTIME_API_EXTERN void
19281928
wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env,
19291929
int instruction_count);
19301930

1931+
/**
1932+
* Resume wasm execution after an instruction metering trap.
1933+
*
1934+
* When instruction metering is enabled and execution stops with
1935+
* `instruction limit exceeded`, the runtime may preserve interpreter frame
1936+
* state in the exec env. This API resumes from that preserved state without
1937+
* requiring the host to call a specific exported function again.
1938+
*
1939+
* The caller should set a new instruction budget with
1940+
* `wasm_runtime_set_instruction_count_limit(...)` before resuming.
1941+
*
1942+
* @param exec_env the execution environment
1943+
*
1944+
* @return true if resumed execution succeeds, false otherwise and exception
1945+
* will be thrown, the caller can call wasm_runtime_get_exception to get
1946+
* exception info.
1947+
*/
1948+
WASM_RUNTIME_API_EXTERN bool
1949+
wasm_runtime_resume_wasm(wasm_exec_env_t exec_env);
1950+
19311951
/**
19321952
* Dump runtime memory consumption, including:
19331953
* Exec env memory consumption

core/iwasm/interpreter/wasm_interp_classic.c

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,25 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
15581558
#define CHECK_INSTRUCTION_LIMIT() (void)0
15591559
#endif
15601560

1561+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
1562+
static inline bool
1563+
is_instruction_metering_exception(WASMModuleInstance *module_inst)
1564+
{
1565+
const char *exception = wasm_get_exception(module_inst);
1566+
return exception && strstr(exception, "instruction limit exceeded");
1567+
}
1568+
1569+
static inline void
1570+
clear_metering_suspend_state(WASMExecEnv *exec_env)
1571+
{
1572+
exec_env->metering_suspended = false;
1573+
exec_env->metering_suspend_frame = NULL;
1574+
exec_env->metering_suspend_function = NULL;
1575+
exec_env->metering_suspend_argc = 0;
1576+
exec_env->metering_suspend_argv = NULL;
1577+
}
1578+
#endif
1579+
15611580
static void
15621581
wasm_interp_call_func_bytecode(WASMModuleInstance *module,
15631582
WASMExecEnv *exec_env,
@@ -1671,6 +1690,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
16711690
#undef HANDLE_OPCODE
16721691
#endif
16731692

1693+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
1694+
if (prev_frame && prev_frame->function == cur_func && prev_frame->ip) {
1695+
RECOVER_CONTEXT(prev_frame);
1696+
#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0
1697+
is_return_call = false;
1698+
#endif
1699+
goto resume_func;
1700+
}
1701+
#endif
1702+
16741703
#if WASM_ENABLE_LABELS_AS_VALUES == 0
16751704
while (frame_ip < frame_ip_end) {
16761705
opcode = *frame_ip++;
@@ -6857,6 +6886,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
68576886

68586887
wasm_exec_env_set_cur_frame(exec_env, frame);
68596888
}
6889+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
6890+
resume_func:
6891+
#endif
68606892
#if WASM_ENABLE_THREAD_MGR != 0
68616893
CHECK_SUSPEND_FLAGS();
68626894
#endif
@@ -7413,6 +7445,10 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
74137445
wasm_runtime_get_running_mode((WASMModuleInstanceCommon *)module_inst);
74147446
/* Allocate sufficient cells for all kinds of return values. */
74157447
bool alloc_frame = true;
7448+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
7449+
bool resume_metering = false;
7450+
WASMRuntimeFrame *suspended_frame = NULL;
7451+
#endif
74167452

74177453
if (argc < function->param_cell_num) {
74187454
char buf[128];
@@ -7456,7 +7492,34 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
74567492
#endif
74577493
}
74587494

7459-
if (alloc_frame) {
7495+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
7496+
if (running_mode == Mode_Interp && exec_env->metering_suspended) {
7497+
suspended_frame = exec_env->metering_suspend_frame;
7498+
if (!suspended_frame || suspended_frame->function != function) {
7499+
wasm_set_exception(module_inst,
7500+
"cannot call different function while metering "
7501+
"resume is pending");
7502+
return;
7503+
}
7504+
if (!suspended_frame->prev_frame) {
7505+
wasm_set_exception(module_inst,
7506+
"invalid metering resume frame state");
7507+
clear_metering_suspend_state(exec_env);
7508+
return;
7509+
}
7510+
7511+
resume_metering = true;
7512+
frame = suspended_frame->prev_frame;
7513+
prev_frame = frame->prev_frame;
7514+
wasm_exec_env_set_cur_frame(exec_env, suspended_frame);
7515+
}
7516+
#endif
7517+
7518+
if (alloc_frame
7519+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
7520+
&& !resume_metering
7521+
#endif
7522+
) {
74607523
unsigned all_cell_num =
74617524
function->ret_cell_num > 2 ? function->ret_cell_num : 2;
74627525
unsigned frame_size;
@@ -7513,7 +7576,13 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
75137576
else {
75147577
if (running_mode == Mode_Interp) {
75157578
wasm_interp_call_func_bytecode(module_inst, exec_env, function,
7516-
frame);
7579+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
7580+
resume_metering ? suspended_frame
7581+
: frame
7582+
#else
7583+
frame
7584+
#endif
7585+
);
75177586
}
75187587
#if WASM_ENABLE_FAST_JIT != 0
75197588
else if (running_mode == Mode_Fast_JIT) {
@@ -7554,6 +7623,27 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
75547623
#endif
75557624
}
75567625

7626+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
7627+
if ((running_mode == Mode_Interp)
7628+
&& is_instruction_metering_exception(module_inst)) {
7629+
exec_env->metering_suspended = true;
7630+
exec_env->metering_suspend_frame =
7631+
wasm_exec_env_get_cur_frame(exec_env);
7632+
if (exec_env->metering_suspend_frame) {
7633+
exec_env->metering_suspend_function =
7634+
exec_env->metering_suspend_frame->function;
7635+
exec_env->metering_suspend_argc = argc;
7636+
exec_env->metering_suspend_argv = argv;
7637+
}
7638+
else {
7639+
exec_env->metering_suspend_function = NULL;
7640+
exec_env->metering_suspend_argc = 0;
7641+
exec_env->metering_suspend_argv = NULL;
7642+
}
7643+
return;
7644+
}
7645+
#endif
7646+
75577647
/* Output the return value to the caller */
75587648
if (!wasm_copy_exception(module_inst, NULL)) {
75597649
if (alloc_frame) {
@@ -7575,4 +7665,8 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
75757665
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
75767666
FREE_FRAME(exec_env, frame);
75777667
}
7668+
7669+
#if WASM_ENABLE_INSTRUCTION_METERING != 0
7670+
clear_metering_suspend_state(exec_env);
7671+
#endif
75787672
}

0 commit comments

Comments
 (0)