Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions tool/sync_default_gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@ def sync_default_gems(gem)

upstream = File.join("..", "..", config.upstream)

unless File.exist?(upstream)
abort %[Expected '#{upstream}' (#{File.expand_path("#{upstream}")}) to be a directory, but it didn't exist.]
end

config.mappings.each do |src, dst|
rm_rf(dst)
end
Expand Down Expand Up @@ -798,26 +802,6 @@ def sync_default_gems_with_commits(gem, ranges, edit: nil)
return true
end

def sync_lib(repo, upstream = nil)
unless upstream and File.directory?(upstream) or File.directory?(upstream = "../#{repo}")
abort %[Expected '#{upstream}' \(#{File.expand_path("#{upstream}")}\) to be a directory, but it wasn't.]
end
rm_rf(["lib/#{repo}.rb", "lib/#{repo}/*", "test/test_#{repo}.rb"])
cp_r(Dir.glob("#{upstream}/lib/*"), "lib")
tests = if File.directory?("test/#{repo}")
"test/#{repo}"
else
"test/test_#{repo}.rb"
end
cp_r("#{upstream}/#{tests}", "test") if File.exist?("#{upstream}/#{tests}")
gemspec = if File.directory?("lib/#{repo}")
"lib/#{repo}/#{repo}.gemspec"
else
"lib/#{repo}.gemspec"
end
cp_r("#{upstream}/#{repo}.gemspec", "#{gemspec}")
end

def update_default_gems(gem, release: false)
config = REPOSITORIES[gem]
author, repository = config.upstream.split('/')
Expand Down
1 change: 1 addition & 0 deletions zjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2899,6 +2899,7 @@ fn gen_string_append_codepoint(jit: &mut JITState, asm: &mut Assembler, string:
/// Generate a JIT entry that just increments exit_compilation_failure and exits
fn gen_compile_error_counter(cb: &mut CodeBlock, compile_error: &CompileError) -> Result<CodePtr, CompileError> {
let mut asm = Assembler::new();
asm.new_block_without_id();
gen_incr_counter(&mut asm, exit_compile_error);
gen_incr_counter(&mut asm, exit_counter_for_compile_error(compile_error));
asm.cret(Qundef.into());
Expand Down
10 changes: 5 additions & 5 deletions zjit/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6293,15 +6293,15 @@ 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);
let _branch_id = fun.push_insn(block, Insn::IfFalse {
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));
Expand All @@ -6313,15 +6313,15 @@ 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);
let _branch_id = fun.push_insn(block, Insn::IfTrue {
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));
Expand All @@ -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));
Expand Down
49 changes: 49 additions & 0 deletions zjit/src/hir/opt_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
");
}
}
63 changes: 63 additions & 0 deletions zjit/src/hir/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
60 changes: 0 additions & 60 deletions zjit/src/hir_type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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);
}
}