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
2 changes: 1 addition & 1 deletion .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ jobs:

ulimit -c unlimited
make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"}
timeout-minutes: 60
timeout-minutes: 90
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
Expand Down
13 changes: 0 additions & 13 deletions .github/workflows/yjit-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,12 @@ jobs:
- test_task: 'test-bundled-gems'
configure: '--enable-yjit=dev'

- test_task: 'yjit-bench'
configure: '--enable-yjit=dev'
yjit_bench_opts: '--yjit-stats'
continue-on-test_task: true

env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUN_OPTS: ${{ matrix.yjit_opts }}
YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }}
SPECOPTS: ${{ matrix.specopts }}
RUBY_DEBUG: ci
BUNDLE_JOBS: 8 # for yjit-bench
RUST_BACKTRACE: 1

runs-on: ubuntu-22.04
Expand Down Expand Up @@ -208,13 +202,6 @@ jobs:
LAUNCHABLE_STDERR: ${{ steps.launchable.outputs.stderr_report_path }}
continue-on-error: ${{ matrix.continue-on-test_task || false }}

- name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison
run: echo "https://github.com/${BASE_REPO}/commit/${BASE_SHA}"
env:
BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
if: ${{ matrix.test_task == 'yjit-bench' && startsWith(github.event_name, 'pull') }}

- uses: ./.github/actions/slack
with:
label: ${{ matrix.test_task }} ${{ matrix.configure }}
Expand Down
1 change: 1 addition & 0 deletions insns.def
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ getinstancevariable
(VALUE val)
/* Ractor crashes when it accesses class/module-level instances variables. */
// attr bool leaf = false; /* has IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR() */
// attr bool zjit_profile = true;
{
val = vm_getinstancevariable(GET_ISEQ(), GET_SELF(), id, ic);
}
Expand Down
12 changes: 12 additions & 0 deletions jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
#include "iseq.h"
#include "internal/gc.h"

// Field offsets for the RObject struct
enum robject_offsets {
ROBJECT_OFFSET_AS_HEAP_FIELDS = offsetof(struct RObject, as.heap.fields),
ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary),
};

unsigned int
rb_iseq_encoded_size(const rb_iseq_t *iseq)
{
Expand Down Expand Up @@ -454,3 +460,9 @@ rb_set_cfp_sp(struct rb_control_frame_struct *cfp, VALUE *sp)
{
cfp->sp = sp;
}

bool
rb_jit_shape_too_complex_p(shape_id_t shape_id)
{
return rb_shape_too_complex_p(shape_id);
}
53 changes: 53 additions & 0 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,36 @@ def test() = @foo
}
end

def test_getinstancevariable_miss
assert_compiles '[1, 1, 4]', %q{
class C
def foo
@foo
end

def foo_then_bar
@foo = 1
@bar = 2
end

def bar_then_foo
@bar = 3
@foo = 4
end
end

o1 = C.new
o1.foo_then_bar
result = []
result << o1.foo
result << o1.foo
o2 = C.new
o2.bar_then_foo
result << o2.foo
result
}
end

def test_setinstancevariable
assert_compiles '1', %q{
def test() = @foo = 1
Expand Down Expand Up @@ -1355,6 +1385,29 @@ def test = return defined?("x".reverse(1)), defined?("x".reverse(1).reverse)
}, insns: [:defined]
end

def test_defined_method_raise
assert_compiles '[nil, nil, nil]', %q{
class C
def assert_equal expected, actual
if expected != actual
raise "NO"
end
end

def test_defined_method
assert_equal(nil, defined?("x".reverse(1).reverse))
end
end

c = C.new
result = []
result << c.test_defined_method
result << c.test_defined_method
result << c.test_defined_method
result
}
end

def test_defined_yield
assert_compiles "nil", "defined?(yield)"
assert_compiles '[nil, nil, "yield"]', %q{
Expand Down
27 changes: 14 additions & 13 deletions vm_insnhelper.c
Original file line number Diff line number Diff line change
Expand Up @@ -6093,27 +6093,28 @@ vm_sendish(

VALUE
rb_vm_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, CALL_DATA cd, ISEQ blockiseq)
{
stack_check(ec);
VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, false);
VALUE val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_method);
VM_EXEC(ec, val);
return val;
}

