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
4 changes: 2 additions & 2 deletions benchmark/string_concat.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
prelude: |
CHUNK = "a" * 64
UCHUNK = "é" * 32
SHORT = "a" * (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] / 2)
LONG = "a" * (GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE] * 2)
SHORT = "a" * (GC.stat_heap(0, :slot_size) / 2)
LONG = "a" * (GC.stat_heap(0, :slot_size) * 2)
GC.disable # GC causes a lot of variance
benchmark:
binary_concat_7bit: |
Expand Down
76 changes: 40 additions & 36 deletions gc/default/default.c
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,17 @@ size_t rb_gc_impl_obj_slot_size(VALUE obj);
# endif
#endif

#define BASE_SLOT_SIZE (sizeof(struct RBasic) + sizeof(VALUE[RBIMPL_RVALUE_EMBED_LEN_MAX]) + RVALUE_OVERHEAD)
#define RVALUE_SLOT_SIZE (sizeof(struct RBasic) + sizeof(VALUE[RBIMPL_RVALUE_EMBED_LEN_MAX]) + RVALUE_OVERHEAD)

static const size_t pool_slot_sizes[HEAP_COUNT] = {
RVALUE_SLOT_SIZE,
RVALUE_SLOT_SIZE * 2,
RVALUE_SLOT_SIZE * 4,
RVALUE_SLOT_SIZE * 8,
RVALUE_SLOT_SIZE * 16,
};

static uint8_t size_to_heap_idx[RVALUE_SLOT_SIZE * (1 << (HEAP_COUNT - 1)) / 8 + 1];

#ifndef MAX
# define MAX(a, b) (((a) > (b)) ? (a) : (b))
Expand All @@ -701,7 +711,7 @@ enum {
HEAP_PAGE_ALIGN = (1UL << HEAP_PAGE_ALIGN_LOG),
HEAP_PAGE_ALIGN_MASK = (~(~0UL << HEAP_PAGE_ALIGN_LOG)),
HEAP_PAGE_SIZE = HEAP_PAGE_ALIGN,
HEAP_PAGE_BITMAP_LIMIT = CEILDIV(CEILDIV(HEAP_PAGE_SIZE, BASE_SLOT_SIZE), BITS_BITLENGTH),
HEAP_PAGE_BITMAP_LIMIT = CEILDIV(CEILDIV(HEAP_PAGE_SIZE, RVALUE_SLOT_SIZE), BITS_BITLENGTH),
HEAP_PAGE_BITMAP_SIZE = (BITS_SIZE * HEAP_PAGE_BITMAP_LIMIT),
};
#define HEAP_PAGE_ALIGN (1 << HEAP_PAGE_ALIGN_LOG)
Expand Down Expand Up @@ -1636,7 +1646,7 @@ heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj
/* obj should belong to page */
!(page->start <= (uintptr_t)obj &&
(uintptr_t)obj < ((uintptr_t)page->start + (page->total_slots * page->slot_size)) &&
obj % BASE_SLOT_SIZE == 0)) {
obj % pool_slot_sizes[0] == 0)) {
rb_bug("heap_page_add_freeobj: %p is not rvalue.", (void *)obj);
}

Expand Down Expand Up @@ -2237,22 +2247,13 @@ heap_slot_size(unsigned char pool_id)
{
GC_ASSERT(pool_id < HEAP_COUNT);

size_t slot_size = (1 << pool_id) * BASE_SLOT_SIZE;

#if RGENGC_CHECK_MODE
rb_objspace_t *objspace = rb_gc_get_objspace();
GC_ASSERT(heaps[pool_id].slot_size == (short)slot_size);
#endif

slot_size -= RVALUE_OVERHEAD;

return slot_size;
return pool_slot_sizes[pool_id] - RVALUE_OVERHEAD;
}

bool
rb_gc_impl_size_allocatable_p(size_t size)
{
return size <= heap_slot_size(HEAP_COUNT - 1);
return size + RVALUE_OVERHEAD <= pool_slot_sizes[HEAP_COUNT - 1];
}

static const size_t ALLOCATED_COUNT_STEP = 1024;
Expand Down Expand Up @@ -2351,28 +2352,30 @@ ractor_cache_set_page(rb_objspace_t *objspace, rb_ractor_newobj_cache_t *cache,
rb_asan_poison_object((VALUE)heap_cache->freelist);
}

static void
init_size_to_heap_idx(void)
{
for (size_t i = 0; i < sizeof(size_to_heap_idx); i++) {
size_t effective = i * 8 + RVALUE_OVERHEAD;
uint8_t idx;
for (idx = 0; idx < HEAP_COUNT; idx++) {
if (effective <= pool_slot_sizes[idx]) break;
}
size_to_heap_idx[i] = idx;
}
}

