diff options
author | Yuta Saito <kateinoigakukun@gmail.com> | 2022-01-27 21:33:39 +0900 |
---|---|---|
committer | Yuta Saito <kateinoigakukun@gmail.com> | 2022-02-18 18:28:18 +0900 |
commit | dff70b50d01930213e7799ee52969ff309cc3601 (patch) | |
tree | b01dc7fbfb0875d6b513f4c141e0d3c05f060095 /vm.c | |
parent | ac32b7023a7743b1f0cdcfe11156c95c0edb7c54 (diff) |
[wasm] vm.c: stop unwinding to main for every vm_exec call by setjmp
the original rb_wasm_setjmp implementation always unwinds to the root
call frame to have setjmp compatible interface, and simulate sjlj's
undefined behavior. Therefore, every vm_exec call unwinds to main, and
a deep call stack makes setjmp call very expensive. The following
snippet from optcarrot takes 5s even though it takes less than 0.3s on
native.
```
[0x0, 0x4, 0x8, 0xc].map do |attr|
(0..7).map do |j|
(0...0x10000).map do |i|
clr = i[15 - j] * 2 + i[7 - j]
clr != 0 ? attr | clr : 0
end
end
end
```
This patch adds a WASI specialized vm_exec which uses lightweight
try-catch API without unwinding to the root frame. After this patch, the
above snippet takes only 0.5s.
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/5502
Diffstat (limited to 'vm.c')
-rw-r--r-- | vm.c | 80 |
1 files changed, 80 insertions, 0 deletions
@@ -2196,6 +2196,85 @@ static inline VALUE vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, VALUE errinfo, VALUE *initial); +// 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; + VALUE initial; + VALUE result; + enum ruby_tag_type state; + bool mjit_enable_p; +}; + +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 ((ctx->result = vm_exec_handle_exception(ec, ctx->state, ctx->result, &ctx->initial)) == Qundef) { + /* caught a jump, exec the handler */ + ctx->result = vm_exec_core(ec, ctx->initial); + 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; + + ctx->state = TAG_NONE; + if (!ctx->mjit_enable_p || (ctx->result = mjit_exec(ctx->ec)) == Qundef) { + ctx->result = vm_exec_core(ctx->ec, ctx->initial); + } + vm_exec_enter_vm_loop(ctx->ec, ctx, ctx->tag, true); +} + +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); +} + +VALUE +vm_exec(rb_execution_context_t *ec, bool mjit_enable_p) +{ + struct rb_vm_exec_context ctx = { + .ec = ec, + .initial = 0, .result = Qundef, + .mjit_enable_p = mjit_enable_p, + }; + struct rb_wasm_try_catch try_catch; + + EC_PUSH_TAG(ec); + + _tag.retval = Qnil; + ctx.tag = &_tag; + + 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; +} + +#else + VALUE vm_exec(rb_execution_context_t *ec, bool mjit_enable_p) { @@ -2228,6 +2307,7 @@ vm_exec(rb_execution_context_t *ec, bool mjit_enable_p) EC_POP_TAG(); return result; } +#endif static inline VALUE vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, |