Skip to content

Commit 556f462

Browse files
committedJun 10, 2024
Reimplement RData on top of RTypedData
The goal is to eliminate `RTypedData.typed_flag` which waste 8B, allowing `TypedData` to fit in a potential `32B` slot. This however makes `RData` 48B long, but given it's a deprecated construct it seems acceptable to degrade it to improve its replacement.
1 parent 448efa9 commit 556f462

File tree

4 files changed

+142
-98
lines changed

4 files changed

+142
-98
lines changed
 

‎gc.c

+80-49
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ typedef struct RVALUE {
661661
struct RArray array;
662662
struct RRegexp regexp;
663663
struct RHash hash;
664-
struct RData data;
664+
struct RDataHeader data;
665665
struct RTypedData typeddata;
666666
struct RStruct rstruct;
667667
struct RBignum bignum;
@@ -2973,35 +2973,19 @@ rb_wb_protected_newobj_of(rb_execution_context_t *ec, VALUE klass, VALUE flags,
29732973
static inline void
29742974
rb_data_object_check(VALUE klass)
29752975
{
2976-
if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
2977-
rb_undef_alloc_func(klass);
2978-
rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass);
2979-
}
2980-
}
2981-
2982-
VALUE
2983-
rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
2984-
{
2985-
RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
2986-
if (klass) rb_data_object_check(klass);
2987-
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap, !dmark, sizeof(struct RTypedData));
2988-
}
2989-
2990-
VALUE
2991-
rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
2992-
{
2993-
VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree);
2994-
DATA_PTR(obj) = xcalloc(1, size);
2995-
return obj;
2976+
if (klass != rb_cObject && (rb_get_alloc_func(klass) == rb_class_allocate_instance)) {
2977+
rb_undef_alloc_func(klass);
2978+
rb_warn("undefining the allocator of T_DATA class %"PRIsVALUE, klass);
2979+
}
29962980
}
29972981

29982982
static VALUE
2999-
typed_data_alloc(VALUE klass, VALUE typed_flag, void *datap, const rb_data_type_t *type, size_t size)
2983+
typed_data_alloc(VALUE klass, const rb_data_type_t *type, void *datap, size_t size)
30002984
{
30012985
RBIMPL_NONNULL_ARG(type);
30022986
if (klass) rb_data_object_check(klass);
30032987
bool wb_protected = (type->flags & RUBY_FL_WB_PROTECTED) || !type->function.dmark;
3004-
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, 1 | typed_flag, (VALUE)datap, wb_protected, size);
2988+
return newobj_of(GET_RACTOR(), klass, T_DATA, (VALUE)type, (VALUE)datap, 0, wb_protected, size);
30052989
}
30062990

30072991
VALUE
@@ -3011,7 +2995,7 @@ rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type)
30112995
rb_raise(rb_eTypeError, "Cannot wrap an embeddable TypedData");
30122996
}
30132997

3014-
return typed_data_alloc(klass, 0, datap, type, sizeof(struct RTypedData));
2998+
return typed_data_alloc(klass, type, datap, sizeof(struct RTypedData));
30152999
}
30163000

30173001
VALUE
@@ -3024,13 +3008,14 @@ rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type
30243008

30253009
size_t embed_size = offsetof(struct RTypedData, data) + size;
30263010
if (rb_gc_size_allocatable_p(embed_size)) {
3027-
VALUE obj = typed_data_alloc(klass, TYPED_DATA_EMBEDDED, 0, type, embed_size);
3011+
VALUE obj = typed_data_alloc(klass, type, NULL, embed_size);
30283012
memset((char *)obj + offsetof(struct RTypedData, data), 0, size);
3013+
FL_SET_RAW(obj, TYPED_DATA_FL_EMBEDDED);
30293014
return obj;
30303015
}
30313016
}
30323017

3033-
VALUE obj = typed_data_alloc(klass, 0, NULL, type, sizeof(struct RTypedData));
3018+
VALUE obj = typed_data_alloc(klass, type, NULL, sizeof(struct RTypedData));
30343019
DATA_PTR(obj) = xcalloc(1, size);
30353020
return obj;
30363021
}
@@ -3043,7 +3028,7 @@ rb_objspace_data_type_memsize(VALUE obj)
30433028
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
30443029
const void *ptr = RTYPEDDATA_GET_DATA(obj);
30453030

3046-
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) {
3031+
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !rbimpl_rtypeddata_embedded_p(obj)) {
30473032
#ifdef HAVE_MALLOC_USABLE_SIZE
30483033
size += malloc_usable_size((void *)ptr);
30493034
#endif
@@ -3068,6 +3053,56 @@ rb_objspace_data_type_name(VALUE obj)
30683053
}
30693054
}
30703055

