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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions