Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] master from ruby:master #350

Merged
merged 6 commits into from
Aug 9, 2023
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
1 change: 0 additions & 1 deletion .github/actions/setup/macos/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ runs:
- name: brew
shell: bash
run: |
brew upgrade --quiet
brew install --quiet gmp libffi openssl@1.1 zlib autoconf automake libtool readline

- name: Set ENV
Expand Down
22 changes: 22 additions & 0 deletions bootstraptest/test_yjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4004,3 +4004,25 @@ def calling_func
_x, _y = func.call
end.call
}

# Catch TAG_BREAK in a non-FINISH frame with JIT code
assert_equal '1', %q{
def entry
catch_break
end

def catch_break
while_true do
break
end
1
end

def while_true
while true
yield
end
end

entry
}
36 changes: 6 additions & 30 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1492,9 +1492,6 @@ asan_poison_object_restore(VALUE obj, void *ptr)
#define RVALUE_PAGE_UNCOLLECTIBLE(page, obj) MARKED_IN_BITMAP((page)->uncollectible_bits, (obj))
#define RVALUE_PAGE_MARKING(page, obj) MARKED_IN_BITMAP((page)->marking_bits, (obj))


static int rgengc_remembered(rb_objspace_t *objspace, VALUE obj);
static int rgengc_remembered_sweep(rb_objspace_t *objspace, VALUE obj);
static int rgengc_remember(rb_objspace_t *objspace, VALUE obj);
static void rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap);
static void rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap);
Expand Down Expand Up @@ -2507,7 +2504,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
GC_ASSERT(RVALUE_OLD_P(obj) == FALSE);
GC_ASSERT(RVALUE_WB_UNPROTECTED(obj) == FALSE);

if (rgengc_remembered(objspace, (VALUE)obj)) rb_bug("newobj: %s is remembered.", obj_info(obj));
if (RVALUE_REMEMBERED((VALUE)obj)) rb_bug("newobj: %s is remembered.", obj_info(obj));
}
RB_VM_LOCK_LEAVE_NO_BARRIER();
#endif
Expand Down Expand Up @@ -5435,7 +5432,7 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit
#if RGENGC_CHECK_MODE
if (!is_full_marking(objspace)) {
if (RVALUE_OLD_P(vp)) rb_bug("page_sweep: %p - old while minor GC.", (void *)p);
if (rgengc_remembered_sweep(objspace, vp)) rb_bug("page_sweep: %p - remembered.", (void *)p);
if (RVALUE_REMEMBERED(vp)) rb_bug("page_sweep: %p - remembered.", (void *)p);
}
#endif
if (obj_free(objspace, vp)) {
Expand Down Expand Up @@ -8578,12 +8575,6 @@ gc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...)

/* bit operations */

static int
rgengc_remembersetbits_get(rb_objspace_t *objspace, VALUE obj)
{
return RVALUE_REMEMBERED(obj);
}

static int
rgengc_remembersetbits_set(rb_objspace_t *objspace, VALUE obj)
{
Expand All @@ -8607,7 +8598,7 @@ static int
rgengc_remember(rb_objspace_t *objspace, VALUE obj)
{
gc_report(6, objspace, "rgengc_remember: %s %s\n", obj_info(obj),
rgengc_remembersetbits_get(objspace, obj) ? "was already remembered" : "is remembered now");
RVALUE_REMEMBERED(obj) ? "was already remembered" : "is remembered now");

check_rvalue_consistency(obj);

Expand All @@ -8616,7 +8607,7 @@ rgengc_remember(rb_objspace_t *objspace, VALUE obj)
}

