diff options
| author | Godfrey Chan <godfreykfc@gmail.com> | 2026-01-28 13:32:33 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-28 21:32:33 +0000 |
| commit | 2d5460e4d78032584dbf261259a8b05b856dffdf (patch) | |
| tree | e784124c767d16ffcd67ffb383b4a38e0ae4d79e /lib/soap/header/handler.rb | |
| parent | 72f11c8bfaedb4260fe9d29b2f9bd26d83b8c18a (diff) | |
~~Two commits:~~
### zjit: Optimize send-with-block to iseq methods
This commit enables JIT-to-JIT calls for send instructions with literal blocks (e.g., `foo { |x| x * 2 }`), rather than falling back to the `rb_vm_send` C wrapper. This optimization applies to both methods with explicit block parameters (`def foo(&block)`) and methods implicitly accepting a block (`def foo; yield if block_given?; end`).
Prior to this change, any callsite with a block would miss out on the JIT-to-JIT fast path and goes through a `rb_vm_send` C wrapper call.
Initially, as https://github.com/Shopify/ruby/issues/931 suggested, we assumed this would involve changes to the JIT-to-JIT calling convention to accommodate passing a block argument. However, during implementation, I discovered that @nirvdrum had already wired up the `specval` protocol used by the interpreter in their `invokesuper` work (https://github.com/ruby/ruby/pull/887). That infrastructure remained dormant but was exactly what we needed here. After plumbing everything through, it Just Worked™.
It may be possible to design a more direct JIT-to-JIT protocol for passing blocks. In the HIR for `def foo(&block)`, the BB for the JIT entrypoint already takes two arguments (self + &block, presumably), and since `yield` is a keyword, it may be possible to rewrite the implicit case to be explicit (thanks @tenderlove for the idea), and do "better" than passing via `specval`.
I'm not sure if that's a goal eventually, but in any case, if `specval` works, there is no harm in enabling this optimization today.
Implementation notes:
This initial pass largely duplicates the existing `SendWithoutBlock` to `SendWithoutBlockDirect` specialization logic. A future refactor could potentially collapse Send and SendWithoutBlock into a single instruction variant (with `blockiseq: Option<IseqPtr>`, you can always pattern match the Option if needed), since they now follow very similar paths.
However, I wanted to keep this PR focused and also get feedback on that direction first before committing to such a big refactor.
The optimization currently handles `VM_METHOD_TYPE_ISEQ` only. It does not handle "block to block" `VM_METHOD_TYPE_BMETHOD`. It's unclear if that'd be all that difficult, I just didn't try. Happy to do it as a follow-up.
Any callsites not handled by this specialization continue to fallthrough to the existing rb_vm_send harness safely.
Test coverage includes both explicit block parameters and yield-based methods.
Thanks to @tenderlove for initial ideas and walkthrough, and @nirvdrum for the foundation this builds on.
Closes https://github.com/Shopify/ruby/issues/931
### ~~zjit: Allow SendWithoutBlockDirect to def f(&blk)~~
Saving this for another time
### Follow-ups
* [ ] Refactor and simplify by merging `hir::Insn::Send` and `hir::Insn::SendWithoutBlock`. (Pending feedback/approval.)
* [ ] Handle block-to-block `VM_METHOD_TYPE_BMETHOD` calls. It appears that `gen_send_iseq_direct` already attempted to handle it.
* [ ] As far as I can tell, we should be able to just enable `super { ... }` too, happy to do that as a follow-up if @nirvdrum doesn't have time for it.
Diffstat (limited to 'lib/soap/header/handler.rb')
0 files changed, 0 insertions, 0 deletions
