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
13 changes: 10 additions & 3 deletions array.c
Original file line number Diff line number Diff line change
Expand Up @@ -2944,13 +2944,20 @@ rb_zjit_array_dup_can_fastpath(VALUE ary, size_t *alloc_size_out, VALUE *flags_o

if (len > embed_capa) return false;

*alloc_size_out = sizeof(struct RArray);
*flags_out = T_ARRAY | RARRAY_EMBED_FLAG | ((VALUE)len << RARRAY_EMBED_LEN_SHIFT);
*len_out = len;
return true;
}

void
rb_zjit_array_new_fastpath(size_t *alloc_size_out, VALUE *flags_out)
{
size_t size = sizeof(struct RArray);
shape_id_t shape_id = rb_shape_transition_slot_size(ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER,
rb_gc_size_slot_size(size));
*alloc_size_out = size;
*flags_out = T_ARRAY | RARRAY_EMBED_FLAG | ((VALUE)len << RARRAY_EMBED_LEN_SHIFT) | ((VALUE)shape_id << SHAPE_FLAG_SHIFT);
*len_out = len;
return true;
*flags_out = T_ARRAY | RARRAY_EMBED_FLAG | ((VALUE)shape_id << SHAPE_FLAG_SHIFT);
}
#endif

Expand Down
17 changes: 11 additions & 6 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,7 @@ rb_class_allocate_instance(VALUE klass)

#if USE_ZJIT
bool
rb_zjit_class_allocate_instance_fastpath(VALUE klass, size_t *size_out, shape_id_t *shape_id_out)
rb_zjit_class_allocate_instance_fastpath(VALUE klass, size_t *size_out, VALUE *flags_out)
{
uint32_t index_tbl_num_entries = RCLASS_MAX_IV_COUNT(klass);

Expand All @@ -1261,12 +1261,17 @@ rb_zjit_class_allocate_instance_fastpath(VALUE klass, size_t *size_out, shape_id
return false;
}

size_t size = robject_embedded_size(index_tbl_num_entries);
*size_out = size;
*shape_id_out = rb_shape_transition_slot_size(rb_shape_transition_robject(0),
rb_gc_size_slot_size(size));
*size_out = robject_embedded_size(index_tbl_num_entries);
*flags_out = T_OBJECT | rb_shape_transition_robject(0);

return true;
}

bool
rb_zjit_newobj_hook_enabled_p(void)
{
return rb_gc_event_hook_required_p(RUBY_INTERNAL_EVENT_NEWOBJ);
}
#endif

