Skip to content

Commit 71b787e

Browse files
committed
ZJIT: Inline Hash#key? and its aliases
1 parent c4f6c55 commit 71b787e

7 files changed

Lines changed: 67 additions & 6 deletions

File tree

test/ruby/test_zjit.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,46 @@ def test(arr) = arr.pop(2)
13971397
}, call_threshold: 2
13981398
end
13991399

1400+
def test_opt_hash_key_p
1401+
assert_compiles 'true', %q{
1402+
def test(h, k) = h.key?(k)
1403+
test({a: 1, b: 2}, :a)
1404+
test({a: 1, b: 2}, :a)
1405+
}, call_threshold: 2
1406+
end
1407+
1408+
def test_opt_hash_key_p_false
1409+
assert_compiles 'false', %q{
1410+
def test(h, k) = h.key?(k)
1411+
test({a: 1, b: 2}, :c)
1412+
test({a: 1, b: 2}, :c)
1413+
}, call_threshold: 2
1414+
end
1415+
1416+
def test_opt_hash_has_key
1417+
assert_compiles 'true', %q{
1418+
def test(h, k) = h.has_key?(k)
1419+
test({a: 1}, :a)
1420+
test({a: 1}, :a)
1421+
}, call_threshold: 2
1422+
end
1423+
1424+
def test_opt_hash_include
1425+
assert_compiles 'true', %q{
1426+
def test(h, k) = h.include?(k)
1427+
test({a: 1}, :a)
1428+
test({a: 1}, :a)
1429+
}, call_threshold: 2
1430+
end
1431+
1432+
def test_opt_hash_member
1433+
assert_compiles 'true', %q{
1434+
def test(h, k) = h.member?(k)
1435+
test({a: 1}, :a)
1436+
test({a: 1}, :a)
1437+
}, call_threshold: 2
1438+
end
1439+
14001440
def test_new_range_inclusive
14011441
assert_compiles '1..5', %q{
14021442
def test(a, b) = a..b

zjit/bindgen/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ fn main() {
118118
.allowlist_type("st_retval")
119119
.allowlist_function("rb_hash_aset")
120120
.allowlist_function("rb_hash_aref")
121+
.allowlist_function("rb_hash_has_key")
121122
.allowlist_function("rb_hash_bulk_insert")
122123
.allowlist_function("rb_hash_stlike_lookup")
123124
.allowlist_function("rb_ary_new_capa")

zjit/src/codegen.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
466466
&Insn::CheckInterrupts { state } => no_output!(gen_check_interrupts(jit, asm, &function.frame_state(state))),
467467
&Insn::HashDup { val, state } => { gen_hash_dup(asm, opnd!(val), &function.frame_state(state)) },
468468
&Insn::HashAref { hash, key, state } => { gen_hash_aref(jit, asm, opnd!(hash), opnd!(key), &function.frame_state(state)) },
469+
&Insn::HashKeyP { hash, key, state } => { gen_hash_key_p(jit, asm, opnd!(hash), opnd!(key), &function.frame_state(state)) },
469470
&Insn::ArrayPush { array, val, state } => { no_output!(gen_array_push(asm, opnd!(array), opnd!(val), &function.frame_state(state))) },
470471
&Insn::ToNewArray { val, state } => { gen_to_new_array(jit, asm, opnd!(val), &function.frame_state(state)) },
471472
&Insn::ToArray { val, state } => { gen_to_array(jit, asm, opnd!(val), &function.frame_state(state)) },
@@ -1048,6 +1049,11 @@ fn gen_hash_aref(jit: &mut JITState, asm: &mut Assembler, hash: Opnd, key: Opnd,
10481049
asm_ccall!(asm, rb_hash_aref, hash, key)
10491050
}
10501051

1052+
fn gen_hash_key_p(jit: &mut JITState, asm: &mut Assembler, hash: Opnd, key: Opnd, state: &FrameState) -> lir::Opnd {
1053+
gen_prepare_non_leaf_call(jit, asm, state);
1054+
asm_ccall!(asm, rb_hash_has_key, hash, key)
1055+
}
1056+
10511057
fn gen_array_push(asm: &mut Assembler, array: Opnd, val: Opnd, state: &FrameState) {
10521058
gen_prepare_leaf_call_with_gc(asm, state);
10531059
asm_ccall!(asm, rb_ary_push, array, val);

zjit/src/cruby_bindings.inc.rs

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

zjit/src/cruby_methods.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,10 @@ pub fn init() -> Annotations {
232232
annotate!(rb_cHash, "size", types::Fixnum, no_gc, leaf, elidable);
233233
annotate!(rb_cHash, "empty?", types::BoolExact, no_gc, leaf, elidable);
234234
// These 4 are all aliases for rb_hash_has_key, which can call #hash and #eql? on the key
235-
annotate!(rb_cHash, "key?", types::BoolExact);
236-
annotate!(rb_cHash, "has_key?", types::BoolExact);
237-
annotate!(rb_cHash, "include?", types::BoolExact);
238-
annotate!(rb_cHash, "member?", types::BoolExact);
235+
annotate!(rb_cHash, "key?", inline_hash_key_p, types::BoolExact);
236+
annotate!(rb_cHash, "has_key?", inline_hash_key_p, types::BoolExact);
237+
annotate!(rb_cHash, "include?", inline_hash_key_p, types::BoolExact);
238+
annotate!(rb_cHash, "member?", inline_hash_key_p, types::BoolExact);
239239
annotate!(rb_cNilClass, "nil?", inline_nilclass_nil_p);
240240
annotate!(rb_mKernel, "nil?", inline_kernel_nil_p);
241241
annotate!(rb_mKernel, "respond_to?", inline_kernel_respond_to_p);
@@ -361,6 +361,13 @@ fn inline_hash_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::Ins
361361
None
362362
}
363363

364+
fn inline_hash_key_p(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> {
365+
if let &[key] = args {
366+
let result = fun.push_insn(block, hir::Insn::HashKeyP { hash: recv, key, state });
367+
return Some(result);
368+
}
369+
None
370+
}
364371

365372
fn inline_string_bytesize(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> {
366373
if args.is_empty() && fun.likely_a(recv, types::String, state) {

zjit/src/hir.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@ pub enum Insn {
685685
ArrayLength { array: InsnId },
686686

687687
HashAref { hash: InsnId, key: InsnId, state: InsnId },
688+
HashKeyP { hash: InsnId, key: InsnId, state: InsnId },
688689
HashDup { val: InsnId, state: InsnId },
689690

690691
/// Allocate an instance of the `val` object without calling `#initialize` on it.
@@ -1127,6 +1128,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
11271128
Insn::ArrayDup { val, .. } => { write!(f, "ArrayDup {val}") }
11281129
Insn::HashDup { val, .. } => { write!(f, "HashDup {val}") }
11291130
Insn::HashAref { hash, key, .. } => { write!(f, "HashAref {hash}, {key}")}
1131+
Insn::HashKeyP { hash, key, .. } => { write!(f, "HashKeyP {hash}, {key}")}
11301132
Insn::ObjectAlloc { val, .. } => { write!(f, "ObjectAlloc {val}") }
11311133
&Insn::ObjectAllocClass { class, .. } => {
11321134
let class_name = get_class_name(class);
@@ -1960,6 +1962,7 @@ impl Function {
19601962
&ArrayDup { val, state } => ArrayDup { val: find!(val), state },
19611963
&HashDup { val, state } => HashDup { val: find!(val), state },
19621964
&HashAref { hash, key, state } => HashAref { hash: find!(hash), key: find!(key), state },
1965+
&HashKeyP { hash, key, state } => HashKeyP { hash: find!(hash), key: find!(key), state },
19631966
&ObjectAlloc { val, state } => ObjectAlloc { val: find!(val), state },
19641967
&ObjectAllocClass { class, state } => ObjectAllocClass { class, state: find!(state) },
19651968
&CCall { cfunc, recv, ref args, name, return_type, elidable } => CCall { cfunc, recv: find!(recv), args: find_vec!(args), name, return_type, elidable },
@@ -2096,6 +2099,7 @@ impl Function {
20962099
Insn::ArrayPop { .. } => types::BasicObject,
20972100
Insn::ArrayLength { .. } => types::CInt64,
20982101
Insn::HashAref { .. } => types::BasicObject,
2102+
Insn::HashKeyP { .. } => types::BoolExact,
20992103
Insn::NewHash { .. } => types::HashExact,
21002104
Insn::HashDup { .. } => types::HashExact,
21012105
Insn::NewRange { .. } => types::RangeExact,
@@ -3778,7 +3782,7 @@ impl Function {
37783782
&Insn::ArrayLength { array } => {
37793783
worklist.push_back(array);
37803784
}
3781-
&Insn::HashAref { hash, key, state } => {
3785+
&Insn::HashAref { hash, key, state } | &Insn::HashKeyP { hash, key, state } => {
37823786
worklist.push_back(hash);
37833787
worklist.push_back(key);
37843788
worklist.push_back(state);
@@ -4471,6 +4475,7 @@ impl Function {
44714475
}
44724476
// Instructions with Hash operands
44734477
Insn::HashAref { hash, .. } => self.assert_subtype(insn_id, hash, types::Hash),
4478+
Insn::HashKeyP { hash, .. } => self.assert_subtype(insn_id, hash, types::Hash),
44744479
Insn::HashDup { val, .. } => self.assert_subtype(insn_id, val, types::HashExact),
44754480
// Other
44764481
Insn::ObjectAllocClass { class, .. } => {

zjit/src/hir/opt_tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5139,7 +5139,8 @@ mod hir_opt_tests {
51395139
PatchPoint MethodRedefined(Hash@0x1000, key?@0x1008, cme:0x1010)
51405140
PatchPoint NoSingletonClass(Hash@0x1000)
51415141
v26:HashExact = GuardType v11, HashExact
5142-
v27:BoolExact = CCallWithFrame v26, :Hash#key?@0x1038, v12
5142+
v27:BoolExact = HashKeyP v26, v12
5143+
IncrCounter inline_cfunc_optimized_send_count
51435144
CheckInterrupts
51445145
Return v27
51455146
");

0 commit comments

Comments
 (0)