3056+
static void
3057+
mark_deprecated_rdata_object(void *ptr)
3058+
{
3059+
struct RData *rdata = (struct RData *)ptr;
3060+
if (rdata->dmark) {
3061+
rdata->dmark(rdata);
3062+
}
3063+
}
3064+
3065+
static size_t
3066+
memsize_deprecated_rdata_object(const void *ptr)
3067+
{
3068+
return sizeof(struct RData);
3069+
}
3070+
3071+
#define DEPRECATED_DATA_FREE RBIMPL_DATA_FUNC(-3)
3072+
3073+
const rb_data_type_t deprecated_rdata_type = {
3074+
.wrap_struct_name = "RDATA(deprecated)",
3075+
.function = {
3076+
.dmark = mark_deprecated_rdata_object,
3077+
.dfree = DEPRECATED_DATA_FREE,
3078+
.dsize = memsize_deprecated_rdata_object,
3079+
},
3080+
};
3081+
3082+
VALUE
3083+
rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
3084+
{
3085+
RUBY_ASSERT_ALWAYS(dfree != (RUBY_DATA_FUNC)1);
3086+
if (klass) rb_data_object_check(klass);
3087+
3088+
VALUE obj = rb_data_typed_object_zalloc(klass, sizeof(struct RData), &deprecated_rdata_type);
3089+
3090+
struct RData *rdata = (struct RData *)obj;
3091+
rdata->dmark = dmark;
3092+
rdata->dfree = dfree;
3093+
rdata->data = datap;
3094+
return obj;
3095+
}
3096+
3097+
VALUE
3098+
rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
3099+
{
3100+
VALUE obj = rb_data_object_wrap(klass, 0, dmark, dfree);
3101+
struct RData *rdata = (struct RData *)obj;
3102+
rdata->data = xcalloc(1, size);
3103+
return obj;
3104+
}
3105+
30713106
static int
30723107
ptr_in_page_body_p(const void *ptr, const void *memb)
30733108
{
@@ -3190,31 +3225,29 @@ obj_free_object_id(rb_objspace_t *objspace, VALUE obj)
31903225
}
31913226

