diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 8555f85b60..c8a4fe292f 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1580,6 +1580,21 @@ assert_equal "#{N}#{N}", %Q{ }.map{|r| r.take}.join } +assert_equal "ok", %Q{ + N = #{N} + a, b = 2.times.map{ + Ractor.new{ + N.times.map{|i| -(i.to_s)} + } + }.map{|r| r.take} + N.times do |i| + unless a[i].equal?(b[i]) + raise [a[i], b[i]].inspect + end + end + :ok +} + # Generic ivtbl n = N/2 assert_equal "#{n}#{n}", %Q{ diff --git a/eval.c b/eval.c index dba196c170..6ee99359f9 100644 --- a/eval.c +++ b/eval.c @@ -79,6 +79,7 @@ ruby_setup(void) Init_BareVM(); rb_vm_encoded_insn_data_table_init(); Init_vm_objects(); + Init_fstring_table(); EC_PUSH_TAG(GET_EC()); if ((state = EC_EXEC_TAG()) == TAG_NONE) { diff --git a/gc.c b/gc.c index a01bbe59f8..e6cf1162c6 100644 --- a/gc.c +++ b/gc.c @@ -341,6 +341,7 @@ rb_gc_shutdown_call_finalizer_p(VALUE obj) if (rb_obj_is_mutex(obj)) return false; if (rb_obj_is_fiber(obj)) return false; if (rb_obj_is_main_ractor(obj)) return false; + if (rb_obj_is_fstring_table(obj)) return false; return true; @@ -3528,6 +3529,7 @@ vm_weak_table_frozen_strings_foreach(st_data_t key, st_data_t value, st_data_t d return retval; } +void rb_fstring_foreach_with_replace(st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg); void rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback, vm_table_update_callback_func update_callback, @@ -3590,14 +3592,11 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback, break; } case RB_GC_VM_FROZEN_STRINGS_TABLE: { - if (vm->frozen_strings) { - st_foreach_with_replace( - vm->frozen_strings, - vm_weak_table_frozen_strings_foreach, - vm_weak_table_foreach_update_weak_key, - (st_data_t)&foreach_data - ); - } + rb_fstring_foreach_with_replace( + vm_weak_table_frozen_strings_foreach, + vm_weak_table_foreach_update_weak_key, + (st_data_t)&foreach_data + ); break; } default: diff --git a/internal/string.h b/internal/string.h index ce4623cb52..50561924f2 100644 --- a/internal/string.h +++ b/internal/string.h @@ -84,6 +84,8 @@ RUBY_SYMBOL_EXPORT_END VALUE rb_fstring_new(const char *ptr, long len); void rb_gc_free_fstring(VALUE obj); +bool rb_obj_is_fstring_table(VALUE obj); +void Init_fstring_table(); VALUE rb_obj_as_string_result(VALUE str, VALUE obj); VALUE rb_str_opt_plus(VALUE x, VALUE y); VALUE rb_str_concat_literals(size_t num, const VALUE *strary); diff --git a/internal/vm.h b/internal/vm.h index c30e26a8b4..6814795835 100644 --- a/internal/vm.h +++ b/internal/vm.h @@ -56,7 +56,6 @@ void rb_vm_check_redefinition_by_prepend(VALUE klass); int rb_vm_check_optimizable_mid(VALUE mid); VALUE rb_yield_refine_block(VALUE refinement, VALUE refinements); VALUE ruby_vm_special_exception_copy(VALUE); -PUREFUNC(st_table *rb_vm_fstring_table(void)); void rb_lastline_set_up(VALUE val, unsigned int up); diff --git a/string.c b/string.c index b278188af4..7e44c64ead 100644 --- a/string.c +++ b/string.c @@ -383,11 +383,6 @@ fstring_hash(VALUE str) #define fstring_hash rb_str_hash #endif -const struct st_hash_type rb_fstring_hash_type = { - fstring_cmp, - fstring_hash, -}; - #define BARE_STRING_P(str) (!FL_ANY_RAW(str, FL_EXIVAR) && RBASIC_CLASS(str) == rb_cString) static inline st_index_t @@ -421,90 +416,72 @@ str_store_precomputed_hash(VALUE str, st_index_t hash) } struct fstr_update_arg { - VALUE fstr; bool copy; bool force_precompute_hash; }; -static int -fstr_update_callback(st_data_t *key, st_data_t *value, st_data_t data, int existing) -{ - struct fstr_update_arg *arg = (struct fstr_update_arg *)data; - VALUE str = (VALUE)*key; +static VALUE build_fstring(VALUE str, struct fstr_update_arg *arg) { + // Unless the string is empty or binary, its coderange has been precomputed. + int coderange = ENC_CODERANGE(str); - if (existing) { - /* because of lazy sweep, str may be unmarked already and swept - * at next time */ + if (FL_TEST_RAW(str, STR_FAKESTR)) { + if (arg->copy) { + VALUE new_str; + long len = RSTRING_LEN(str); + long capa = len + sizeof(st_index_t); + int term_len = TERM_LEN(str); - if (rb_objspace_garbage_object_p(str)) { - arg->fstr = Qundef; - // When RSTRING_FSTR strings are swept, they call `st_delete`. - // To avoid a race condition if an equivalent string was inserted - // we must remove the flag immediately. - FL_UNSET_RAW(str, RSTRING_FSTR); - return ST_DELETE; - } - - arg->fstr = str; - return ST_STOP; - } - else { - // Unless the string is empty or binary, its coderange has been precomputed. - int coderange = ENC_CODERANGE(str); - - if (FL_TEST_RAW(str, STR_FAKESTR)) { - if (arg->copy) { - VALUE new_str; - long len = RSTRING_LEN(str); - long capa = len + sizeof(st_index_t); - int term_len = TERM_LEN(str); - - if (arg->force_precompute_hash && STR_EMBEDDABLE_P(capa, term_len)) { - new_str = str_alloc_embed(rb_cString, capa + term_len); - memcpy(RSTRING_PTR(new_str), RSTRING_PTR(str), len); - STR_SET_LEN(new_str, RSTRING_LEN(str)); - TERM_FILL(RSTRING_END(new_str), TERM_LEN(str)); - rb_enc_copy(new_str, str); - str_store_precomputed_hash(new_str, str_do_hash(str)); - } - else { - new_str = str_new(rb_cString, RSTRING(str)->as.heap.ptr, RSTRING(str)->len); - rb_enc_copy(new_str, str); -#ifdef PRECOMPUTED_FAKESTR_HASH - if (rb_str_capacity(new_str) >= RSTRING_LEN(str) + term_len + sizeof(st_index_t)) { - str_store_precomputed_hash(new_str, (st_index_t)RSTRING(str)->as.heap.aux.capa); - } -#endif - } - str = new_str; + if (arg->force_precompute_hash && STR_EMBEDDABLE_P(capa, term_len)) { + new_str = str_alloc_embed(rb_cString, capa + term_len); + memcpy(RSTRING_PTR(new_str), RSTRING_PTR(str), len); + STR_SET_LEN(new_str, RSTRING_LEN(str)); + TERM_FILL(RSTRING_END(new_str), TERM_LEN(str)); + rb_enc_copy(new_str, str); + str_store_precomputed_hash(new_str, str_do_hash(str)); } else { - str = str_new_static(rb_cString, RSTRING(str)->as.heap.ptr, - RSTRING(str)->len, - ENCODING_GET(str)); + new_str = str_new(rb_cString, RSTRING(str)->as.heap.ptr, RSTRING(str)->len); + rb_enc_copy(new_str, str); +#ifdef PRECOMPUTED_FAKESTR_HASH + if (rb_str_capacity(new_str) >= RSTRING_LEN(str) + term_len + sizeof(st_index_t)) { + str_store_precomputed_hash(new_str, (st_index_t)RSTRING(str)->as.heap.aux.capa); + } +#endif } - OBJ_FREEZE(str); + str = new_str; } else { - if (!OBJ_FROZEN(str) || CHILLED_STRING_P(str)) { - str = str_new_frozen(rb_cString, str); - } - if (STR_SHARED_P(str)) { /* str should not be shared */ - /* shared substring */ - str_make_independent(str); - RUBY_ASSERT(OBJ_FROZEN(str)); - } - if (!BARE_STRING_P(str)) { - str = str_new_frozen(rb_cString, str); - } + str = str_new_static(rb_cString, RSTRING(str)->as.heap.ptr, + RSTRING(str)->len, + ENCODING_GET(str)); } - - ENC_CODERANGE_SET(str, coderange); - RBASIC(str)->flags |= RSTRING_FSTR; - - *key = *value = arg->fstr = str; - return ST_CONTINUE; + OBJ_FREEZE(str); } + else { + if (!OBJ_FROZEN(str) || CHILLED_STRING_P(str)) { + str = str_new_frozen(rb_cString, str); + } + if (STR_SHARED_P(str)) { /* str should not be shared */ + /* shared substring */ + str_make_independent(str); + RUBY_ASSERT(OBJ_FROZEN(str)); + } + if (!BARE_STRING_P(str)) { + str = str_new_frozen(rb_cString, str); + } + } + + ENC_CODERANGE_SET(str, coderange); + RBASIC(str)->flags |= RSTRING_FSTR; + + RUBY_ASSERT(RB_TYPE_P(str, T_STRING)); + RUBY_ASSERT(OBJ_FROZEN(str)); + RUBY_ASSERT(!FL_TEST_RAW(str, STR_FAKESTR)); + RUBY_ASSERT(!FL_TEST_RAW(str, FL_EXIVAR)); + RUBY_ASSERT(RBASIC_CLASS(str) == rb_cString); + RUBY_ASSERT(!rb_objspace_garbage_object_p(str)); + + return str; } VALUE @@ -544,6 +521,298 @@ rb_fstring(VALUE str) return fstr; } +#define FSTRING_TABLE_EMPTY Qfalse +#define FSTRING_TABLE_TOMBSTONE Qtrue +#define FSTRING_TABLE_MOVED Qundef + +struct fstring_table_entry { + VALUE str; + VALUE hash; +}; + +struct fstring_table_struct { + struct fstring_table_entry *entries; + int capacity; + int deleted_entries; + rb_atomic_t count; // TODO: pad to own cache line? +}; + +static void fstring_table_free(void *ptr) { + struct fstring_table_struct *table = ptr; + xfree(table->entries); +} + +// We declare a type for the table so that we can lean on Ruby's GC for deferred reclamation +static const rb_data_type_t fstring_table_type = { + .wrap_struct_name = "fstring_table", + .function = { + .dmark = NULL, + .dfree = fstring_table_free, + .dsize = NULL, + }, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE +}; + + +static VALUE fstring_table_obj; + +static VALUE +new_fstring_table(int capacity) { + VALUE obj; + struct fstring_table_struct *table; + obj = TypedData_Make_Struct(0, struct fstring_table_struct, &fstring_table_type, table); + table->capacity = capacity; + table->count = 0; + table->entries = ZALLOC_N(struct fstring_table_entry, capacity); + return obj; +} + +void Init_fstring_table(void) { + fstring_table_obj = new_fstring_table(8192); + rb_gc_register_address(&fstring_table_obj); +} + +#if 0 + +// Linear probe +struct fstring_table_probe { + int idx; + int mask; +}; + +static int fstring_table_probe_start(struct fstring_table_probe *probe, struct fstring_table_struct *table, VALUE hash_code) { + RUBY_ASSERT((table->capacity & (table->capacity - 1)) == 0); + probe->mask = table->capacity - 1; + probe->idx = hash_code & probe->mask; + return probe->idx; +} + +static int fstring_table_probe_next(struct fstring_table_probe *probe) { + probe->idx = (probe->idx + 1) & probe->mask; + return probe->idx; +} + +#else + +// Struct containing probe information. Intended that the compiler should always inline this +// Quadratic probing +struct fstring_table_probe { + int idx; + int d; + int mask; +}; + +static int fstring_table_probe_start(struct fstring_table_probe *probe, struct fstring_table_struct *table, VALUE hash_code) { + RUBY_ASSERT((table->capacity & (table->capacity - 1)) == 0); + probe->d = 0; + probe->mask = table->capacity - 1; + probe->idx = hash_code & probe->mask; + return probe->idx; +} + +static int fstring_table_probe_next(struct fstring_table_probe *probe) { + probe->d++; + probe->idx = (probe->idx + probe->d) & probe->mask; + return probe->idx; +} +#endif + +#define RUBY_ATOMIC_VALUE_LOAD(x) (VALUE)(RUBY_ATOMIC_PTR_LOAD(x)) + +static void fstring_insert_on_resize(struct fstring_table_struct *table, VALUE hash_code, VALUE value) { + struct fstring_table_probe probe; + int idx = fstring_table_probe_start(&probe, table, hash_code); + + for (;;) { + struct fstring_table_entry *entry = &table->entries[idx]; + VALUE candidate = entry->str; + + RUBY_ASSERT(candidate != FSTRING_TABLE_TOMBSTONE); + RUBY_ASSERT(candidate != FSTRING_TABLE_MOVED); + + if (candidate == FSTRING_TABLE_EMPTY) { + table->count++; + + RUBY_ASSERT(table->count < table->capacity / 2); + RUBY_ASSERT(entry->hash == 0); + + entry->str = value; + entry->hash = hash_code; + return; + } + + idx = fstring_table_probe_next(&probe); + } +} + +// Rebuilds the table +static void fstring_try_resize(VALUE old_table_obj) { + RB_VM_LOCK_ENTER(); + + // Check if another thread has already resized + if (RUBY_ATOMIC_VALUE_LOAD(fstring_table_obj) != old_table_obj) { + goto end; + } + + struct fstring_table_struct *old_table = RTYPEDDATA_GET_DATA(old_table_obj); + + // This may overcount by up to the number of threads concurrently attempting to insert + // GC may also happen between now and the table being rebuilt + int expected_count = RUBY_ATOMIC_LOAD(old_table->count) - old_table->deleted_entries; + + struct fstring_table_entry *old_entries = old_table->entries; + int old_capacity = old_table->capacity; + int new_capacity = old_capacity * 2; + if (new_capacity > expected_count * 8) { + new_capacity = old_capacity / 2; + } + else if (new_capacity > expected_count * 4) { + new_capacity = old_capacity; + } + + // May cause GC and therefore deletes, so must hapen first + VALUE new_table_obj = new_fstring_table(new_capacity); + struct fstring_table_struct *new_table = RTYPEDDATA_GET_DATA(new_table_obj); + + for (int i = 0; i < old_capacity; i++) { + struct fstring_table_entry *entry = &old_entries[i]; + VALUE val = RUBY_ATOMIC_VALUE_EXCHANGE(entry->str, FSTRING_TABLE_MOVED); + RUBY_ASSERT(val != FSTRING_TABLE_MOVED); + if (val == FSTRING_TABLE_EMPTY) continue; + if (val == FSTRING_TABLE_TOMBSTONE) continue; + if (rb_objspace_garbage_object_p(val)) continue; + + VALUE hash_code = RUBY_ATOMIC_VALUE_LOAD(entry->hash); + if (hash_code == 0) { + // Either in-progress insert or extremely unlikely 0 hash + // Re-calculate the hash ourselves + hash_code = fstring_hash(val); + } + RUBY_ASSERT(hash_code == fstring_hash(val)); + fstring_insert_on_resize(new_table, hash_code, val); + } + +#if 0 + fprintf(stderr, "resized: %p(%i) -> %p(%i) (count: %i->%i)\n", old_table, old_table->capacity, new_table, new_table->capacity, old_table->count, new_table->count); +#endif + + RUBY_ATOMIC_VALUE_SET(fstring_table_obj, new_table_obj); + +end: + RB_GC_GUARD(old_table_obj); + RB_VM_LOCK_LEAVE(); +} + +static VALUE fstring_find_or_insert(VALUE hash_code, VALUE value, struct fstr_update_arg *arg) { + struct fstring_table_probe probe; + bool inserting = false; + int idx; + VALUE table_obj; + struct fstring_table_struct *table; + + retry: + table_obj = RUBY_ATOMIC_VALUE_LOAD(fstring_table_obj); + RUBY_ASSERT(table_obj); + table = RTYPEDDATA_GET_DATA(table_obj); + idx = fstring_table_probe_start(&probe, table, hash_code); + + for (;;) { + struct fstring_table_entry *entry = &table->entries[idx]; + VALUE candidate = RUBY_ATOMIC_VALUE_LOAD(entry->str); + + if (candidate == FSTRING_TABLE_EMPTY) { + // Not in table + if (!inserting) { + // Prepare a string suitable for inserting into the table + value = build_fstring(value, arg); + RUBY_ASSERT(hash_code == fstring_hash(value)); + inserting = true; + } + + int prev_count = RUBY_ATOMIC_FETCH_ADD(table->count, 1); + + if (UNLIKELY(prev_count > table->capacity / 2)) { + fstring_try_resize(table_obj); + goto retry; + } + + VALUE found = RUBY_ATOMIC_VALUE_CAS(entry->str, FSTRING_TABLE_EMPTY, value); + if (found == FSTRING_TABLE_EMPTY) { + // Success! Our value was inserted + + // Also set the hash code + RUBY_ATOMIC_VALUE_SET(entry->hash, hash_code); + + RB_GC_GUARD(table_obj); + return value; + } else { + // Nothing was inserted + RUBY_ATOMIC_DEC(table->count); // we didn't end up inserting + + // Another thread won the race, try again at the same location + continue; + } + } else if (candidate == FSTRING_TABLE_TOMBSTONE) { + // Deleted entry, continue searching + } else if (candidate == FSTRING_TABLE_MOVED) { + // Wait + RB_VM_LOCK_ENTER(); + RB_VM_LOCK_LEAVE(); + + goto retry; + } else { + VALUE candidate_hash = RUBY_ATOMIC_VALUE_LOAD(entry->hash); + if ((candidate_hash == hash_code || candidate_hash == 0) && !fstring_cmp(candidate, value)) { + // We've found a match + if (UNLIKELY(rb_objspace_garbage_object_p(candidate))) { + // This is a weakref table, so after marking but before sweeping is complete we may find a matching garbage object. + // Skip it and mark it as a tombstone to help other threads out + RUBY_ATOMIC_VALUE_CAS(entry->str, candidate, FSTRING_TABLE_TOMBSTONE); + + // Fall through and continue our search + } else { + RB_GC_GUARD(table_obj); + return candidate; + } + } + } + + idx = fstring_table_probe_next(&probe); + } +} + + +// Removes an fstring from the table. Compares by identity +static void fstring_delete(VALUE hash_code, VALUE value) { + // Delete is never called concurrently, so atomic operations are unnecessary + VALUE table_obj = RUBY_ATOMIC_VALUE_LOAD(fstring_table_obj); + RUBY_ASSERT_ALWAYS(table_obj); + struct fstring_table_struct *table = RTYPEDDATA_GET_DATA(table_obj); + + struct fstring_table_probe probe; + int idx = fstring_table_probe_start(&probe, table, hash_code); + + for (;;) { + struct fstring_table_entry *entry = &table->entries[idx]; + VALUE candidate = entry->str; + + // Allocations should only occur at the beginning of the resize + RUBY_ASSERT(candidate != FSTRING_TABLE_MOVED); + + if (candidate == FSTRING_TABLE_EMPTY) { + // We didn't find our string to delete + return; + } else if (candidate == value) { + // We found our string, replace it with a tombstone and increment the count + entry->str = FSTRING_TABLE_TOMBSTONE; + table->deleted_entries++; + return; + } + + idx = fstring_table_probe_next(&probe); + } +} + static VALUE register_fstring(VALUE str, bool copy, bool force_precompute_hash) { @@ -560,29 +829,71 @@ register_fstring(VALUE str, bool copy, bool force_precompute_hash) } #endif - RB_VM_LOCK_ENTER(); - { - st_table *frozen_strings = rb_vm_fstring_table(); - do { - args.fstr = str; - st_update(frozen_strings, (st_data_t)str, fstr_update_callback, (st_data_t)&args); - } while (UNDEF_P(args.fstr)); + VALUE hash_code = fstring_hash(str); + VALUE result = fstring_find_or_insert(hash_code, str, &args); + + RUBY_ASSERT(!rb_objspace_garbage_object_p(result)); + RUBY_ASSERT(RB_TYPE_P(result, T_STRING)); + RUBY_ASSERT(OBJ_FROZEN(result)); + RUBY_ASSERT(!FL_TEST_RAW(result, STR_FAKESTR)); + RUBY_ASSERT(!FL_TEST_RAW(result, FL_EXIVAR)); + RUBY_ASSERT(RBASIC_CLASS(result) == rb_cString); + + return result; +} + +void rb_fstring_foreach_with_replace(st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg) { + // Assume locking and barrier (which there is no assert for) + ASSERT_vm_locking(); + + VALUE table_obj = RUBY_ATOMIC_VALUE_LOAD(fstring_table_obj); + if (!table_obj) { + // Table not yet initialized. Nothing to iterate over + return; } - RB_VM_LOCK_LEAVE(); + struct fstring_table_struct *table = RTYPEDDATA_GET_DATA(table_obj); - RUBY_ASSERT(OBJ_FROZEN(args.fstr)); - RUBY_ASSERT(!FL_TEST_RAW(args.fstr, STR_FAKESTR)); - RUBY_ASSERT(!FL_TEST_RAW(args.fstr, FL_EXIVAR)); - RUBY_ASSERT(RBASIC_CLASS(args.fstr) == rb_cString); + for (int i = 0; i < table->capacity; i++) { + VALUE key = table->entries[i].str; + if(key == FSTRING_TABLE_EMPTY) continue; + if(key == FSTRING_TABLE_TOMBSTONE) continue; - return args.fstr; + enum st_retval retval; + retval = (*func)(key, key, arg, 0); + + if (retval == ST_REPLACE && replace) { + st_data_t value = key; + retval = (*replace)(&key, &value, arg, TRUE); + table->entries[i].str = key; + } + switch (retval) { + case ST_REPLACE: + case ST_CONTINUE: + break; + case ST_CHECK: + rb_bug("unsupported"); + case ST_STOP: + return; + case ST_DELETE: + table->entries[i].str = FSTRING_TABLE_TOMBSTONE; + break; + } + } +} + +bool rb_obj_is_fstring_table(VALUE obj) { + ASSERT_vm_locking(); + + return obj == fstring_table_obj; } void rb_gc_free_fstring(VALUE obj) { + // Assume locking and barrier (which there is no assert for) ASSERT_vm_locking(); - st_data_t fstr = (st_data_t)obj; - st_delete(rb_vm_fstring_table(), &fstr, NULL); + VALUE str_hash = fstring_hash(obj); + fstring_delete(str_hash, obj); + RB_DEBUG_COUNTER_INC(obj_str_fstr); FL_UNSET(obj, RSTRING_FSTR); @@ -641,18 +952,15 @@ rb_fstring_cstr(const char *ptr) return rb_fstring_new(ptr, strlen(ptr)); } -static int -fstring_set_class_i(st_data_t key, st_data_t val, st_data_t arg) -{ - RBASIC_SET_CLASS((VALUE)key, (VALUE)arg); - return ST_CONTINUE; -} - static int fstring_cmp(VALUE a, VALUE b) { long alen, blen; const char *aptr, *bptr; + + RUBY_ASSERT(RB_TYPE_P(a, T_STRING)); + RUBY_ASSERT(RB_TYPE_P(b, T_STRING)); + RSTRING_GETMEM(a, aptr, alen); RSTRING_GETMEM(b, bptr, blen); return (alen != blen || @@ -12636,8 +12944,12 @@ void Init_String(void) { rb_cString = rb_define_class("String", rb_cObject); - RUBY_ASSERT(rb_vm_fstring_table()); - st_foreach(rb_vm_fstring_table(), fstring_set_class_i, rb_cString); + struct fstring_table_struct *fstring_table = RTYPEDDATA_GET_DATA(fstring_table_obj); + for (int i = 0; i < fstring_table->capacity; i++) { + VALUE str = fstring_table->entries[i].str; + if (!str) continue; + RBASIC_SET_CLASS(str, rb_cString); + } rb_include_module(rb_cString, rb_mComparable); rb_define_alloc_func(rb_cString, empty_str_alloc); rb_define_singleton_method(rb_cString, "new", rb_str_s_new, -1); diff --git a/vm.c b/vm.c index eca8bbc895..dffb5fb5ad 100644 --- a/vm.c +++ b/vm.c @@ -3134,10 +3134,6 @@ ruby_vm_destruct(rb_vm_t *vm) st_free_table(vm->ci_table); vm->ci_table = NULL; } - if (vm->frozen_strings) { - st_free_table(vm->frozen_strings); - vm->frozen_strings = 0; - } RB_ALTSTACK_FREE(vm->main_altstack); struct global_object_list *next; @@ -3236,7 +3232,6 @@ vm_memsize(const void *ptr) rb_vm_memsize_workqueue(&vm->workqueue) + vm_memsize_at_exit_list(vm->at_exit) + rb_st_memsize(vm->ci_table) + - rb_st_memsize(vm->frozen_strings) + vm_memsize_builtin_function_table(vm->builtin_function_table) + rb_id_table_memsize(vm->negative_cme_table) + rb_st_memsize(vm->overloaded_cme_table) + @@ -4441,7 +4436,6 @@ Init_vm_objects(void) vm->mark_object_ary = pin_array_list_new(Qnil); vm->loading_table = st_init_strtable(); vm->ci_table = st_init_table(&vm_ci_hashtype); - vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000); } // Stub for builtin function when not building YJIT units @@ -4503,12 +4497,6 @@ VALUE rb_insn_operand_intern(const rb_iseq_t *iseq, VALUE insn, int op_no, VALUE op, int len, size_t pos, VALUE *pnop, VALUE child); -st_table * -rb_vm_fstring_table(void) -{ - return GET_VM()->frozen_strings; -} - #if VM_COLLECT_USAGE_DETAILS #define HASH_ASET(h, k, v) rb_hash_aset((h), (st_data_t)(k), (st_data_t)(v)) diff --git a/vm_core.h b/vm_core.h index 5c99320bd0..79e35cc82c 100644 --- a/vm_core.h +++ b/vm_core.h @@ -778,8 +778,6 @@ typedef struct rb_vm_struct { rb_at_exit_list *at_exit; - st_table *frozen_strings; - const struct rb_builtin_function *builtin_function_table; st_table *ci_table;