VALUE
rb_vm_sendforward(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, CALL_DATA cd, ISEQ blockiseq)
{
stack_check(ec);

struct rb_forwarding_call_data adjusted_cd;
struct rb_callinfo adjusted_ci;

VALUE bh;
VALUE val;

if (vm_ci_flag(cd->ci) & VM_CALL_FORWARDING) {
bh = vm_caller_setup_fwd_args(GET_EC(), GET_CFP(), cd, blockiseq, false, &adjusted_cd, &adjusted_ci);
VALUE bh = vm_caller_setup_fwd_args(GET_EC(), GET_CFP(), cd, blockiseq, false, &adjusted_cd, &adjusted_ci);

val = vm_sendish(ec, GET_CFP(), &adjusted_cd.cd, bh, mexp_search_method);
VALUE val = vm_sendish(ec, GET_CFP(), &adjusted_cd.cd, bh, mexp_search_method);

if (cd->cc != adjusted_cd.cd.cc && vm_cc_markable(adjusted_cd.cd.cc)) {
RB_OBJ_WRITE(GET_ISEQ(), &cd->cc, adjusted_cd.cd.cc);
}
}
else {
bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, false);
val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_method);
if (cd->cc != adjusted_cd.cd.cc && vm_cc_markable(adjusted_cd.cd.cc)) {
RB_OBJ_WRITE(GET_ISEQ(), &cd->cc, adjusted_cd.cd.cc);
}

VM_EXEC(ec, val);
Expand Down
12 changes: 0 additions & 12 deletions yjit.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,6 @@

#include <errno.h>

// Field offsets for the RObject struct
enum robject_offsets {
ROBJECT_OFFSET_AS_HEAP_FIELDS = offsetof(struct RObject, as.heap.fields),
ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary),
};

// Field offsets for the RString struct
enum rstring_offsets {
RUBY_OFFSET_RSTRING_LEN = offsetof(struct RString, len)
Expand Down Expand Up @@ -758,12 +752,6 @@ rb_object_shape_count(void)
return ULONG2NUM((unsigned long)rb_shapes_count());
}

bool
rb_yjit_shape_too_complex_p(shape_id_t shape_id)
{
return rb_shape_too_complex_p(shape_id);
}