31923227
static bool
3193-
rb_data_free(rb_objspace_t *objspace, VALUE obj)
3228+
rb_typeddata_free(rb_objspace_t *objspace, VALUE obj)
31943229
{
3195-
void *data = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
3230+
void *data = RTYPEDDATA_GET_DATA(obj);
31963231
if (data) {
31973232
int free_immediately = false;
3198-
void (*dfree)(void *);
31993233

3200-
if (RTYPEDDATA_P(obj)) {
3201-
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
3202-
dfree = RANY(obj)->as.typeddata.type->function.dfree;
3203-
}
3204-
else {
3205-
dfree = RANY(obj)->as.data.dfree;
3234+
free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
3235+
3236+
RUBY_DATA_FUNC dfree = RANY(obj)->as.typeddata.type->function.dfree;
3237+
if (UNLIKELY(dfree == DEPRECATED_DATA_FREE)) {
3238+
dfree = RDATA(obj)->dfree;
32063239
}
32073240

32083241
if (dfree) {
32093242
if (dfree == RUBY_DEFAULT_FREE) {
3210-
if (!RTYPEDDATA_EMBEDDED_P(obj)) {
3243+
if (!rbimpl_rtypeddata_embedded_p(obj)) {
32113244
xfree(data);
32123245
RB_DEBUG_COUNTER_INC(obj_data_xfree);
32133246
}
32143247
}
32153248
else if (free_immediately) {
32163249
(*dfree)(data);
3217-
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !RTYPEDDATA_EMBEDDED_P(obj)) {
3250+
if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_EMBEDDABLE && !rbimpl_rtypeddata_embedded_p(obj)) {
32183251
xfree(data);
32193252
}
32203253

@@ -3372,7 +3405,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
33723405
}
33733406
break;
33743407
case T_DATA:
3375-
if (!rb_data_free(objspace, obj)) return false;
3408+
if (!rb_typeddata_free(objspace, obj)) return false;
33763409
break;
33773410
case T_MATCH:
33783411
{
@@ -4339,7 +4372,7 @@ rb_objspace_call_finalizer_i(VALUE obj, void *data)
43394372

43404373
switch (BUILTIN_TYPE(obj)) {
43414374
case T_DATA:
4342-
if (!rb_free_at_exit && (!DATA_PTR(obj) || !RANY(obj)->as.data.dfree)) break;
4375+
if (!rb_free_at_exit && (!DATA_PTR(obj) || !RDATA(obj)->dfree)) break;
43434376
if (rb_obj_is_thread(obj)) break;
43444377
if (rb_obj_is_mutex(obj)) break;
43454378
if (rb_obj_is_fiber(obj)) break;
@@ -6954,20 +6987,18 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
69546987

69556988
case T_DATA:
69566989
{
6957-
void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
6990+
void *const ptr = RTYPEDDATA_GET_DATA(obj);
69586991

69596992
if (ptr) {
6960-
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
6993+
if (gc_declarative_marking_p(any->as.typeddata.type)) {
69616994
size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
69626995

69636996
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
69646997
rb_gc_mark_movable(*(VALUE *)((char *)ptr + offset));
69656998
}
69666999
}
69677000
else {
6968-
RUBY_DATA_FUNC mark_func = RTYPEDDATA_P(obj) ?
6969-
any->as.typeddata.type->function.dmark :
6970-
any->as.data.dmark;
7001+
RUBY_DATA_FUNC mark_func = any->as.typeddata.type->function.dmark;
69717002
if (mark_func) (*mark_func)(ptr);
69727003
}
69737004
}
@@ -10170,9 +10201,9 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
1017010201
case T_DATA:
1017110202
/* Call the compaction callback, if it exists */
1017210203
{
10173-
void *const ptr = RTYPEDDATA_P(obj) ? RTYPEDDATA_GET_DATA(obj) : DATA_PTR(obj);
10204+
void *const ptr = RTYPEDDATA_GET_DATA(obj);
1017410205
if (ptr) {
10175-
if (RTYPEDDATA_P(obj) && gc_declarative_marking_p(any->as.typeddata.type)) {
10206+
if (gc_declarative_marking_p(any->as.typeddata.type)) {
1017610207
size_t *offset_list = (size_t *)RANY(obj)->as.typeddata.type->function.dmark;
1017710208

1017810209
for (size_t offset = *offset_list; offset != RUBY_REF_END; offset = *offset_list++) {
@@ -10181,7 +10212,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
1018110212
*ref = rb_gc_location(*ref);
1018210213
}
1018310214
}
10184-
else if (RTYPEDDATA_P(obj)) {
10215+
else {
1018510216
RUBY_DATA_FUNC compact_func = any->as.typeddata.type->function.dcompact;
1018610217
if (compact_func) (*compact_func)(ptr);
1018710218
}
@@ -13306,7 +13337,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU
1330613337
rb_raw_iseq_info(BUFF_ARGS, iseq);
1330713338
}
1330813339
else if (rb_ractor_p(obj)) {
13309-
rb_ractor_t *r = (void *)DATA_PTR(obj);
13340+
rb_ractor_t *r = (void *)RTYPEDDATA_GET_DATA(obj);
1331013341
if (r) {
1331113342
APPEND_F("r:%d", r->pub.id);
1331213343
}

‎include/ruby/internal/core/rdata.h

+17-13
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "ruby/internal/attr/warning.h"
3131
#include "ruby/internal/cast.h"
3232
#include "ruby/internal/core/rbasic.h"
33+
#include "ruby/internal/core/rtypeddata.h"
3334
#include "ruby/internal/dllexport.h"
3435
#include "ruby/internal/fl_type.h"
3536
#include "ruby/internal/value.h"
@@ -77,6 +78,7 @@
7778
*/
7879
#define RUBY_DEFAULT_FREE RBIMPL_DATA_FUNC(-1)
7980

81+
8082
/**
8183
* This is a value you can set to ::RData::dfree. Setting this means the data
8284
* is managed by someone else, like, statically allocated. Of course you are
@@ -93,16 +95,6 @@
9395
*/
9496
#define RUBY_UNTYPED_DATA_FUNC(f) f RBIMPL_ATTRSET_UNTYPED_DATA_FUNC()
9597

96-
/*
97-
#define RUBY_DATA_FUNC(func) ((void (*)(void*))(func))
98-
*/
99-
100-
/**
101-
* This is the type of callbacks registered to ::RData. The argument is the
102-
* `data` field.
103-
*/
104-
typedef void (*RUBY_DATA_FUNC)(void*);
105-
10698
/**
10799
* @deprecated
108100
*
@@ -117,11 +109,26 @@ typedef void (*RUBY_DATA_FUNC)(void*);
117109
* too many warnings in the core. Maybe we want to retry later... Just add
118110
* deprecated document for now.
119111
*/
112+
struct RDataHeader {
113+
/** Basic part, including flags and class. */
114+
struct RBasic basic;
115+
116+
const rb_data_type_t *const type;
117+
118+
/** Pointer to the actual C level struct that you want to wrap. */
119+
void *data;
120+
};
121+
120122
struct RData {
121123

122124
/** Basic part, including flags and class. */
123125
struct RBasic basic;
124126

127+
const rb_data_type_t *const type;
128+
129+
/** Pointer to the actual C level struct that you want to wrap. */
130+
void *data;
131+
125132
/**
126133
* This function is called when the object is experiencing GC marks. If it
127134
* contains references to other Ruby objects, you need to mark them also.
@@ -141,9 +148,6 @@ struct RData {
141148
* impossible at that moment (that is why GC runs).
142149
*/
143150
RUBY_DATA_FUNC dfree;
144-
145-
/** Pointer to the actual C level struct that you want to wrap. */
146-
void *data;
147151
};
148152

149153
RBIMPL_SYMBOL_EXPORT_BEGIN()

0 commit comments

Comments
 (0)
Failed to load comments.