static inline size_t
heap_idx_for_size(size_t size)
{
size += RVALUE_OVERHEAD;

size_t slot_count = CEILDIV(size, BASE_SLOT_SIZE);

/* heap_idx is ceil(log2(slot_count)) */
size_t heap_idx = 64 - nlz_int64(slot_count - 1);

if (heap_idx >= HEAP_COUNT) {
rb_bug("heap_idx_for_size: allocation size too large "
"(size=%"PRIuSIZE"u, heap_idx=%"PRIuSIZE"u)", size, heap_idx);
size_t compressed = (size + 7) >> 3;
if (compressed < sizeof(size_to_heap_idx)) {
size_t heap_idx = size_to_heap_idx[compressed];
if (RB_LIKELY(heap_idx < HEAP_COUNT)) return heap_idx;
}

#if RGENGC_CHECK_MODE
rb_objspace_t *objspace = rb_gc_get_objspace();
GC_ASSERT(size <= (size_t)heaps[heap_idx].slot_size);
if (heap_idx > 0) GC_ASSERT(size > (size_t)heaps[heap_idx - 1].slot_size);
#endif

return heap_idx;
rb_bug("heap_idx_for_size: allocation size too large "
"(size=%"PRIuSIZE")", size);
}

size_t
Expand Down Expand Up @@ -2589,7 +2592,7 @@ is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr)
if (p < heap_pages_lomem || p > heap_pages_himem) return FALSE;
RB_DEBUG_COUNTER_INC(gc_isptr_range);

if (p % BASE_SLOT_SIZE != 0) return FALSE;
if (p % pool_slot_sizes[0] != 0) return FALSE;
RB_DEBUG_COUNTER_INC(gc_isptr_align);

page = heap_page_for_ptr(objspace, (uintptr_t)ptr);
Expand Down Expand Up @@ -3495,7 +3498,7 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit

do {
VALUE vp = (VALUE)p;
GC_ASSERT(vp % BASE_SLOT_SIZE == 0);
GC_ASSERT(vp % pool_slot_sizes[0] == 0);

rb_asan_unpoison_object(vp, false);
if (bitset & 1) {
Expand Down Expand Up @@ -5594,7 +5597,7 @@ gc_compact_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t b

do {
VALUE vp = (VALUE)p;
GC_ASSERT(vp % BASE_SLOT_SIZE == 0);
GC_ASSERT(vp % pool_slot_sizes[0] == 0);

if (bitset & 1) {
objspace->rcompactor.considered_count_table[BUILTIN_TYPE(vp)]++;
Expand Down Expand Up @@ -9518,12 +9521,14 @@ rb_gc_impl_objspace_init(void *objspace_ptr)
for (int i = 0; i < HEAP_COUNT; i++) {
rb_heap_t *heap = &heaps[i];

heap->slot_size = (1 << i) * BASE_SLOT_SIZE;
heap->slot_size = pool_slot_sizes[i];
slot_div_magics[i] = (uint32_t)((uint64_t)UINT32_MAX / heap->slot_size + 1);

ccan_list_head_init(&heap->pages);
}

init_size_to_heap_idx();

rb_darray_make_without_gc(&objspace->heap_pages.sorted, 0);
rb_darray_make_without_gc(&objspace->weak_references, 0);

Expand Down Expand Up @@ -9556,7 +9561,6 @@ rb_gc_impl_init(void)
{
VALUE gc_constants = rb_hash_new();
rb_hash_aset(gc_constants, ID2SYM(rb_intern("DEBUG")), GC_DEBUG ? Qtrue : Qfalse);
rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(BASE_SLOT_SIZE - RVALUE_OVERHEAD));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RBASIC_SIZE")), SIZET2NUM(sizeof(struct RBasic)));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), SIZET2NUM(RVALUE_OVERHEAD));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_PAGE_BITMAP_SIZE")), SIZET2NUM(HEAP_PAGE_BITMAP_SIZE));
Expand Down
19 changes: 15 additions & 4 deletions gc/mmtk/mmtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,6 @@ void
rb_gc_impl_init(void)
{
VALUE gc_constants = rb_hash_new();
rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(sizeof(VALUE) * 5));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RBASIC_SIZE")), SIZET2NUM(sizeof(struct RBasic)));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), INT2NUM(0));
rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(MMTK_MAX_OBJ_SIZE));
Expand Down Expand Up @@ -1536,12 +1535,24 @@ rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym)
VALUE
rb_gc_impl_stat_heap(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym)
{
if (FIXNUM_P(heap_name) && SYMBOL_P(hash_or_sym)) {
int heap_idx = FIX2INT(heap_name);
if (heap_idx < 0 || heap_idx >= MMTK_HEAP_COUNT) {
rb_raise(rb_eArgError, "size pool index out of range");
}

if (hash_or_sym == ID2SYM(rb_intern("slot_size"))) {
return SIZET2NUM(heap_sizes[heap_idx]);
}

return Qundef;
}

if (RB_TYPE_P(hash_or_sym, T_HASH)) {
return hash_or_sym;
}
else {
return Qundef;
}

return Qundef;
}

