summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yjit.c6
-rw-r--r--yjit/bindgen/src/main.rs1
-rw-r--r--yjit/src/codegen.rs43
-rw-r--r--yjit/src/cruby_bindings.inc.rs1
-rw-r--r--yjit/src/stats.rs1
5 files changed, 42 insertions, 10 deletions
diff --git a/yjit.c b/yjit.c
index 4e9fed35ae..207974073b 100644
--- a/yjit.c
+++ b/yjit.c
@@ -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,