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
3 changes: 2 additions & 1 deletion io.c
Original file line number Diff line number Diff line change
Expand Up @@ -7835,7 +7835,8 @@ static VALUE popen_finish(VALUE port, VALUE klass);
* If a block is given, the stream is passed to the block
* (again, open for reading, writing, or both);
* when the block exits, the stream is closed,
* and the block's value is assigned to global variable <tt>$?</tt> and returned.
* the block's value is returned,
* and the global variable <tt>$?</tt> is set to the child's exit status.
*
* Optional argument +mode+ may be any valid \IO mode.
* See {Access Modes}[rdoc-ref:File@Access+Modes].
Expand Down
18 changes: 10 additions & 8 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1136,19 +1136,21 @@ def test(n) = n..10
def test_new_range_fixnum_both_literals_inclusive
assert_compiles '1..2', %q{
def test()
(1..2)
a = 2
(1..a)
end
test; test
}, call_threshold: 2
}, call_threshold: 2, insns: [:newrange]
end

def test_new_range_fixnum_both_literals_exclusive
assert_compiles '1...2', %q{
def test()
(1...2)
a = 2
(1...a)
end
test; test
}, call_threshold: 2
}, call_threshold: 2, insns: [:newrange]
end

def test_new_range_fixnum_low_literal_inclusive
Expand All @@ -1157,7 +1159,7 @@ def test(a)
(1..a)
end
test(2); test(3)
}, call_threshold: 2
}, call_threshold: 2, insns: [:newrange]
end

def test_new_range_fixnum_low_literal_exclusive
Expand All @@ -1166,7 +1168,7 @@ def test(a)
(1...a)
end
test(2); test(3)
}, call_threshold: 2
}, call_threshold: 2, insns: [:newrange]
end

def test_new_range_fixnum_high_literal_inclusive
Expand All @@ -1175,7 +1177,7 @@ def test(a)
(a..10)
end
test(2); test(3)
}, call_threshold: 2
}, call_threshold: 2, insns: [:newrange]
end

def test_new_range_fixnum_high_literal_exclusive
Expand All @@ -1184,7 +1186,7 @@ def test(a)
(a...10)
end
test(2); test(3)
}, call_threshold: 2
}, call_threshold: 2, insns: [:newrange]
end

def test_if
Expand Down
8 changes: 5 additions & 3 deletions tool/zjit_bisect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
end
end.parse!

RUBY = ARGV[0] || raise("Usage: ruby jit_bisect.rb <path_to_ruby> -- <options>")
usage = "Usage: zjit_bisect.rb <path_to_ruby> -- <options>"
RUBY = ARGV[0] || raise(usage)
OPTIONS = ARGV[1..]
raise("Usage: ruby jit_bisect.rb <path_to_ruby> -- <options>") if OPTIONS.empty?
raise(usage) if OPTIONS.empty?
LOGGER = Logger.new($stdout)

# From https://github.com/tekknolagi/omegastar
Expand Down Expand Up @@ -103,7 +104,8 @@ def run_with_jit_list(ruby, options, jit_list)

# Try running with no JIT list to get a stable baseline
unless run_with_jit_list(RUBY, OPTIONS, []).success?
raise "Command failed with empty JIT list"
cmd = [RUBY, "--zjit-allowed-iseqs=/dev/null", *OPTIONS].shelljoin
raise "The command failed unexpectedly with an empty JIT list. To reproduce, try running the following: `#{cmd}`"
end
# Collect the JIT list from the failing Ruby process
jit_list = nil
Expand Down
21 changes: 14 additions & 7 deletions vm_method.c
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,9 @@ rb_clear_all_refinement_method_cache(void)
void
rb_method_table_insert(VALUE klass, struct rb_id_table *table, ID method_id, const rb_method_entry_t *me)
{
rb_method_table_insert0(klass, table, method_id, me, RB_TYPE_P(klass, T_ICLASS) && !RICLASS_OWNS_M_TBL_P(klass));
RB_VM_LOCKING() {
rb_method_table_insert0(klass, table, method_id, me, RB_TYPE_P(klass, T_ICLASS) && !RICLASS_OWNS_M_TBL_P(klass));
}
}

