summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zjit/src/hir.rs10
-rw-r--r--zjit/src/hir/opt_tests.rs49
-rw-r--r--zjit/src/hir/tests.rs63
-rw-r--r--zjit/src/hir_type/mod.rs60
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);
- }
}