#if RGENGC_PROFILE > 0
if (!rgengc_remembered(objspace, obj)) {
if (!RVALUE_REMEMBERED(obj)) {
if (RVALUE_WB_UNPROTECTED(obj) == 0) {
objspace->profile.total_remembered_normal_object_count++;
#if RGENGC_PROFILE >= 2
Expand All @@ -8629,21 +8620,6 @@ rgengc_remember(rb_objspace_t *objspace, VALUE obj)
return rgengc_remembersetbits_set(objspace, obj);
}

static int
rgengc_remembered_sweep(rb_objspace_t *objspace, VALUE obj)
{
int result = rgengc_remembersetbits_get(objspace, obj);
check_rvalue_consistency(obj);
return result;
}

static int
rgengc_remembered(rb_objspace_t *objspace, VALUE obj)
{
gc_report(6, objspace, "rgengc_remembered: %s\n", obj_info(obj));
return rgengc_remembered_sweep(objspace, obj);
}

#ifndef PROFILE_REMEMBERSET_MARK
#define PROFILE_REMEMBERSET_MARK 0
#endif
Expand Down Expand Up @@ -8749,7 +8725,7 @@ gc_writebarrier_generational(VALUE a, VALUE b, rb_objspace_t *objspace)
}

/* mark `a' and remember (default behavior) */
if (!rgengc_remembered(objspace, a)) {
if (!RVALUE_REMEMBERED(a)) {
RB_VM_LOCK_ENTER_NO_BARRIER();
{
rgengc_remember(objspace, a);
Expand Down Expand Up @@ -8844,7 +8820,7 @@ rb_gc_writebarrier_unprotect(VALUE obj)
rb_objspace_t *objspace = &rb_objspace;

gc_report(2, objspace, "rb_gc_writebarrier_unprotect: %s %s\n", obj_info(obj),
rgengc_remembered(objspace, obj) ? " (already remembered)" : "");
RVALUE_REMEMBERED(obj) ? " (already remembered)" : "");

RB_VM_LOCK_ENTER_NO_BARRIER();
{
Expand Down
6 changes: 3 additions & 3 deletions lib/ruby_vm/rjit/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def compile(iseq, cfp)
asm = Assembler.new
compile_prologue(asm, iseq, pc)
compile_block(asm, jit:, pc:)
iseq.body.jit_func = @cb.write(asm)
iseq.body.jit_entry = @cb.write(asm)
rescue Exception => e
$stderr.puts e.full_message
exit 1
Expand Down Expand Up @@ -176,8 +176,8 @@ def invalidate_blocks(iseq, pc)

# If they were the ISEQ's first blocks, re-compile RJIT entry as well
if iseq.body.iseq_encoded.to_i == pc
iseq.body.jit_func = 0
iseq.body.total_calls = 0
iseq.body.jit_entry = 0
iseq.body.jit_entry_calls = 0
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/ruby_vm/rjit/invariants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,11 @@ def invalidate_all

C.rjit_for_each_iseq do |iseq|
# Avoid entering past code
iseq.body.jit_func = 0
iseq.body.jit_entry = 0
# Avoid reusing past code
iseq.body.rjit_blocks.clear if iseq.body.rjit_blocks
# Compile this again if not converted to trace_* insns
iseq.body.total_calls = 0
iseq.body.jit_entry_calls = 0
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions object.c
Original file line number Diff line number Diff line change
Expand Up @@ -2165,6 +2165,10 @@ rb_class_superclass(VALUE klass)
if (klass == rb_cBasicObject) return Qnil;
rb_raise(rb_eTypeError, "uninitialized class");
}

if (!RCLASS_SUPERCLASS_DEPTH(klass)) {
return Qnil;
}
else {
super = RCLASS_SUPERCLASSES(klass)[RCLASS_SUPERCLASS_DEPTH(klass) - 1];
RUBY_ASSERT(RB_TYPE_P(klass, T_CLASS));
Expand Down
4 changes: 2 additions & 2 deletions rjit_c.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1177,8 +1177,8 @@ def C.rb_iseq_constant_body
), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mark_bits)")],
outer_variables: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), outer_variables)")],
mandatory_only_iseq: [CType::Pointer.new { self.rb_iseq_t }, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), mandatory_only_iseq)")],
jit_func: [self.rb_jit_func_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_func)")],
total_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), total_calls)")],
jit_entry: [self.rb_jit_func_t, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_entry)")],
jit_entry_calls: [CType::Immediate.parse("unsigned long"), Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), jit_entry_calls)")],
rjit_blocks: [self.VALUE, Primitive.cexpr!("OFFSETOF((*((struct rb_iseq_constant_body *)NULL)), rjit_blocks)"), true],
)
end
Expand Down
7 changes: 7 additions & 0 deletions test/ruby/test_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ def test_instantiate_singleton_class