void
Expand Down Expand Up @@ -1545,7 +1547,9 @@ method_added(VALUE klass, ID mid)
void
rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_visibility_t visi)
{
rb_method_entry_make(klass, mid, klass, visi, type, NULL, mid, opts);
RB_VM_LOCKING() {
rb_method_entry_make(klass, mid, klass, visi, type, NULL, mid, opts);
}

if (type != VM_METHOD_TYPE_UNDEF && type != VM_METHOD_TYPE_REFINED) {
method_added(klass, mid);
Expand All @@ -1570,11 +1574,14 @@ static rb_method_entry_t *
method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me,
rb_method_visibility_t visi, VALUE defined_class)
{
rb_method_entry_t *newme = rb_method_entry_make(klass, mid, defined_class, visi,
me->def->type, me->def, 0, NULL);
if (newme == me) {
me->def->no_redef_warning = TRUE;
METHOD_ENTRY_FLAGS_SET(newme, visi, FALSE);
rb_method_entry_t *newme;
RB_VM_LOCKING() {
newme = rb_method_entry_make(klass, mid, defined_class, visi,
me->def->type, me->def, 0, NULL);
if (newme == me) {
me->def->no_redef_warning = TRUE;
METHOD_ENTRY_FLAGS_SET(newme, visi, FALSE);
}
}

method_added(klass, mid);
Expand Down
95 changes: 35 additions & 60 deletions zjit/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1986,6 +1986,20 @@ impl Function {
self.insn_types[replacement.0] = self.infer_type(replacement);
self.make_equal_to(insn_id, replacement);
}
Insn::NewRange { low, high, flag, state } => {
let low_is_fix = self.is_a(low, types::Fixnum);
let high_is_fix = self.is_a(high, types::Fixnum);

if low_is_fix || high_is_fix {
let low_fix = self.coerce_to_fixnum(block, low, state);
let high_fix = self.coerce_to_fixnum(block, high, state);
let replacement = self.push_insn(block, Insn::NewRangeFixnum { low: low_fix, high: high_fix, flag, state });
self.make_equal_to(insn_id, replacement);
self.insn_types[replacement.0] = self.infer_type(replacement);
} else {
self.push_insn_id(block, insn_id);
};
}
_ => { self.push_insn_id(block, insn_id); }
}
}
Expand Down Expand Up @@ -2194,52 +2208,6 @@ impl Function {
.unwrap_or(insn_id)
}

fn optimize_ranges(&mut self) {
for block in self.rpo() {
let old_insns = std::mem::take(&mut self.blocks[block.0].insns);
assert!(self.blocks[block.0].insns.is_empty());

for insn_id in old_insns {
match self.find(insn_id) {
Insn::NewRange { low, high, flag, state } => {

// The NewRange rewrite triggers mostly on literals because that is the
// case we can easily prove Fixnum statically and cheaply guard the other
// side.
let low_is_fix = self.is_a(low, types::Fixnum);
let high_is_fix = self.is_a(high, types::Fixnum);

if low_is_fix && high_is_fix {
// Both statically fixnum => specialize directly
let repl = self.push_insn(block, Insn::NewRangeFixnum { low, high, flag, state });
self.make_equal_to(insn_id, repl);
self.insn_types[repl.0] = self.infer_type(repl);
} else if low_is_fix {
// Only left is fixnum => guard right
let high_fix = self.coerce_to_fixnum(block, high, state);
let repl = self.push_insn(block, Insn::NewRangeFixnum { low, high: high_fix, flag, state });
self.make_equal_to(insn_id, repl);
self.insn_types[repl.0] = self.infer_type(repl);
} else if high_is_fix {
// Only right is fixnum => guard left
let low_fix = self.coerce_to_fixnum(block, low, state);
let repl = self.push_insn(block, Insn::NewRangeFixnum { low: low_fix, high, flag, state });
self.make_equal_to(insn_id, repl);
self.insn_types[repl.0] = self.infer_type(repl);
} else {
// Keep generic op
self.push_insn_id(block, insn_id);
}
}
_ => {
self.push_insn_id(block, insn_id);
}
}
}
}
self.infer_types();
}

/// Use type information left by `infer_types` to fold away operations that can be evaluated at compile-time.
///
/// It can fold fixnum math, truthiness tests, and branches with constant conditionals.
Expand Down Expand Up @@ -2635,8 +2603,6 @@ impl Function {
#[cfg(debug_assertions)] self.assert_validates();
self.optimize_c_calls();
#[cfg(debug_assertions)] self.assert_validates();
self.optimize_ranges();
#[cfg(debug_assertions)] self.assert_validates();
self.fold_constants();
#[cfg(debug_assertions)] self.assert_validates();
self.clean_cfg();
Expand Down Expand Up @@ -7253,41 +7219,50 @@ mod opt_tests {
}

#[test]
fn test_optimize_range_fixnum_inclusive_literals() {
fn test_optimize_new_range_fixnum_inclusive_literals() {
eval("
def test()
(1..2)
a = 2
(1..a)
end
test; test
");
assert_snapshot!(hir_string("test"), @r"
fn test@<compiled>:3:
bb0(v0:BasicObject):
v4:RangeExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
v1:NilClass = Const Value(nil)
v5:Fixnum[2] = Const Value(2)
v8:Fixnum[1] = Const Value(1)
v16:RangeExact = NewRangeFixnum v8 NewRangeInclusive v5
CheckInterrupts
Return v4
Return v16
");
}


#[test]
fn test_optimize_range_fixnum_exclusive_literals() {
fn test_optimize_new_range_fixnum_exclusive_literals() {
eval("
def test()
(1...2)
a = 2
(1...a)
end
test; test
");
assert_snapshot!(hir_string("test"), @r"
fn test@<compiled>:3:
bb0(v0:BasicObject):
v4:RangeExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
v1:NilClass = Const Value(nil)
v5:Fixnum[2] = Const Value(2)
v8:Fixnum[1] = Const Value(1)
v16:RangeExact = NewRangeFixnum v8 NewRangeExclusive v5
CheckInterrupts
Return v4
Return v16
");
}

#[test]
fn test_optimize_range_fixnum_inclusive_high_guarded() {
fn test_optimize_new_range_fixnum_inclusive_high_guarded() {
eval("
def test(a)
(1..a)
Expand All @@ -7306,7 +7281,7 @@ mod opt_tests {
}

#[test]
fn test_optimize_range_fixnum_exclusive_high_guarded() {
fn test_optimize_new_range_fixnum_exclusive_high_guarded() {
eval("
def test(a)
(1...a)
Expand All @@ -7325,7 +7300,7 @@ mod opt_tests {
}

#[test]
fn test_optimize_range_fixnum_inclusive_low_guarded() {
fn test_optimize_new_range_fixnum_inclusive_low_guarded() {
eval("
def test(a)
(a..10)
Expand All @@ -7344,7 +7319,7 @@ mod opt_tests {
}

#[test]
fn test_optimize_range_fixnum_exclusive_low_guarded() {
fn test_optimize_new_range_fixnum_exclusive_low_guarded() {
eval("
def test(a)
(a...10)
Expand Down