// Miscellaneous
Expand Down
8 changes: 4 additions & 4 deletions lib/prism/lex_compat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ class Result < Prism::Result

# Create a new lex compat result object with the given values.
#--
#: (Array[lex_compat_token] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void
def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
#: (Array[lex_compat_token] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source)
@value = value
super(comments, magic_comments, data_loc, errors, warnings, source)
super(comments, magic_comments, data_loc, errors, warnings, continuable, source)
end

# Implement the hash pattern matching interface for Result.
Expand Down Expand Up @@ -825,7 +825,7 @@ def result

tokens = post_process_tokens(tokens, source, result.data_loc, bom, eof_token)

Result.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, source)
Result.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, result.continuable?, source)
end

private
Expand Down
73 changes: 14 additions & 59 deletions lib/prism/parse_result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -898,13 +898,14 @@ class Result

# Create a new result object with the given values.
#--
#: (Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void
def initialize(comments, magic_comments, data_loc, errors, warnings, source)
#: (Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
def initialize(comments, magic_comments, data_loc, errors, warnings, continuable, source)
@comments = comments
@magic_comments = magic_comments
@data_loc = data_loc
@errors = errors
@warnings = warnings
@continuable = continuable
@source = source
end

Expand Down Expand Up @@ -961,54 +962,8 @@ def failure?
#--
#: () -> bool
def continuable?
return false if errors.empty?

offset = source.source.bytesize
errors.all? { |error| CONTINUABLE.include?(error.type) || error.location.start_offset == offset }
end

# The set of error types whose location the parser places at the opening
# token of an unclosed construct rather than at the end of the source. These
# errors always indicate incomplete input regardless of their byte position,
# so they are checked by type rather than by location.
#--
#: Array[Symbol]
CONTINUABLE = %i[
begin_term
begin_upcase_term
block_param_pipe_term
block_term_brace
block_term_end
case_missing_conditions
case_term
class_term
conditional_term
conditional_term_else
def_term
embdoc_term
end_upcase_term
for_term
hash_term
heredoc_term
lambda_term_brace
lambda_term_end
list_i_lower_term
list_i_upper_term
list_w_lower_term
list_w_upper_term
module_term
regexp_term
rescue_term
string_interpolated_term
string_literal_eof
symbol_term_dynamic
symbol_term_interpolated
until_term
while_term
xstring_term
].freeze

private_constant :CONTINUABLE
@continuable
end

# Create a code units cache for the given encoding.
#--
Expand All @@ -1033,10 +988,10 @@ class ParseResult < Result

# Create a new parse result object with the given values.
#--
#: (ProgramNode value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void
def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
#: (ProgramNode value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source)
@value = value
super(comments, magic_comments, data_loc, errors, warnings, source)
super(comments, magic_comments, data_loc, errors, warnings, continuable, source)
end

# Implement the hash pattern matching interface for ParseResult.
Expand Down Expand Up @@ -1077,10 +1032,10 @@ class LexResult < Result

# Create a new lex result object with the given values.
#--
#: (Array[[Token, Integer]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void
def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
#: (Array[[Token, Integer]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source)
@value = value
super(comments, magic_comments, data_loc, errors, warnings, source)
super(comments, magic_comments, data_loc, errors, warnings, continuable, source)
end

# Implement the hash pattern matching interface for LexResult.
Expand All @@ -1099,10 +1054,10 @@ class ParseLexResult < Result

# Create a new parse lex result object with the given values.
#--
#: ([ProgramNode, Array[[Token, Integer]]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void
def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
#: ([ProgramNode, Array[[Token, Integer]]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source)
@value = value
super(comments, magic_comments, data_loc, errors, warnings, source)
super(comments, magic_comments, data_loc, errors, warnings, continuable, source)
end

# Implement the hash pattern matching interface for ParseLexResult.
Expand Down
3 changes: 2 additions & 1 deletion prism/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,10 +641,11 @@ parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_enco
parser_data_loc(parser, source, freeze),
parser_errors(parser, encoding, source, freeze),
parser_warnings(parser, encoding, source, freeze),
parser->continuable ? Qtrue : Qfalse,
source
};

return rb_class_new_instance_freeze(7, result_argv, class, freeze);
return rb_class_new_instance_freeze(8, result_argv, class, freeze);
}

/******************************************************************************/
Expand Down
8 changes: 8 additions & 0 deletions prism/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,14 @@ struct pm_parser {
/** Whether or not we're currently recovering from a syntax error. */
bool recovering;

/**
* Whether or not the source being parsed could become valid if more input
* were appended. This is set to false when the parser encounters a token
* that is definitively wrong (e.g., a stray `end` or `]`) as opposed to
* merely incomplete.
*/
bool continuable;

/**
* This is very specialized behavior for when you want to parse in a context
* that does not respect encoding comments. Its main use case is translating
Expand Down
Loading