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
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ Note: We're only listing outstanding class updates.

[[Feature #21219]]

* A deprecated behavior, process creation by `Kernel#open` with a
leading `|`, was removed. [[Feature #19630]]

* Binding

* `Binding#local_variables` does no longer include numbered parameters.
Expand All @@ -66,6 +69,9 @@ Note: We're only listing outstanding class updates.
* `IO.select` accepts `Float::INFINITY` as a timeout argument.
[[Feature #20610]]

* A deprecated behavior, process creation by `IO` class methods
with a leading `|`, was removed. [[Feature #19630]]

* Math

* `Math.log1p` and `Math.expm1` are added. [[Feature #21527]]
Expand Down Expand Up @@ -317,6 +323,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[Feature #15408]: https://bugs.ruby-lang.org/issues/15408
[Feature #17473]: https://bugs.ruby-lang.org/issues/17473
[Feature #18455]: https://bugs.ruby-lang.org/issues/18455
[Feature #19630]: https://bugs.ruby-lang.org/issues/19630
[Feature #19908]: https://bugs.ruby-lang.org/issues/19908
[Feature #20610]: https://bugs.ruby-lang.org/issues/20610
[Feature #20724]: https://bugs.ruby-lang.org/issues/20724
Expand Down
10 changes: 7 additions & 3 deletions defs/gmake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -548,13 +548,17 @@ matz: OLD := $(MAJOR).$(MINOR).0
ifdef NEW
matz: MAJOR := $(word 1,$(subst ., ,$(NEW)))
matz: MINOR := $(word 2,$(subst ., ,$(NEW)))
matz: .WAIT bump_news
matz: $(DOT_WAIT) bump_news
bump_news$(DOT_WAIT): up
bump_headers$(DOT_WAIT): bump_news
else
matz: MINOR := $(shell expr $(MINOR) + 1)
matz: .WAIT reset_news
matz: $(DOT_WAIT) reset_news
flush_news$(DOT_WAIT): up
bump_headers$(DOT_WAIT): reset_news
endif

matz: .WAIT bump_headers
matz: $(DOT_WAIT) bump_headers
matz: override NEW := $(MAJOR).$(MINOR).0
matz: files := include/ruby/version.h include/ruby/internal/abi.h
matz: message := Development of $(NEW) started.
Expand Down
16 changes: 0 additions & 16 deletions include/ruby/internal/intern/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,22 +211,6 @@ int rb_is_absolute_path(const char *path);
*/
rb_off_t rb_file_size(VALUE file);

#ifdef RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY
RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY()
#endif
/**
* If the PATH_SEPARATOR-separated list of directory names contains the name of
* a world-writable directory, issue a warning for it. This may do nothing on
* some platforms.
*
* @param[in] path A local path.
* @retval 0 The "check" succeeded.
* @retval otherwise The "check" failed.
* @note This feature may be disabled by setting `ENABLE_PATH_CHECK`
* macro to zero at compilation time.
*/
int rb_path_check(const char *path);

RBIMPL_SYMBOL_EXPORT_END()

#endif /* RBIMPL_INTERN_FILE_H */
4 changes: 2 additions & 2 deletions vm_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c
iseq = cfp->iseq;
pc = cfp->pc - ISEQ_BODY(iseq)->iseq_encoded;
iseq_name = RSTRING_PTR(ISEQ_BODY(iseq)->location.label);
if (pc >= 0 && pc <= ISEQ_BODY(iseq)->iseq_size) {
if (pc >= 0 && (size_t)pc <= ISEQ_BODY(iseq)->iseq_size) {
line = rb_vm_get_sourceline(cfp);
}
if (line) {
Expand Down Expand Up @@ -355,7 +355,7 @@ box_env_dump(const rb_execution_context_t *ec, const VALUE *env, const rb_contro
iseq = cfp->iseq;
pc = cfp->pc - ISEQ_BODY(iseq)->iseq_encoded;
iseq_name = RSTRING_PTR(ISEQ_BODY(iseq)->location.label);
if (pc >= 0 && pc <= ISEQ_BODY(iseq)->iseq_size) {
if (pc >= 0 && (size_t)pc <= ISEQ_BODY(iseq)->iseq_size) {
line = rb_vm_get_sourceline(cfp);
}
if (line) {
Expand Down
10 changes: 8 additions & 2 deletions win32/mkexports.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,21 @@ def each_export(objs)

class Exports::Cygwin < Exports
def self.nm
@@nm ||= RbConfig::CONFIG["NM"]
@@nm ||=
begin
require 'shellwords'
RbConfig::CONFIG["NM"].shellsplit
end
end

def exports(*)
super()
end

def each_line(objs, &block)
IO.foreach("|#{self.class.nm} --extern-only --defined-only #{objs.join(' ')}", &block)
IO.popen([*self.class.nm, *%w[--extern-only --defined-only], *objs]) do |f|
f.each(&block)
end
end

def each_export(objs)
Expand Down
3 changes: 2 additions & 1 deletion zjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use crate::invariants::{
track_bop_assumption, track_cme_assumption, track_no_ep_escape_assumption, track_no_trace_point_assumption,
track_single_ractor_assumption, track_stable_constant_names_assumption, track_no_singleton_class_assumption
};
use crate::gc::{append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqCodePtrs, IseqPayload, IseqStatus};
use crate::gc::append_gc_offsets;
use crate::payload::{get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqCodePtrs, IseqPayload, IseqStatus};
use crate::state::ZJITState;
use crate::stats::{send_fallback_counter, exit_counter_for_compile_error, incr_counter, incr_counter_by, send_fallback_counter_for_method_type, send_without_block_fallback_counter_for_method_type, send_without_block_fallback_counter_for_optimized_method_type, send_fallback_counter_ptr_for_opcode, CompileError};
use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_ns, exit_compile_error}};
Expand Down
87 changes: 2 additions & 85 deletions zjit/src/gc.rs
Original file line number Diff line number Diff line change
@@ -1,94 +1,11 @@
//! This module is responsible for marking/moving objects on GC.

use std::{ffi::c_void, ops::Range};
use crate::codegen::IseqCallRef;
use crate::stats::CompileError;
use crate::{cruby::*, profile::IseqProfile, state::ZJITState, stats::with_time_stat, virtualmem::CodePtr};
use crate::{cruby::*, state::ZJITState, stats::with_time_stat, virtualmem::CodePtr};
use crate::payload::{IseqPayload, get_or_create_iseq_payload, payload_ptr_as_mut};
use crate::stats::Counter::gc_time_ns;
use crate::state::gc_mark_raw_samples;

/// This is all the data ZJIT stores on an ISEQ. We mark objects in this struct on GC.
#[derive(Debug)]
pub struct IseqPayload {
/// Compilation status of the ISEQ. It has the JIT code address of the first block if Compiled.
pub status: IseqStatus,

/// Type information of YARV instruction operands
pub profile: IseqProfile,

/// GC offsets of the JIT code. These are the addresses of objects that need to be marked.
pub gc_offsets: Vec<CodePtr>,

/// JIT-to-JIT calls in the ISEQ. The IseqPayload's ISEQ is the caller of it.
pub iseq_calls: Vec<IseqCallRef>,
}

impl IseqPayload {
fn new(iseq_size: u32) -> Self {
Self {
status: IseqStatus::NotCompiled,
profile: IseqProfile::new(iseq_size),
gc_offsets: vec![],
iseq_calls: vec![],
}
}
}

/// Set of CodePtrs for an ISEQ
#[derive(Clone, Debug, PartialEq)]
pub struct IseqCodePtrs {
/// Entry for the interpreter
pub start_ptr: CodePtr,
/// Entries for JIT-to-JIT calls
pub jit_entry_ptrs: Vec<CodePtr>,
}

#[derive(Debug, PartialEq)]
pub enum IseqStatus {
Compiled(IseqCodePtrs),
CantCompile(CompileError),
NotCompiled,
}

/// Get a pointer to the payload object associated with an ISEQ. Create one if none exists.
pub fn get_or_create_iseq_payload_ptr(iseq: IseqPtr) -> *mut IseqPayload {
type VoidPtr = *mut c_void;

unsafe {
let payload = rb_iseq_get_zjit_payload(iseq);
if payload.is_null() {
// Allocate a new payload with Box and transfer ownership to the GC.
// We drop the payload with Box::from_raw when the GC frees the ISEQ and calls us.
// NOTE(alan): Sometimes we read from an ISEQ without ever writing to it.
// We allocate in those cases anyways.
let iseq_size = get_iseq_encoded_size(iseq);
let new_payload = IseqPayload::new(iseq_size);
let new_payload = Box::into_raw(Box::new(new_payload));
rb_iseq_set_zjit_payload(iseq, new_payload as VoidPtr);

new_payload
} else {
payload as *mut IseqPayload
}
}
}

/// Get the payload object associated with an ISEQ. Create one if none exists.
pub fn get_or_create_iseq_payload(iseq: IseqPtr) -> &'static mut IseqPayload {
let payload_non_null = get_or_create_iseq_payload_ptr(iseq);
payload_ptr_as_mut(payload_non_null)
}

/// Convert an IseqPayload pointer to a mutable reference. Only one reference
/// should be kept at a time.
fn payload_ptr_as_mut(payload_ptr: *mut IseqPayload) -> &'static mut IseqPayload {
// SAFETY: we should have the VM lock and all other Ruby threads should be asleep. So we have
// exclusive mutable access.
// Hmm, nothing seems to stop calling this on the same
// iseq twice, though, which violates aliasing rules.
unsafe { payload_ptr.as_mut() }.unwrap()
}

/// GC callback for marking GC objects in the per-ISEQ payload.
#[unsafe(no_mangle)]
pub extern "C" fn rb_zjit_iseq_mark(payload: *mut c_void) {
Expand Down
2 changes: 1 addition & 1 deletion zjit/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#![allow(clippy::if_same_then_else)]
#![allow(clippy::match_like_matches_macro)]
use crate::{
cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, gc::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState
cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState
};
use std::{
cell::RefCell, collections::{HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter
Expand Down
5 changes: 3 additions & 2 deletions zjit/src/invariants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

use std::{collections::{HashMap, HashSet}, mem};

use crate::{backend::lir::{asm_comment, Assembler}, cruby::{iseq_name, rb_callable_method_entry_t, rb_gc_location, ruby_basic_operators, src_loc, with_vm_lock, IseqPtr, RedefinitionFlag, ID, VALUE}, gc::IseqPayload, hir::Invariant, options::debug, state::{zjit_enabled_p, ZJITState}, virtualmem::CodePtr};
use crate::{backend::lir::{asm_comment, Assembler}, cruby::{iseq_name, rb_callable_method_entry_t, rb_gc_location, ruby_basic_operators, src_loc, with_vm_lock, IseqPtr, RedefinitionFlag, ID, VALUE}, hir::Invariant, options::debug, state::{zjit_enabled_p, ZJITState}, virtualmem::CodePtr};
use crate::payload::IseqPayload;
use crate::stats::with_time_stat;
use crate::stats::Counter::invalidation_time_ns;
use crate::gc::remove_gc_offsets;
Expand Down Expand Up @@ -367,7 +368,7 @@ pub fn track_no_trace_point_assumption(patch_point_ptr: CodePtr, side_exit_ptr:

#[unsafe(no_mangle)]
pub extern "C" fn rb_zjit_tracing_invalidate_all() {
use crate::gc::{get_or_create_iseq_payload, IseqStatus};
use crate::payload::{get_or_create_iseq_payload, IseqStatus};
use crate::cruby::{for_each_iseq, rb_iseq_reset_jit_func};

if !zjit_enabled_p() {
Expand Down
1 change: 1 addition & 0 deletions zjit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ mod profile;
mod invariants;
mod bitset;
mod gc;
mod payload;
86 changes: 86 additions & 0 deletions zjit/src/payload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::ffi::c_void;
use crate::codegen::IseqCallRef;
use crate::stats::CompileError;
use crate::{cruby::*, profile::IseqProfile, virtualmem::CodePtr};

/// This is all the data ZJIT stores on an ISEQ. We mark objects in this struct on GC.
#[derive(Debug)]
pub struct IseqPayload {
/// Compilation status of the ISEQ. It has the JIT code address of the first block if Compiled.
pub status: IseqStatus,

/// Type information of YARV instruction operands
pub profile: IseqProfile,

/// GC offsets of the JIT code. These are the addresses of objects that need to be marked.
pub gc_offsets: Vec<CodePtr>,

/// JIT-to-JIT calls in the ISEQ. The IseqPayload's ISEQ is the caller of it.
pub iseq_calls: Vec<IseqCallRef>,
}

impl IseqPayload {
fn new(iseq_size: u32) -> Self {
Self {
status: IseqStatus::NotCompiled,
profile: IseqProfile::new(iseq_size),
gc_offsets: vec![],
iseq_calls: vec![],
}
}
}

/// Set of CodePtrs for an ISEQ
#[derive(Clone, Debug, PartialEq)]
pub struct IseqCodePtrs {
/// Entry for the interpreter
pub start_ptr: CodePtr,
/// Entries for JIT-to-JIT calls
pub jit_entry_ptrs: Vec<CodePtr>,
}

#[derive(Debug, PartialEq)]
pub enum IseqStatus {
Compiled(IseqCodePtrs),
CantCompile(CompileError),
NotCompiled,
}

/// Get a pointer to the payload object associated with an ISEQ. Create one if none exists.
pub fn get_or_create_iseq_payload_ptr(iseq: IseqPtr) -> *mut IseqPayload {
type VoidPtr = *mut c_void;

unsafe {
let payload = rb_iseq_get_zjit_payload(iseq);
if payload.is_null() {
// Allocate a new payload with Box and transfer ownership to the GC.
// We drop the payload with Box::from_raw when the GC frees the ISEQ and calls us.
// NOTE(alan): Sometimes we read from an ISEQ without ever writing to it.
// We allocate in those cases anyways.
let iseq_size = get_iseq_encoded_size(iseq);
let new_payload = IseqPayload::new(iseq_size);
let new_payload = Box::into_raw(Box::new(new_payload));
rb_iseq_set_zjit_payload(iseq, new_payload as VoidPtr);

new_payload
} else {
payload as *mut IseqPayload
}
}
}

/// Get the payload object associated with an ISEQ. Create one if none exists.
pub fn get_or_create_iseq_payload(iseq: IseqPtr) -> &'static mut IseqPayload {
let payload_non_null = get_or_create_iseq_payload_ptr(iseq);
payload_ptr_as_mut(payload_non_null)
}

/// Convert an IseqPayload pointer to a mutable reference. Only one reference
/// should be kept at a time.
pub fn payload_ptr_as_mut(payload_ptr: *mut IseqPayload) -> &'static mut IseqPayload {
// SAFETY: we should have the VM lock and all other Ruby threads should be asleep. So we have
// exclusive mutable access.
// Hmm, nothing seems to stop calling this on the same
// iseq twice, though, which violates aliasing rules.
unsafe { payload_ptr.as_mut() }.unwrap()
}
2 changes: 1 addition & 1 deletion zjit/src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// We use the YARV bytecode constants which have a CRuby-style name
#![allow(non_upper_case_globals)]

use crate::{cruby::*, gc::get_or_create_iseq_payload, options::{get_option, NumProfiles}};
use crate::{cruby::*, payload::get_or_create_iseq_payload, options::{get_option, NumProfiles}};
use crate::distribution::{Distribution, DistributionSummary};
use crate::stats::Counter::profile_time_ns;
use crate::stats::with_time_stat;
Expand Down