diff options
-rw-r--r-- | yjit.c | 6 | ||||
-rw-r--r-- | yjit/bindgen/src/main.rs | 1 | ||||
-rw-r--r-- | yjit/src/codegen.rs | 43 | ||||
-rw-r--r-- | yjit/src/cruby_bindings.inc.rs | 1 | ||||
-rw-r--r-- | yjit/src/stats.rs | 1 |
5 files changed, 42 insertions, 10 deletions
@@ -660,6 +660,12 @@ rb_get_iseq_flags_has_kwrest(const rb_iseq_t *iseq) } bool +rb_get_iseq_flags_anon_kwrest(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.anon_kwrest; +} + +bool rb_get_iseq_flags_has_rest(const rb_iseq_t *iseq) { return iseq->body->param.flags.has_rest; diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index ffe56619cc..a1b8cf3a75 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -421,6 +421,7 @@ fn main() { .allowlist_function("rb_get_iseq_flags_has_rest") .allowlist_function("rb_get_iseq_flags_has_post") .allowlist_function("rb_get_iseq_flags_has_kwrest") + .allowlist_function("rb_get_iseq_flags_anon_kwrest") .allowlist_function("rb_get_iseq_flags_has_block") .allowlist_function("rb_get_iseq_flags_ambiguous_param0") .allowlist_function("rb_get_iseq_flags_accepts_no_kwarg") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 28436a402b..6a518292ab 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -1530,6 +1530,19 @@ fn gen_splatkw( // If a compile-time hash operand is T_HASH, just guard that it's T_HASH. let hash_opnd = asm.stack_opnd(1); guard_object_is_hash(asm, hash_opnd, hash_opnd.into(), Counter::splatkw_not_hash); + } else if comptime_hash.nil_p() { + // Speculate we'll see nil if compile-time hash operand is nil + let hash_opnd = asm.stack_opnd(1); + let hash_opnd_type = asm.ctx.get_opnd_type(hash_opnd.into()); + + if hash_opnd_type != Type::Nil { + asm.cmp(hash_opnd, Qnil.into()); + asm.jne(Target::side_exit(Counter::splatkw_not_nil)); + + if Type::Nil.diff(hash_opnd_type) != TypeDiff::Incompatible { + asm.ctx.upgrade_opnd_type(hash_opnd.into(), Type::Nil); + } + } } else { // Otherwise, call #to_hash on the operand if it's not nil. @@ -7283,6 +7296,7 @@ fn gen_iseq_kw_call( unsafe { get_cikw_keyword_len(ci_kwarg) } }; let caller_keyword_len: usize = caller_keyword_len_i32.try_into().unwrap(); + let anon_kwrest = unsafe { rb_get_iseq_flags_anon_kwrest(iseq) }; // This struct represents the metadata about the callee-specified // keyword parameters. @@ -7363,16 +7377,25 @@ fn gen_iseq_kw_call( } } - // Save PC and SP before allocating - jit_save_pc(jit, asm); - gen_save_sp(asm); + let (kwrest, kwrest_type) = if rest_mask == 0 && anon_kwrest { + // In case the kwrest hash should be empty and is anonymous in the callee, + // we can pass nil instead of allocating. Anonymous kwrest can only be + // delegated, and nil is the same as an empty hash when delegating. + (Qnil.into(), Type::Nil) + } else { + // Save PC and SP before allocating + jit_save_pc(jit, asm); + gen_save_sp(asm); + + // Build the kwrest hash. `struct rb_callinfo_kwarg` is malloc'd, so no GC concerns. + let kwargs_start = asm.lea(asm.ctx.sp_opnd(-caller_keyword_len_i32 * SIZEOF_VALUE_I32)); + let hash = asm.ccall( + build_kw_rest as _, + vec![rest_mask.into(), kwargs_start, Opnd::const_ptr(ci_kwarg.cast())] + ); + (hash, Type::THash) + }; - // Build the kwrest hash. `struct rb_callinfo_kwarg` is malloc'd, so no GC concerns. - let kwargs_start = asm.lea(asm.ctx.sp_opnd(-caller_keyword_len_i32 * SIZEOF_VALUE_I32)); - let kwrest = asm.ccall( - build_kw_rest as _, - vec![rest_mask.into(), kwargs_start, Opnd::const_ptr(ci_kwarg.cast())] - ); // The kwrest parameter sits after `unspecified_bits` if the callee specifies any // keywords. let stack_kwrest_idx = kwargs_stack_base - callee_kw_count_i32 - i32::from(callee_kw_count > 0); @@ -7394,7 +7417,7 @@ fn gen_iseq_kw_call( asm.ctx.dealloc_temp_reg(stack_kwrest.stack_idx()); asm.mov(stack_kwrest, kwrest); if stack_kwrest_idx >= 0 { - asm.ctx.set_opnd_mapping(stack_kwrest.into(), TempMapping::map_to_stack(Type::THash)); + asm.ctx.set_opnd_mapping(stack_kwrest.into(), TempMapping::map_to_stack(kwrest_type)); } } diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index d7bfc2a0c9..ec874df2a0 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1149,6 +1149,7 @@ extern "C" { pub fn rb_get_iseq_flags_has_kw(iseq: *const rb_iseq_t) -> bool; pub fn rb_get_iseq_flags_has_post(iseq: *const rb_iseq_t) -> bool; pub fn rb_get_iseq_flags_has_kwrest(iseq: *const rb_iseq_t) -> bool; + pub fn rb_get_iseq_flags_anon_kwrest(iseq: *const rb_iseq_t) -> bool; pub fn rb_get_iseq_flags_has_rest(iseq: *const rb_iseq_t) -> bool; pub fn rb_get_iseq_flags_ruby2_keywords(iseq: *const rb_iseq_t) -> bool; pub fn rb_get_iseq_flags_has_block(iseq: *const rb_iseq_t) -> bool; diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index 5a76c306d9..4211711180 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -544,6 +544,7 @@ make_counters! { objtostring_not_string, splatkw_not_hash, + splatkw_not_nil, binding_allocations, binding_set, |