Skip to content

ZJIT: invokeblock IFUNC specialization drops keyword-ness of literal kwargs #1018

Description

@dak2

Overview

ZJIT specializes invokeblock into gen_invokeblock_ifunc when the profiled block handler is an IFUNC.
That path drops the callinfo and passes the raw argv to rb_vm_yield_with_cfunc, which hardcodes kw_splat = 0 (vm_insnhelper.c:5201).

The interpreter's vm_invoke_ifunc_block instead runs CALLER_SETUP_ARG and sets kw_splat = 1, so literal keyword arguments get folded into a Hash.

Details

The IFUNC gate in zjit/src/hir.rs:9312 only checks two flags

let is_ifunc = (flags & (VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT)) == 0
    && block_handler_class.is_some_and(|obj| unsafe { rb_IMEMO_TYPE_P(obj, imemo_ifunc) == 1 });

VM_CALL_KWARG is missing from is_ifunc.

Reproduction

normal

dak2 build (master) % ./miniruby -e 'def foo; yield 1, a: 2; end; 2.times { p enum_for(:foo).to_a }'
[[1, {a: 2}]]
[[1, {a: 2}]]

with --zjit

dak2 build (master) % ./miniruby --zjit --zjit-call-threshold=2 -e 'def foo; yield 1, a: 2; end; 2.times { p enum_for(:foo).to_a }'
[[1, {a: 2}]]
[[1, 2]]

Environment

dak2 build (master) % ./miniruby -v
ruby 4.1.0dev (2026-07-18T20:35:38Z master 7802cdbecd) +PRISM [arm64-darwin25]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions