diff options
| -rw-r--r-- | zjit/src/hir.rs | 10 | ||||
| -rw-r--r-- | zjit/src/hir/opt_tests.rs | 49 | ||||
| -rw-r--r-- | zjit/src/hir/tests.rs | 63 | ||||
| -rw-r--r-- | zjit/src/hir_type/mod.rs | 60 |
4 files changed, 117 insertions, 65 deletions
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index b4f78c025d..18649b9800 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6293,7 +6293,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let test_id = fun.push_insn(block, Insn::Test { val }); let target_idx = insn_idx_at_offset(insn_idx, offset); let target = insn_idx_to_block[&target_idx]; - let nil_false_type = types::NilClass.union(types::FalseClass); + let nil_false_type = types::Falsy; let nil_false = fun.push_insn(block, Insn::RefineType { val, new_type: nil_false_type }); let mut iffalse_state = state.clone(); iffalse_state.replace(val, nil_false); @@ -6301,7 +6301,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { val: test_id, target: BranchEdge { target, args: iffalse_state.as_args(self_param) } }); - let not_nil_false_type = types::BasicObject.subtract(types::NilClass).subtract(types::FalseClass); + let not_nil_false_type = types::Truthy; let not_nil_false = fun.push_insn(block, Insn::RefineType { val, new_type: not_nil_false_type }); state.replace(val, not_nil_false); queue.push_back((state.clone(), target, target_idx, local_inval)); @@ -6313,7 +6313,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let test_id = fun.push_insn(block, Insn::Test { val }); let target_idx = insn_idx_at_offset(insn_idx, offset); let target = insn_idx_to_block[&target_idx]; - let not_nil_false_type = types::BasicObject.subtract(types::NilClass).subtract(types::FalseClass); + let not_nil_false_type = types::Truthy; let not_nil_false = fun.push_insn(block, Insn::RefineType { val, new_type: not_nil_false_type }); let mut iftrue_state = state.clone(); iftrue_state.replace(val, not_nil_false); @@ -6321,7 +6321,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { val: test_id, target: BranchEdge { target, args: iftrue_state.as_args(self_param) } }); - let nil_false_type = types::NilClass.union(types::FalseClass); + let nil_false_type = types::Falsy; let nil_false = fun.push_insn(block, Insn::RefineType { val, new_type: nil_false_type }); state.replace(val, nil_false); queue.push_back((state.clone(), target, target_idx, local_inval)); @@ -6340,7 +6340,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { val: test_id, target: BranchEdge { target, args: iftrue_state.as_args(self_param) } }); - let new_type = types::BasicObject.subtract(types::NilClass); + let new_type = types::NotNil; let not_nil = fun.push_insn(block, Insn::RefineType { val, new_type }); state.replace(val, not_nil); queue.push_back((state.clone(), target, target_idx, local_inval)); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 0a42652993..dcfcc1e136 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -11319,4 +11319,53 @@ mod hir_opt_tests { Return v31 "); } + + #[test] + fn test_infer_truthiness_from_branch() { + eval(" + def test(x) + if x + if x + if x + 3 + else + 4 + end + else + 5 + end + else + 6 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :x, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + CheckInterrupts + v15:CBool = Test v9 + v16:Falsy = RefineType v9, Falsy + IfFalse v15, bb5(v8, v16) + v18:Truthy = RefineType v9, Truthy + CheckInterrupts + v26:Truthy = RefineType v18, Truthy + CheckInterrupts + v34:Truthy = RefineType v26, Truthy + v37:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v37 + bb5(v42:BasicObject, v43:Falsy): + v47:Fixnum[6] = Const Value(6) + CheckInterrupts + Return v47 + "); + } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 44082ce908..56f1928f1f 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -3223,6 +3223,69 @@ pub mod hir_build_tests { } #[test] + fn test_infer_truthiness_from_branch() { + eval(" + def test(x) + if x + if x + if x + 3 + else + 4 + end + else + 5 + end + else + 6 + end + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :x, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + CheckInterrupts + v15:CBool = Test v9 + v16:Falsy = RefineType v9, Falsy + IfFalse v15, bb5(v8, v16) + v18:Truthy = RefineType v9, Truthy + CheckInterrupts + v23:CBool[true] = Test v18 + v24 = RefineType v18, Falsy + IfFalse v23, bb4(v8, v24) + v26:Truthy = RefineType v18, Truthy + CheckInterrupts + v31:CBool[true] = Test v26 + v32 = RefineType v26, Falsy + IfFalse v31, bb3(v8, v32) + v34:Truthy = RefineType v26, Truthy + v37:Fixnum[3] = Const Value(3) + CheckInterrupts + Return v37 + bb5(v42:BasicObject, v43:Falsy): + v47:Fixnum[6] = Const Value(6) + CheckInterrupts + Return v47 + bb4(v52, v53): + v57 = Const Value(5) + CheckInterrupts + Return v57 + bb3(v62, v63): + v67 = Const Value(4) + CheckInterrupts + Return v67 + "); + } + + #[test] fn test_invokebuiltin_delegate_annotated() { assert_contains_opcode("Float", YARVINSN_opt_invokebuiltin_delegate_leave); assert_snapshot!(hir_string("Float"), @r" diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs index 1f7526915c..cc6a208bcd 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -453,25 +453,6 @@ impl Type { types::Empty } - /// Subtract `other` from `self`, preserving specialization if possible. - pub fn subtract(&self, other: Type) -> Type { - // If self is a subtype of other, the result is empty (no negative types). - if self.is_subtype(other) { return types::Empty; } - // Self is not a subtype of other. That means either: - // * Their type bits do not overlap at all (eg Int vs String) - // * Their type bits overlap but self's specialization is not a subtype of other's (eg - // Fixnum[5] vs Fixnum[4]) - // Check for the latter case, returning self unchanged if so. - if !self.spec_is_subtype_of(other) { - return *self; - } - // Now self is either a supertype of other (eg Object vs String or Fixnum vs Fixnum[5]) or - // their type bits do not overlap at all (eg Int vs String). - // Just subtract the bits and keep self's specialization. - let bits = self.bits & !other.bits; - Type { bits, spec: self.spec } - } - pub fn could_be(&self, other: Type) -> bool { !self.intersection(other).bit_equal(types::Empty) } @@ -1079,45 +1060,4 @@ mod tests { assert!(!types::CBool.has_value(Const::CBool(true))); assert!(!types::CShape.has_value(Const::CShape(crate::cruby::ShapeId(0x1234)))); } - - #[test] - fn test_subtract_with_superset_returns_empty() { - let left = types::NilClass; - let right = types::BasicObject; - let result = left.subtract(right); - assert_bit_equal(result, types::Empty); - } - - #[test] - fn test_subtract_with_subset_removes_bits() { - let left = types::BasicObject; - let right = types::NilClass; - let result = left.subtract(right); - assert_subtype(result, types::BasicObject); - assert_not_subtype(types::NilClass, result); - } - - #[test] - fn test_subtract_with_no_overlap_returns_self() { - let left = types::Fixnum; - let right = types::StringExact; - let result = left.subtract(right); - assert_bit_equal(result, left); - } - - #[test] - fn test_subtract_with_no_specialization_overlap_returns_self() { - let left = Type::fixnum(4); - let right = Type::fixnum(5); - let result = left.subtract(right); - assert_bit_equal(result, left); - } - - #[test] - fn test_subtract_with_specialization_subset_removes_specialization() { - let left = types::Fixnum; - let right = Type::fixnum(42); - let result = left.subtract(right); - assert_bit_equal(result, types::Fixnum); - } } |