bool
rb_yjit_shape_obj_too_complex_p(VALUE obj)
{
Expand Down
4 changes: 2 additions & 2 deletions yjit/bindgen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ fn main() {
.allowlist_function("rb_shape_get_iv_index")
.allowlist_function("rb_shape_transition_add_ivar_no_warnings")
.allowlist_function("rb_yjit_shape_obj_too_complex_p")
.allowlist_function("rb_yjit_shape_too_complex_p")
.allowlist_function("rb_yjit_shape_capacity")
.allowlist_function("rb_yjit_shape_index")
.allowlist_var("SHAPE_ID_NUM_BITS")
Expand Down Expand Up @@ -351,11 +350,12 @@ fn main() {
.allowlist_function("rb_yjit_invokeblock_sp_pops")
.allowlist_function("rb_yjit_set_exception_return")
.allowlist_function("rb_yjit_str_concat_codepoint")
.allowlist_type("robject_offsets")
.allowlist_type("rstring_offsets")

// From jit.c
.allowlist_function("rb_assert_holding_vm_lock")
.allowlist_function("rb_jit_shape_too_complex_p")
.allowlist_type("robject_offsets")

// from vm_sync.h
.allowlist_function("rb_vm_barrier")
Expand Down
21 changes: 19 additions & 2 deletions yjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3103,7 +3103,7 @@ fn gen_set_ivar(

// If the VM ran out of shapes, or this class generated too many leaf,
// it may be de-optimized into OBJ_TOO_COMPLEX_SHAPE (hash-table).
new_shape_too_complex = unsafe { rb_yjit_shape_too_complex_p(next_shape_id) };
new_shape_too_complex = unsafe { rb_jit_shape_too_complex_p(next_shape_id) };
if new_shape_too_complex {
Some((next_shape_id, None, 0_usize))
} else {
Expand Down Expand Up @@ -9564,7 +9564,24 @@ fn gen_sendforward(
jit: &mut JITState,
asm: &mut Assembler,
) -> Option<CodegenStatus> {
return gen_send(jit, asm);
// Generate specialized code if possible
let cd = jit.get_arg(0).as_ptr();
let block = jit.get_arg(1).as_optional_ptr().map(|iseq| BlockHandler::BlockISeq(iseq));
if let Some(status) = perf_call! { gen_send_general(jit, asm, cd, block) } {
return Some(status);
}

// Otherwise, fallback to dynamic dispatch using the interpreter's implementation of sendforward
let blockiseq = jit.get_arg(1).as_iseq();
gen_send_dynamic(jit, asm, cd, unsafe { rb_yjit_sendish_sp_pops((*cd).ci) }, |asm| {
extern "C" {
fn rb_vm_sendforward(ec: EcPtr, cfp: CfpPtr, cd: VALUE, blockiseq: IseqPtr) -> VALUE;
}
asm.ccall(
rb_vm_sendforward as *const u8,
vec![EC, CFP, (cd as usize).into(), VALUE(blockiseq as usize).into()],
)
})
}

fn gen_invokeblock(
Expand Down
8 changes: 4 additions & 4 deletions yjit/src/cruby_bindings.inc.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 0 additions & 13 deletions yjit/yjit.mk
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,6 @@ ifneq ($(YJIT_SUPPORT),no)
$(RUST_LIB): $(YJIT_SRC_FILES)
endif

# By using YJIT_BENCH_OPTS instead of RUN_OPTS, you can skip passing the options to `make install`
YJIT_BENCH_OPTS = $(RUN_OPTS) --enable-gems
YJIT_BENCH = benchmarks/railsbench/benchmark.rb

# Run yjit-bench's ./run_once.sh for CI
yjit-bench: install update-yjit-bench PHONY
$(Q) cd $(srcdir)/yjit-bench && PATH=$(prefix)/bin:$$PATH \
./run_once.sh $(YJIT_BENCH_OPTS) $(YJIT_BENCH)

update-yjit-bench:
$(Q) $(tooldir)/git-refresh -C $(srcdir) --branch main \
https://github.com/Shopify/yjit-bench yjit-bench $(GIT_OPTS)

RUST_VERSION = +1.58.0

# Gives quick feedback about YJIT. Not a replacement for a full test run.
Expand Down
6 changes: 0 additions & 6 deletions zjit.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,12 +337,6 @@ rb_zjit_print_exception(void)
rb_warn("Ruby error: %"PRIsVALUE"", rb_funcall(exception, rb_intern("full_message"), 0));
}

bool
rb_zjit_shape_obj_too_complex_p(VALUE obj)
{
return rb_shape_obj_too_complex_p(obj);
}

enum {
RB_INVALID_SHAPE_ID = INVALID_SHAPE_ID,
};
Expand Down
6 changes: 3 additions & 3 deletions zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def stats_string
stats = self.stats

print_counters_with_prefix(prefix: 'failed_', prompt: 'compilation failure reasons', buf:, stats:)
print_counters_with_prefix(prefix: 'unhandled_call_', prompt: 'unhandled call types', buf:, stats:, limit: 20)
print_counters([
:dynamic_send_count,

Expand All @@ -53,8 +54,8 @@ def stats_string
:zjit_insn_count,
:ratio_in_zjit,
], buf:, stats:)
print_counters_with_prefix(prefix: 'unhandled_yarv_insn_', prompt: 'unhandled YARV insns', buf:, stats:, limit: 20)
print_counters_with_prefix(prefix: 'exit_', prompt: 'side exit reasons', buf:, stats:, limit: 20)
print_counters_with_prefix(prefix: 'specific_exit_', prompt: 'specific side exit reasons', buf:, stats:, limit: 20)

buf
end
Expand Down Expand Up @@ -96,15 +97,14 @@ def print_counters_with_prefix(buf:, stats:, prefix:, prompt:, limit: nil)
left_pad = counters.keys.map(&:size).max
right_pad = counters.values.map { |value| number_with_delimiter(value).size }.max
total = counters.values.sum
count = counters.size

counters = counters.to_a
counters.sort_by! { |_, value| -value }
counters = counters.first(limit) if limit

buf << "Top-#{counters.size} " if limit
buf << "#{prompt}"
buf << " (%.1f%% of all #{count})" % (100.0 * counters.map(&:last).sum / total) if limit
buf << " (%.1f%% of total #{number_with_delimiter(total)})" % (100.0 * counters.map(&:last).sum / total) if limit
buf << ":\n"
counters.each do |key, value|
padded_key = key.rjust(left_pad, ' ')
Expand Down
3 changes: 2 additions & 1 deletion zjit/bindgen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ fn main() {
.allowlist_function("rb_shape_id_offset")
.allowlist_function("rb_shape_get_iv_index")
.allowlist_function("rb_shape_transition_add_ivar_no_warnings")
.allowlist_function("rb_zjit_shape_obj_too_complex_p")
.allowlist_var("SHAPE_ID_NUM_BITS")

// From ruby/internal/intern/object.h
Expand Down Expand Up @@ -367,6 +366,8 @@ fn main() {

// From jit.c
.allowlist_function("rb_assert_holding_vm_lock")
.allowlist_function("rb_jit_shape_too_complex_p")
.allowlist_type("robject_offsets")

// from vm_sync.h
.allowlist_function("rb_vm_barrier")
Expand Down
Loading