Skip to content

Commit 0f89fa9

Browse files
authored
ZJIT: Inline BasicObject#! (ruby#15201)
1 parent 32b8f97 commit 0f89fa9

File tree

2 files changed

+111
-4
lines changed

2 files changed

+111
-4
lines changed

zjit/src/cruby_methods.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ pub fn init() -> Annotations {
221221
annotate!(rb_mKernel, "nil?", inline_kernel_nil_p);
222222
annotate!(rb_mKernel, "respond_to?", inline_kernel_respond_to_p);
223223
annotate!(rb_cBasicObject, "==", inline_basic_object_eq, types::BoolExact, no_gc, leaf, elidable);
224-
annotate!(rb_cBasicObject, "!", types::BoolExact, no_gc, leaf, elidable);
224+
annotate!(rb_cBasicObject, "!", inline_basic_object_not, types::BoolExact, no_gc, leaf, elidable);
225225
annotate!(rb_cBasicObject, "!=", inline_basic_object_neq, types::BoolExact);
226226
annotate!(rb_cBasicObject, "initialize", inline_basic_object_initialize);
227227
annotate!(rb_cInteger, "succ", inline_integer_succ);
@@ -517,6 +517,19 @@ fn inline_basic_object_eq(fun: &mut hir::Function, block: hir::BlockId, recv: hi
517517
Some(result)
518518
}
519519

520+
fn inline_basic_object_not(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option<hir::InsnId> {
521+
let &[] = args else { return None; };
522+
if fun.type_of(recv).is_known_truthy() {
523+
let result = fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(Qfalse) });
524+
return Some(result);
525+
}
526+
if fun.type_of(recv).is_known_falsy() {
527+
let result = fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(Qtrue) });
528+
return Some(result);
529+
}
530+
None
531+
}
532+
520533
fn inline_basic_object_neq(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> {
521534
let &[other] = args else { return None; };
522535
let result = try_inline_fixnum_op(fun, block, &|left, right| hir::Insn::FixnumNeq { left, right }, BOP_NEQ, recv, other, state);

zjit/src/hir/opt_tests.rs

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4602,7 +4602,7 @@ mod hir_opt_tests {
46024602
}
46034603

46044604
#[test]
4605-
fn test_specialize_basicobject_not_to_ccall() {
4605+
fn test_specialize_basicobject_not_truthy() {
46064606
eval("
46074607
def test(a) = !a
46084608
@@ -4622,10 +4622,104 @@ mod hir_opt_tests {
46224622
PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010)
46234623
PatchPoint NoSingletonClass(Array@0x1000)
46244624
v23:ArrayExact = GuardType v9, ArrayExact
4625+
v24:FalseClass = Const Value(false)
46254626
IncrCounter inline_cfunc_optimized_send_count
4626-
v25:BoolExact = CCall !@0x1038, v23
46274627
CheckInterrupts
4628-
Return v25
4628+
Return v24
4629+
");
4630+
}
4631+
4632+
#[test]
4633+
fn test_specialize_basicobject_not_false() {
4634+
eval("
4635+
def test(a) = !a
4636+
4637+
test(false)
4638+
");
4639+
assert_snapshot!(hir_string("test"), @r"
4640+
fn test@<compiled>:2:
4641+
bb0():
4642+
EntryPoint interpreter
4643+
v1:BasicObject = LoadSelf
4644+
v2:BasicObject = GetLocal l0, SP@4
4645+
Jump bb2(v1, v2)
4646+
bb1(v5:BasicObject, v6:BasicObject):
4647+
EntryPoint JIT(0)
4648+
Jump bb2(v5, v6)
4649+
bb2(v8:BasicObject, v9:BasicObject):
4650+
PatchPoint MethodRedefined(FalseClass@0x1000, !@0x1008, cme:0x1010)
4651+
v22:FalseClass = GuardType v9, FalseClass
4652+
v23:TrueClass = Const Value(true)
4653+
IncrCounter inline_cfunc_optimized_send_count
4654+
CheckInterrupts
4655+
Return v23
4656+
");
4657+
}
4658+
4659+
#[test]
4660+
fn test_specialize_basicobject_not_nil() {
4661+
eval("
4662+
def test(a) = !a
4663+
4664+
test(nil)
4665+
");
4666+
assert_snapshot!(hir_string("test"), @r"
4667+
fn test@<compiled>:2:
4668+
bb0():
4669+
EntryPoint interpreter
4670+
v1:BasicObject = LoadSelf
4671+
v2:BasicObject = GetLocal l0, SP@4
4672+
Jump bb2(v1, v2)
4673+
bb1(v5:BasicObject, v6:BasicObject):
4674+
EntryPoint JIT(0)
4675+
Jump bb2(v5, v6)
4676+
bb2(v8:BasicObject, v9:BasicObject):
4677+
PatchPoint MethodRedefined(NilClass@0x1000, !@0x1008, cme:0x1010)
4678+
v22:NilClass = GuardType v9, NilClass
4679+
v23:TrueClass = Const Value(true)
4680+
IncrCounter inline_cfunc_optimized_send_count
4681+
CheckInterrupts
4682+
Return v23
4683+
");
4684+
}
4685+
4686+
#[test]
4687+
fn test_specialize_basicobject_not_falsy() {
4688+
eval("
4689+
def test(a) = !(if a then false else nil end)
4690+
4691+
# TODO(max): Make this not GuardType NilClass and instead just reason
4692+
# statically
4693+
test(false)
4694+
test(true)
4695+
");
4696+
assert_snapshot!(hir_string("test"), @r"
4697+
fn test@<compiled>:2:
4698+
bb0():
4699+
EntryPoint interpreter
4700+
v1:BasicObject = LoadSelf
4701+
v2:BasicObject = GetLocal l0, SP@4
4702+
Jump bb2(v1, v2)
4703+
bb1(v5:BasicObject, v6:BasicObject):
4704+
EntryPoint JIT(0)
4705+
Jump bb2(v5, v6)
4706+
bb2(v8:BasicObject, v9:BasicObject):
4707+
CheckInterrupts
4708+
v15:CBool = Test v9
4709+
IfFalse v15, bb3(v8, v9)
4710+
v18:FalseClass = Const Value(false)
4711+
CheckInterrupts
4712+
Jump bb4(v8, v9, v18)
4713+
bb3(v22:BasicObject, v23:BasicObject):
4714+
v26:NilClass = Const Value(nil)
4715+
Jump bb4(v22, v23, v26)
4716+
bb4(v28:BasicObject, v29:BasicObject, v30:NilClass|FalseClass):
4717+
PatchPoint MethodRedefined(NilClass@0x1000, !@0x1008, cme:0x1010)
4718+
v41:NilClass = GuardType v30, NilClass
4719+
v42:TrueClass = Const Value(true)
4720+
IncrCounter inline_cfunc_optimized_send_count
4721+
CheckInterrupts
4722+
Return v42
46294723
");
46304724
}
46314725

0 commit comments

Comments
 (0)