8000 Begin wiring up direct keyword args by headius · Pull Request #8851 · jruby/jruby · GitHub
[go: up one dir, main page]

Skip to content

Begin wiring up direct keyword args #8851

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

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

headius
Copy link
Member
@headius headius commented May 28, 2025

This is the beginning of passing keyword arguments directly on the stack, without a transient RubyHash object to carry them.

The first pass of this work plumbs the keywords directly through to invokedynamic call sites by making the following changes:

  • The Hash operand used by CallInstr for keyword args is now directly in its Operand list.
  • The IR visitor can see that Hash operand and use it to emit the keyword argument values directly on the stack.
  • The indy call sites receive all kwarg values along with metadata for the keys they represent and handles it from there.

Initially, the indy site will simply reconstitute the keyword names and values into a RubyHash as before, passing it on the stack as normal. The missing piece to continue passing them through is a way to detect that the callee accepts those keyword arguments. Once that piece is in place, the indy site will be able to pass the arguments on the stack straight through to a target method, which will see only the argument values and not have to query a full RubyHash object to get them.

headius added 4 commits May 29, 2025 12:56
In order to go further with directly passing keyword arguments on
the call stack, we need call instructions to aggregate information
on the kwargs structure. Previous to this commit, the kwargs hash
was constructed as an entirely separate operand, copied to the
call's parameters via a temporary variable. This effectively hid
the actual hash structure from logic reading the call object.

The patch here embeds the operand for the Hash directly into the
call's argument list, allowing for it to be processed at the same
time as the call. The keys and values are still processed as their
own operands at call time, and any elaborate hash or argument list
construction pulls the hash back out to its own operand. The change
here only allows simple kwargs to be directly associated with the
call that will use them.

The IR output illustrates the change more clearly.

For a call like "foo(1, bar: 1, baz: 2, &boo)", here's old IR:

  02:  %v_1 := call_0o(self<%self>, callType: VARIABLE, name: baz, potentiallyRefined: false, flags: 0)
  03:  %v_2 := call_0o(self<%self>, callType: VARIABLE, name: quux, potentiallyRefined: false, flags: 0)
  04:  %v_3 := copy(hash<sym<a>=>%v_1,sym<b>=>%v_2>)
  05:  %v_4 := call_0o(self<%self>, callType: VARIABLE, name: boo, potentiallyRefined: false, flags: 0)
  06:  %v_0 := call(self<%self>, fix<1>, %v_3, %v_4, callType: FUNCTIONAL, name: foo, potentiallyRefined: false, flags: 2)

And here is the new IR, with the Hash directly in the call's
list of operands:

  2:  %v_1 := call_0o(self<%self>, callType: VARIABLE, name: baz, potentiallyRefined: false, flags: 0)
  3:  %v_2 := call_0o(self<%self>, callType: VARIABLE, name: quux, potentiallyRefined: false, flags: 0)
  4:  %v_3 := call_0o(self<%self>, callType: VARIABLE, name: boo, potentiallyRefined: false, flags: 0)
  5:  %v_0 := call(self<%self>, fix<1>, hash<sym<a>=>%v_1,sym<b>=>%v_2>, %v_3, callType: FUNCTIONAL, name: foo, potentiallyRefined: false, flags: 2)

In both cases, all arguments and all operands to the hash are
accessed and constructed in the same order, but there's no temp
var required to hold the intermediate hash. It is directly
associated with the call.
This is a first step toward passing keyword arguments all the way
through the stack without allocating a Hash object. When we detect
that there's a trailing Hash operand in the argument list, we know
it must be a keyword arguments hash. Rather than eagerly creating
the Hash, we instead evaluate its values as normal but leave them
on the stack for the call. The invocation takes the positional and
keyword argument values along with a keyword names descriptor and
reassembles the values into a kwargs hash lazily before invoking
a normal call site.

Once the normal call process has a way to plumb kwarg values all
the way through to a target method, this will push further into
that stack.
This is the first place where the Indy compiler will start to
seriously diverge from the non-Indy version. It's just too hard
to plumb direct keyword arguments through the non-Indy compiler, so
we add a way for the IR visitor to alter compilation strategy if
running in a mode that has reduced capabilities.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
0