def test_superclass_of_basicobject
assert_equal(nil, BasicObject.superclass)

assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
module Mod end
BasicObject.include(Mod)
assert_equal(nil, BasicObject.superclass)
end;
end

def test_module_function
Expand Down
2 changes: 1 addition & 1 deletion tool/rjit/bindgen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ def push_target(target)
skip_fields: {
'rb_execution_context_struct.machine': %w[regs], # differs between macOS and Linux
rb_execution_context_struct: %w[method_missing_reason], # non-leading bit fields not supported
rb_iseq_constant_body: %w[yjit_payload], # conditionally defined
rb_iseq_constant_body: %w[jit_exception jit_exception_calls yjit_payload], # conditionally defined
rb_thread_struct: %w[status has_dedicated_nt to_kill abort_on_exception report_on_exception pending_interrupt_queue_checked],
:'' => %w[is_from_method is_lambda is_isolated], # rb_proc_t
},
Expand Down
95 changes: 72 additions & 23 deletions vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,14 @@ extern VALUE rb_vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, V
static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE block_handler);

#if USE_RJIT || USE_YJIT
// Try to compile the current ISeq in ec. Return 0 if not compiled.
// Generate JIT code that supports the following kinds of ISEQ entries:
// * The first ISEQ on vm_exec (e.g. <main>, or Ruby methods/blocks
// called by a C method). The current frame has VM_FRAME_FLAG_FINISH.
// The current vm_exec stops if JIT code returns a non-Qundef value.
// * ISEQs called by the interpreter on vm_sendish (e.g. Ruby methods or
// blocks called by a Ruby frame that isn't compiled or side-exited).
// The current frame doesn't have VM_FRAME_FLAG_FINISH. The current
// vm_exec does NOT stop whether JIT code returns Qundef or not.
static inline rb_jit_func_t
jit_compile(rb_execution_context_t *ec)
{
Expand All @@ -379,35 +386,29 @@ jit_compile(rb_execution_context_t *ec)
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
bool yjit_enabled = rb_yjit_compile_new_iseqs();
if (yjit_enabled || rb_rjit_call_p) {
body->total_calls++;
body->jit_entry_calls++;
}
else {
return 0;
}

// Don't try to compile the function if it's already compiled
if (body->jit_func) {
return body->jit_func;
return NULL;
}

// Trigger JIT compilation as needed
if (yjit_enabled) {
if (rb_yjit_threshold_hit(iseq)) {
rb_yjit_compile_iseq(iseq, ec);
// Trigger JIT compilation if not compiled
if (body->jit_entry == NULL) {
if (yjit_enabled) {
if (rb_yjit_threshold_hit(iseq, body->jit_entry_calls)) {
rb_yjit_compile_iseq(iseq, ec, false);
}
}
}
else { // rb_rjit_call_p
if (body->total_calls == rb_rjit_call_threshold()) {
rb_rjit_compile(iseq);
else { // rb_rjit_call_p
if (body->jit_entry_calls == rb_rjit_call_threshold()) {
rb_rjit_compile(iseq);
}
}
}

return body->jit_func;
return body->jit_entry;
}

// Try to execute the current iseq in ec. Use JIT code if it is ready.
// If it is not, add ISEQ to the compilation queue and return Qundef for RJIT.
// YJIT compiles on the thread running the iseq.
// Execute JIT code compiled by jit_compile()
static inline VALUE
jit_exec(rb_execution_context_t *ec)
{
Expand All @@ -425,6 +426,51 @@ jit_exec(rb_execution_context_t *ec)
# define jit_exec(ec) Qundef
#endif

#if USE_YJIT
// Generate JIT code that supports the following kind of ISEQ entry:
// * The first ISEQ pushed by vm_exec_handle_exception. The frame would
// point to a location specified by a catch table, and it doesn't have
// VM_FRAME_FLAG_FINISH. The current vm_exec stops if JIT code returns
// a non-Qundef value. So you should not return a non-Qundef value
// until ec->cfp is changed to a frame with VM_FRAME_FLAG_FINISH.
static inline rb_jit_func_t
jit_compile_exception(rb_execution_context_t *ec)
{
// Increment the ISEQ's call counter
const rb_iseq_t *iseq = ec->cfp->iseq;
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
if (rb_yjit_compile_new_iseqs()) {
body->jit_exception_calls++;
}
else {
return NULL;
}

// Trigger JIT compilation if not compiled
if (body->jit_exception == NULL && rb_yjit_threshold_hit(iseq, body->jit_exception_calls)) {
rb_yjit_compile_iseq(iseq, ec, true);
}
return body->jit_exception;
}

// Execute JIT code compiled by jit_compile_exception()
static inline VALUE
jit_exec_exception(rb_execution_context_t *ec)
{
rb_jit_func_t func = jit_compile_exception(ec);
if (func) {
// Call the JIT code
return func(ec, ec->cfp);
}
else {
return Qundef;
}
}
#else
# define jit_compile_exception(ec) ((rb_jit_func_t)0)
# define jit_exec_exception(ec) Qundef
#endif

#include "vm_insnhelper.c"

#include "vm_exec.c"
Expand Down Expand Up @@ -2381,8 +2427,11 @@ vm_exec_loop(rb_execution_context_t *ec, enum ruby_tag_type state,

rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY);
while (UNDEF_P(result = vm_exec_handle_exception(ec, state, result))) {
/* caught a jump, exec the handler */
result = vm_exec_core(ec);
// caught a jump, exec the handler. JIT code in jit_exec_exception()
// may return Qundef to run remaining frames with vm_exec_core().
if (UNDEF_P(result = jit_exec_exception(ec))) {
result = vm_exec_core(ec);
}
vm_loop_start:
VM_ASSERT(ec->tag == tag);
/* when caught `throw`, `tag.state` is set. */
Expand Down
15 changes: 11 additions & 4 deletions vm_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -503,10 +503,17 @@ struct rb_iseq_constant_body {
const rb_iseq_t *mandatory_only_iseq;

#if USE_RJIT || USE_YJIT
// Function pointer for JIT code
rb_jit_func_t jit_func;
// Number of total calls with jit_exec()
long unsigned total_calls;
// Function pointer for JIT code on jit_exec()
rb_jit_func_t jit_entry;
// Number of calls on jit_exec()
long unsigned jit_entry_calls;
#endif

#if USE_YJIT
// Function pointer for JIT code on jit_exec_exception()
rb_jit_func_t jit_exception;
// Number of calls on jit_exec_exception()
long unsigned jit_exception_calls;
#endif

#if USE_RJIT
Expand Down
6 changes: 6 additions & 0 deletions vm_insnhelper.c
Original file line number Diff line number Diff line change
Expand Up @@ -2485,6 +2485,12 @@ vm_base_ptr(const rb_control_frame_t *cfp)
}
}

VALUE *
rb_vm_base_ptr(const rb_control_frame_t *cfp)
{
return vm_base_ptr(cfp);
}

/* method call processes with call_info */

#include "vm_args.c"
Expand Down
Loading