void
Expand Down Expand Up @@ -3773,7 +3778,7 @@ rb_gc_ractor_cache_free(void *cache)
bool
rb_gc_zjit_new_obj_fastpath(size_t alloc_size, VALUE flags, VALUE klass, struct rb_gc_zjit_fastpath *fastpath)
{
#if RACTOR_CHECK_MODE || defined(RUBY_ASAN_ENABLED)
#if defined(RUBY_ASAN_ENABLED)
(void)rb_gc_impl_zjit_new_obj_fastpath;
return false;
#else
Expand Down
5 changes: 5 additions & 0 deletions gc/default/default.c
Original file line number Diff line number Diff line change
Expand Up @@ -5418,6 +5418,11 @@ init_mark_stack(mark_stack_t *stack)

/* Marking */

ALWAYS_INLINE(static int gc_mark_set(rb_objspace_t *objspace, VALUE obj));
ALWAYS_INLINE(static void gc_mark_check_t_none(rb_objspace_t *objspace, VALUE obj));
ALWAYS_INLINE(static void rgengc_check_relation(rb_objspace_t *objspace, VALUE obj));
ALWAYS_INLINE(static void gc_aging(rb_objspace_t *objspace, VALUE obj));
ALWAYS_INLINE(static void gc_grey(rb_objspace_t *objspace, VALUE obj));
static void
rgengc_check_relation(rb_objspace_t *objspace, VALUE obj)
{
Expand Down
8 changes: 2 additions & 6 deletions hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1467,12 +1467,8 @@ hash_alloc(VALUE klass)
size_t
rb_zjit_hash_new_size(VALUE *flags_out)
{
size_t size = hash_slot_size(sizeof(st_table) > sizeof(ar_table));
// mimic rb_newobj()
shape_id_t shape_id = rb_shape_transition_slot_size(ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER,
rb_gc_size_slot_size(size));
*flags_out = T_HASH | ((VALUE)shape_id << SHAPE_FLAG_SHIFT);
return size;
*flags_out = T_HASH;
return hash_slot_size(sizeof(st_table) > sizeof(ar_table));
}
#endif

Expand Down
16 changes: 9 additions & 7 deletions ractor.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,20 +268,22 @@ ractor_mark_unshareable_parts(rb_ractor_t *r)
ccan_list_for_each(&r->threads.set, th, lt_node) {
VM_ASSERT(th != NULL);
rb_gc_mark(th->self);
/* Mark the EC directly: the stack must stay alive even in windows where
* the Thread wrapper's own mark has not been traversed yet (mid-creation,
* teardown). */
if (th->ec) rb_execution_context_mark(th->ec);

/* A thread's ec lives inside the root fiber struct and is freed with that
* fiber's wrapper object, so keep the fiber wrappers alive from here too. */
if (th->root_fiber) {
VALUE root_fiber_self = rb_fiberptr_self(th->root_fiber);
if (root_fiber_self) rb_gc_mark(root_fiber_self);
}
if (th->ec && th->ec->fiber_ptr) {
VALUE fiber_self = rb_fiberptr_self(th->ec->fiber_ptr);
if (fiber_self) rb_gc_mark(fiber_self);
/* The ec sits inside its fiber, so marking that fiber's wrapper scans the ec
* as well. Only when there is no wrapper yet (mid-creation, teardown) does
* the ec need marking of its own. */
VALUE ec_fiber_self = (th->ec && th->ec->fiber_ptr) ? rb_fiberptr_self(th->ec->fiber_ptr) : 0;
if (ec_fiber_self) {
rb_gc_mark(ec_fiber_self);
}
else if (th->ec) {
rb_execution_context_mark(th->ec);
}

/* Root the thread's remaining possessions directly as well; thgroup in
Expand Down
15 changes: 4 additions & 11 deletions range.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,20 +87,13 @@ void
rb_zjit_range_new_fastpath(bool exclude_end, size_t *alloc_size_out, VALUE *flags_out)
{
const long len = 2;
size_t size = offsetof(struct RStruct, as.ary) + (sizeof(VALUE) * len);
*alloc_size_out = offsetof(struct RStruct, as.ary) + (sizeof(VALUE) * len);
if (RCLASS_MAX_IV_COUNT(rb_cRange) > 0) {
size += sizeof(VALUE);
*alloc_size_out += sizeof(VALUE);
}

VALUE flags = T_STRUCT | (len << RSTRUCT_EMBED_LEN_SHIFT) | RANGE_FL_INIT | FL_FREEZE;
if (exclude_end) flags |= RANGE_FL_EXCL;

shape_id_t shape_id = rb_shape_transition_slot_size(ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_EXTENDED,
rb_gc_size_slot_size(size));
shape_id = rb_shape_transition_frozen(shape_id);

*alloc_size_out = size;
*flags_out = flags | ((VALUE)shape_id << SHAPE_FLAG_SHIFT);
*flags_out = T_STRUCT | (len << RSTRUCT_EMBED_LEN_SHIFT) | RANGE_FL_INIT | FL_FREEZE;
if (exclude_end) *flags_out |= RANGE_FL_EXCL;
}
#endif

Expand Down
6 changes: 5 additions & 1 deletion spec/mspec/lib/mspec/utils/deprecate.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
module MSpec
FATAL_DEPRECATION = ENV['MSPEC_FATAL_DEPRECATION']

def self.deprecate(what, replacement)
user_caller = caller.find { |line| !line.include?('lib/mspec') }
$stderr.puts "\n#{what} is deprecated, use #{replacement} instead.\nfrom #{user_caller}"
message = "\n#{what} is deprecated, use #{replacement} instead.\nfrom #{user_caller}"
$stderr.puts message
raise SpecExpectationNotMetError, message if FATAL_DEPRECATION
end
end
18 changes: 7 additions & 11 deletions spec/ruby/core/array/sort_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,16 @@
end

it "uses the sign of Integer block results as the sort result" do
ruby_exe(<<~RUBY).should == "[-4, 1, 2, 5, 7, 10, 12]\n"
a = [1, 2, 5, 10, 7, -4, 12]
begin
class Integer
alias old_spaceship <=>
def <=>(other)
raise
end
end
a.sort {|n, m| (n - m) * (2 ** 200)}.should == [-4, 1, 2, 5, 7, 10, 12]
ensure
class Integer
alias <=> old_spaceship
class Integer
alias old_spaceship <=>
def <=>(other)
raise
end
end
p a.sort { |n,m| (n - m) * (2 ** 200) }
RUBY
end

it "compares values returned by block with 0" do
Expand Down
27 changes: 0 additions & 27 deletions spec/ruby/core/proc/fixtures/refined.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,4 @@ def quiet
end
end
end

# Refines operators and element access, including Hash#[] with a String
# key, so specs can check the specialized call paths implementations use
# for them.
module Operators
refine Integer do
def +(other)
"plus(#{self},#{other})"
end

def <(other)
"lt"
end
end

refine Array do
def [](i)
"at#{i}"
end
end

refine Hash do
def [](k)
"aref(#{k})"
end
end
end
end
33 changes: 33 additions & 0 deletions spec/ruby/core/proc/fixtures/refined_basic_operations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Refines basic operations like operators and element access,
# including Hash#[] with a String key, so specs can check
# the specialized call paths implementations use for them.
# Do this in a subprocess to not disable optimizations globally for the main process.
module Operators
refine Integer do
def +(other)
"plus(#{self},#{other})"
end

def <(other)
"lt"
end
end

refine Array do
def [](i)
"at#{i}"
end
end

refine Hash do
def [](k)
"aref(#{k})"
end
end
end

refined = -> a, b { [a + b, a < b] }.refined(Operators)
puts refined.call(1, 2)
puts -> a { a[0] }.refined(Operators).call([9])
puts -> h { h["x"] }.refined(Operators).call({ "x" => 1 })
puts -> a, b { a + b }.call(1, 2)
13 changes: 8 additions & 5 deletions spec/ruby/core/proc/refined_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,14 @@ def shout_hi
end

it "applies the refinements to operators and element access" do
refined = -> a, b { [a + b, a < b] }.refined(ProcRefinedSpecs::Operators)
refined.call(1, 2).should == ["plus(1,2)", "lt"]
-> a { a[0] }.refined(ProcRefinedSpecs::Operators).call([9]).should == "at0"
-> h { h["x"] }.refined(ProcRefinedSpecs::Operators).call({ "x" => 1 }).should == "aref(x)"
-> a, b { a + b }.call(1, 2).should == 3
file = fixture(__FILE__, "refined_basic_operations.rb")
ruby_exe(file).should == <<~EXPECTED
plus(1,2)
lt
at0
aref(x)
3
EXPECTED
end

it "keeps the refinements active when called via instance_eval, instance_exec and class_eval" do
Expand Down
3 changes: 2 additions & 1 deletion spec/ruby/shared/file/setgid.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
describe :file_setgid, shared: true do
platform_is :darwin do
# Fails on RubyCI
quarantine! do # platform_is :darwin do
it "accepts a path in a non-UTF-8, ASCII-compatible encoding containing non-ASCII characters" do
utf8_path = tmp("file_predicate_utf8_path_\u{3042}.txt")
# Can fail with UndefinedConversionError if tmp path has non-Shift_JIS chars (e.g. Emojis, Hangul, Cyrillic, accented letters)
Expand Down
25 changes: 9 additions & 16 deletions string.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ STR_EMBEDDABLE_P(long len, long termlen)
return rb_gc_size_allocatable_p(rb_str_embed_size(len, termlen));
}

/* Substrings and duplicated strings that need a slot larger than this are shared
* instead of copied. Larger slots hold fewer objects per page and trigger GC
* more often, which outweighs the copy they save; see [Feature #22186] for the
* benchmarks. */
#define STR_COPY_MAX_EMBED_SIZE 256

static VALUE str_replace_shared_without_enc(VALUE str2, VALUE str);
static VALUE str_new_frozen(VALUE klass, VALUE orig);
static VALUE str_new_frozen_buffer(VALUE klass, VALUE orig, int copy_encoding);
Expand Down Expand Up @@ -1977,16 +1983,11 @@ str_duplicate_setup_heap(VALUE klass, VALUE str, VALUE dup)
str_duplicate_setup_encoding(str, dup, flags);
}

/* Force duplicated strings above 256 bytes to be views rather than copies since
* copying will use memory and have significant overhead.
* Calculated as: 256 - header size - NUL terminator size */
#define STR_DUPLICATE_MAX_EMBED_LEN ((long)(256 - offsetof(struct RString, as.embed) - 1))

static inline VALUE
str_duplicate(VALUE klass, VALUE str)
{
VALUE dup;
if (STR_EMBED_P(str) && RSTRING_LEN(str) <= STR_DUPLICATE_MAX_EMBED_LEN) {
if (STR_EMBED_P(str) && rb_str_embed_size(RSTRING_LEN(str), 1) <= STR_COPY_MAX_EMBED_SIZE) {
dup = str_alloc_embed(klass, RSTRING_LEN(str) + TERM_LEN(str));

str_duplicate_setup_embed(klass, str, dup);
Expand Down Expand Up @@ -2070,11 +2071,8 @@ rb_zjit_str_resurrect_fastpath(VALUE str, bool chilled, size_t *size_out,
flags |= T_STRING;
if (chilled) flags |= STR_CHILLED;

shape_id_t shape_id = rb_shape_transition_slot_size(ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER,
rb_gc_size_slot_size(size));

*size_out = size;
*flags_out = flags | ((VALUE)shape_id << SHAPE_FLAG_SHIFT);
*flags_out = flags;
*len_out = len;
*byte_size_out = (size_t)(len + termlen);
return true;
Expand Down Expand Up @@ -3174,11 +3172,6 @@ rb_str_sublen(VALUE str, long pos)
}
}

/* Substrings that need a slot larger than this are shared instead of copied.
* Larger slots hold fewer objects per page and trigger GC more often, which
* outweighs the copy they save; see [Feature #22186] for the benchmarks. */
#define STR_SUBSEQ_MAX_EMBED_SIZE 256

static VALUE
str_subseq(VALUE str, long beg, long len)
{
Expand All @@ -3203,7 +3196,7 @@ str_subseq(VALUE str, long beg, long len)
const bool root_available = STR_SHARED_P(str) ||
RB_FL_TEST_RAW(str, FL_FREEZE | STR_CHILLED) == FL_FREEZE;
const size_t max_embed_size = root_available ?
rb_gc_size_slot_size(sizeof(struct RString)) : STR_SUBSEQ_MAX_EMBED_SIZE;
rb_gc_size_slot_size(sizeof(struct RString)) : STR_COPY_MAX_EMBED_SIZE;
const size_t embed_size = rb_str_embed_size(len, termlen);

if (embed_size <= max_embed_size && rb_gc_size_allocatable_p(embed_size)) {
Expand Down
6 changes: 3 additions & 3 deletions test/-ext-/string/test_rb_str_dup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
require '-test-/string'

class Test_RbStrDup < Test::Unit::TestCase
STR_DUPLICATE_MAX_EMBED_LEN = 256 - (RbConfig::SIZEOF["void*"] * 3) - 1 # From macro defined in string.c
STR_COPY_MAX_EMBED_SIZE = 256 - (RbConfig::SIZEOF["void*"] * 3) - 1 # From macro defined in string.c

def test_nested_shared_non_frozen
orig_str = "a" * (STR_DUPLICATE_MAX_EMBED_LEN + 1)
orig_str = "a" * (STR_COPY_MAX_EMBED_SIZE + 1)
str = Bug::String.rb_str_dup(Bug::String.rb_str_dup(orig_str))
assert_send([Bug::String, :shared_string?, str])
assert_not_send([Bug::String, :sharing_with_shared?, str], '[Bug #15792]')
end

def test_nested_shared_frozen
orig_str = "a" * (STR_DUPLICATE_MAX_EMBED_LEN + 1)
orig_str = "a" * (STR_COPY_MAX_EMBED_SIZE + 1)
str = Bug::String.rb_str_dup(Bug::String.rb_str_dup(orig_str).freeze)
assert_send([Bug::String, :shared_string?, str])
assert_not_send([Bug::String, :sharing_with_shared?, str], '[Bug #15792]')
Expand Down
4 changes: 2 additions & 2 deletions test/objspace/test_objspace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ def test_memsize_of
ObjectSpace.memsize_of(//.match("")))
end

STR_DUPLICATE_MAX_EMBED_LEN = 256 - (RbConfig::SIZEOF["void*"] * 3) - 1 # From macro defined in string.c
STR_COPY_MAX_EMBED_SIZE = 256 - (RbConfig::SIZEOF["void*"] * 3) - 1 # From macro defined in string.c

def test_memsize_of_root_shared_string
a = "a" * (STR_DUPLICATE_MAX_EMBED_LEN + 1)
a = "a" * (STR_COPY_MAX_EMBED_SIZE + 1)
b = a.dup
c = nil
ObjectSpace.each_object(String) {|x| break c = x if a == x and x.frozen?}
Expand Down
2 changes: 1 addition & 1 deletion test/ruby/test_string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3644,7 +3644,7 @@ def test_substring_embed

require 'objspace'

# 128 and 320 sit either side of STR_SUBSEQ_MAX_EMBED_SIZE in string.c, which
# 128 and 320 sit either side of STR_COPY_MAX_EMBED_SIZE in string.c, which
# the copy has to fit in along with the header and the terminator
substr = str.byteslice(320, 128)
assert_equal "a" * 128, substr
Expand Down
Loading