Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Refactor VM exec loop #8182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 8, 2023
Merged
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
112 changes: 49 additions & 63 deletions vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -2298,114 +2298,100 @@ hook_before_rewind(rb_execution_context_t *ec, const rb_control_frame_t *cfp,

static inline VALUE
vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, VALUE errinfo);
static inline VALUE
vm_exec_loop(rb_execution_context_t *ec, enum ruby_tag_type state, struct rb_vm_tag *tag, VALUE result);

// for non-Emscripten Wasm build, use vm_exec with optimized setjmp for runtime performance
#if defined(__wasm__) && !defined(__EMSCRIPTEN__)

struct rb_vm_exec_context {
rb_execution_context_t *ec;
struct rb_vm_tag *tag;
rb_execution_context_t *const ec;
struct rb_vm_tag *const tag;

VALUE result;
enum ruby_tag_type state;
};

static void
vm_exec_enter_vm_loop(rb_execution_context_t *ec, struct rb_vm_exec_context *ctx,
struct rb_vm_tag *_tag, bool skip_first_ex_handle)
{
if (skip_first_ex_handle) {
goto vm_loop_start;
}

ctx->result = ec->errinfo;
rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY);
while (UNDEF_P(ctx->result = vm_exec_handle_exception(ec, ctx->state, ctx->result))) {
/* caught a jump, exec the handler */
ctx->result = vm_exec_core(ec);
vm_loop_start:
VM_ASSERT(ec->tag == _tag);
/* when caught `throw`, `tag.state` is set. */
if ((ctx->state = _tag->state) == TAG_NONE) break;
_tag->state = TAG_NONE;
}
}

static void
vm_exec_bottom_main(void *context)
{
struct rb_vm_exec_context *ctx = (struct rb_vm_exec_context *)context;
struct rb_vm_exec_context *ctx = context;
rb_execution_context_t *ec = ctx->ec;

ctx->state = TAG_NONE;
ctx->result = vm_exec_core(ctx->ec);
vm_exec_enter_vm_loop(ctx->ec, ctx, ctx->tag, true);
ctx->result = vm_exec_loop(ec, TAG_NONE, ctx->tag, vm_exec_core(ec));
}

static void
vm_exec_bottom_rescue(void *context)
{
struct rb_vm_exec_context *ctx = (struct rb_vm_exec_context *)context;
ctx->state = rb_ec_tag_state(ctx->ec);
vm_exec_enter_vm_loop(ctx->ec, ctx, ctx->tag, false);
struct rb_vm_exec_context *ctx = context;
rb_execution_context_t *ec = ctx->ec;

ctx->result = vm_exec_loop(ec, rb_ec_tag_state(ec), ctx->tag, ec->errinfo);
}
#endif

VALUE
vm_exec(rb_execution_context_t *ec)
{
struct rb_vm_exec_context ctx = {
.ec = ec,
.result = Qundef,
};
struct rb_wasm_try_catch try_catch;
VALUE result = Qundef;

EC_PUSH_TAG(ec);

_tag.retval = Qnil;
ctx.tag = &_tag;

#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
struct rb_vm_exec_context ctx = {
.ec = ec,
.tag = &_tag,
};
struct rb_wasm_try_catch try_catch;

EC_REPUSH_TAG();

rb_wasm_try_catch_init(&try_catch, vm_exec_bottom_main, vm_exec_bottom_rescue, &ctx);

rb_wasm_try_catch_loop_run(&try_catch, &_tag.buf);

EC_POP_TAG();
return ctx.result;
}

result = ctx.result;
#else

VALUE
vm_exec(rb_execution_context_t *ec)
{
enum ruby_tag_type state;
VALUE result = Qundef;

EC_PUSH_TAG(ec);

_tag.retval = Qnil;
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
if (UNDEF_P(result = jit_exec(ec))) {
result = vm_exec_core(ec);
}
goto vm_loop_start; /* fallback to the VM */
/* fallback to the VM */
result = vm_exec_loop(ec, TAG_NONE, &_tag, result);
}
else {
result = ec->errinfo;
rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY);
while (UNDEF_P(result = vm_exec_handle_exception(ec, state, result))) {
/* caught a jump, exec the handler */
result = vm_exec_core(ec);
vm_loop_start:
VM_ASSERT(ec->tag == &_tag);
/* when caught `throw`, `tag.state` is set. */
if ((state = _tag.state) == TAG_NONE) break;
_tag.state = TAG_NONE;
}
result = vm_exec_loop(ec, state, &_tag, ec->errinfo);
}
#endif

EC_POP_TAG();
return result;
}
#endif

static inline VALUE
vm_exec_loop(rb_execution_context_t *ec, enum ruby_tag_type state,
struct rb_vm_tag *tag, VALUE result)
{
if (state == TAG_NONE) { /* no jumps, result is discarded */
goto vm_loop_start;
}

rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY);
while (UNDEF_P(result = vm_exec_handle_exception(ec, state, result))) {
/* caught a jump, exec the handler */
result = vm_exec_core(ec);
vm_loop_start:
VM_ASSERT(ec->tag == tag);
/* when caught `throw`, `tag.state` is set. */
if ((state = tag->state) == TAG_NONE) break;
tag->state = TAG_NONE;
}

return result;
}

static inline VALUE
vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, VALUE errinfo)
Expand Down
23 changes: 7 additions & 16 deletions wasm/setjmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,21 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf
target->state = JMP_BUF_STATE_CAPTURED;

switch ((enum try_catch_phase)try_catch->state) {
case TRY_CATCH_PHASE_MAIN: {
case TRY_CATCH_PHASE_MAIN:
// may unwind
try_catch->try_f(try_catch->context);
break;
}
case TRY_CATCH_PHASE_RESCUE: {
case TRY_CATCH_PHASE_RESCUE:
if (try_catch->catch_f) {
// may unwind
try_catch->catch_f(try_catch->context);
}
break;
}
}

while (1) {
{
// catch longjmp with target jmp_buf
if (rb_asyncify_unwind_buf && _rb_wasm_active_jmpbuf == target) {
while (rb_asyncify_unwind_buf && _rb_wasm_active_jmpbuf == target) {
// do similar steps setjmp does when JMP_BUF_STATE_RETURNING

// stop unwinding
Expand All @@ -184,14 +182,9 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf
if (try_catch->catch_f) {
try_catch->catch_f(try_catch->context);
}
continue;
} else if (rb_asyncify_unwind_buf /* unrelated unwind */) {
return;
}
// no unwind, then exit
break;
// no unwind or unrelated unwind, then exit
}
return;
}

void *
Expand All @@ -203,18 +196,16 @@ rb_wasm_handle_jmp_unwind(void)
}

switch (_rb_wasm_active_jmpbuf->state) {
case JMP_BUF_STATE_CAPTURING: {
case JMP_BUF_STATE_CAPTURING:
RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_CAPTURING");
// save the captured Asyncify stack top
_rb_wasm_active_jmpbuf->dst_buf_top = _rb_wasm_active_jmpbuf->setjmp_buf.top;
break;
}
case JMP_BUF_STATE_RETURNING: {
case JMP_BUF_STATE_RETURNING:
RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_RETURNING");
// restore the saved Asyncify stack top
_rb_wasm_active_jmpbuf->setjmp_buf.top = _rb_wasm_active_jmpbuf->dst_buf_top;
break;
}
default:
assert(0 && "unexpected state");
}
Expand Down