mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00:00
ZJIT: Clean up partial SSI (#15929)
After Kokubun requested named unions, I realized we don't actually need a `Type::subtract` function. They were only used for the ad-hoc unions. Also, add a test that is illustrative of what we can get from this partial SSI.
This commit is contained in:
parent
61724b9e4a
commit
a7a0c36b20
Notes:
git
2026-01-22 16:41:55 +00:00
Merged-By: tekknolagi <donotemailthisaddress@bernsteinbear.com>
@ -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));
|
||||
|
||||
@ -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
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3222,6 +3222,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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user