Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 11aa74d

Browse files
committed
optimize Struct getter/setter
Introduce new optimized method type `OPTIMIZED_METHOD_TYPE_STRUCT_AREF/ASET` with index information.
1 parent 398f7f4 commit 11aa74d

File tree

10 files changed

+181
-238
lines changed

10 files changed

+181
-238
lines changed

common.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16065,6 +16065,7 @@ vm.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
1606516065
vm.$(OBJEXT): $(top_srcdir)/internal/serial.h
1606616066
vm.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
1606716067
vm.$(OBJEXT): $(top_srcdir)/internal/string.h
16068+
vm.$(OBJEXT): $(top_srcdir)/internal/struct.h
1606816069
vm.$(OBJEXT): $(top_srcdir)/internal/symbol.h
1606916070
vm.$(OBJEXT): $(top_srcdir)/internal/thread.h
1607016071
vm.$(OBJEXT): $(top_srcdir)/internal/variable.h

compile.c

Lines changed: 0 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -10591,101 +10591,6 @@ rb_local_defined(ID id, const rb_iseq_t *iseq)
1059110591
return 0;
1059210592
}
1059310593

10594-
static int
10595-
caller_location(VALUE *path, VALUE *realpath)
10596-
{
10597-
const rb_execution_context_t *ec = GET_EC();
10598-
const rb_control_frame_t *const cfp =
10599-
rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
10600-
10601-
if (cfp) {
10602-
int line = rb_vm_get_sourceline(cfp);
10603-
*path = rb_iseq_path(cfp->iseq);
10604-
*realpath = rb_iseq_realpath(cfp->iseq);
10605-
return line;
10606-
}
10607-
else {
10608-
*path = rb_fstring_lit("<compiled>");
10609-
*realpath = *path;
10610-
return 1;
10611-
}
10612-
}
10613-
10614-
typedef struct {
10615-
VALUE arg;
10616-
VALUE func;
10617-
int line;
10618-
} accessor_args;
10619-
10620-
static const rb_iseq_t *
10621-
method_for_self(VALUE name, VALUE arg, const struct rb_builtin_function *func,
10622-
void (*build)(rb_iseq_t *, LINK_ANCHOR *, const void *))
10623-
{
10624-
VALUE path, realpath;
10625-
accessor_args acc;
10626-
10627-
acc.arg = arg;
10628-
acc.func = (VALUE)func;
10629-
acc.line = caller_location(&path, &realpath);
10630-
struct rb_iseq_new_with_callback_callback_func *ifunc =
10631-
rb_iseq_new_with_callback_new_callback(build, &acc);
10632-
return rb_iseq_new_with_callback(ifunc,
10633-
rb_sym2str(name), path, realpath,
10634-
INT2FIX(acc.line), 0, ISEQ_TYPE_METHOD, 0);
10635-
}
10636-
10637-
static void
10638-
for_self_aref(rb_iseq_t *iseq, LINK_ANCHOR *ret, const void *a)
10639-
{
10640-
const accessor_args *const args = (void *)a;
10641-
const int line = args->line;
10642-
struct rb_iseq_constant_body *const body = iseq->body;
10643-
10644-
iseq_set_local_table(iseq, 0);
10645-
body->param.lead_num = 0;
10646-
body->param.size = 0;
10647-
10648-
NODE dummy_line_node = generate_dummy_line_node(line, -1);
10649-
ADD_INSN1(ret, &dummy_line_node, putobject, args->arg);
10650-
ADD_INSN1(ret, &dummy_line_node, invokebuiltin, args->func);
10651-
}
10652-
10653-
static void
10654-
for_self_aset(rb_iseq_t *iseq, LINK_ANCHOR *ret, const void *a)
10655-
{
10656-
const accessor_args *const args = (void *)a;
10657-
const int line = args->line;
10658-
struct rb_iseq_constant_body *const body = iseq->body;
10659-
static const ID vars[] = {1, idUScore};
10660-
10661-
iseq_set_local_table(iseq, vars);
10662-
body->param.lead_num = 1;
10663-
body->param.size = 1;
10664-
10665-
NODE dummy_line_node = generate_dummy_line_node(line, -1);
10666-
ADD_GETLOCAL(ret, &dummy_line_node, numberof(vars)-1, 0);
10667-
ADD_INSN1(ret, &dummy_line_node, putobject, args->arg);
10668-
ADD_INSN1(ret, &dummy_line_node, invokebuiltin, args->func);
10669-
}
10670-
10671-
/*
10672-
* func (index) -> (value)
10673-
*/
10674-
const rb_iseq_t *
10675-
rb_method_for_self_aref(VALUE name, VALUE arg, const struct rb_builtin_function *func)
10676-
{
10677-
return method_for_self(name, arg, func, for_self_aref);
10678-
}
10679-
10680-
/*
10681-
* func (index, value) -> (value)
10682-
*/
10683-
const rb_iseq_t *
10684-
rb_method_for_self_aset(VALUE name, VALUE arg, const struct rb_builtin_function *func)
10685-
{
10686-
return method_for_self(name, arg, func, for_self_aset);
10687-
}
10688-
1068910594
/* ISeq binary format */
1069010595

1069110596
#ifndef IBF_ISEQ_DEBUG

debug_counter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ RB_DEBUG_COUNTER(ccf_bmethod)
9797
RB_DEBUG_COUNTER(ccf_opt_send)
9898
RB_DEBUG_COUNTER(ccf_opt_call)
9999
RB_DEBUG_COUNTER(ccf_opt_block_call)
100+
RB_DEBUG_COUNTER(ccf_opt_struct_aref)
101+
RB_DEBUG_COUNTER(ccf_opt_struct_aset)
100102
RB_DEBUG_COUNTER(ccf_super_method)
101103

102104
/*

method.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,14 @@ enum method_optimized_type {
167167
OPTIMIZED_METHOD_TYPE_SEND,
168168
OPTIMIZED_METHOD_TYPE_CALL,
169169
OPTIMIZED_METHOD_TYPE_BLOCK_CALL,
170+
OPTIMIZED_METHOD_TYPE_STRUCT_AREF,
171+
OPTIMIZED_METHOD_TYPE_STRUCT_ASET,
170172
OPTIMIZED_METHOD_TYPE__MAX
171173
};
172174

173175
typedef struct rb_method_optimized {
174176
enum method_optimized_type type;
177+
unsigned int index;
175178
} rb_method_optimized_t;
176179

177180
struct rb_method_definition_struct {

proc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,6 +2681,12 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max)
26812681
case OPTIMIZED_METHOD_TYPE_BLOCK_CALL:
26822682
*max = UNLIMITED_ARGUMENTS;
26832683
return 0;
2684+
case OPTIMIZED_METHOD_TYPE_STRUCT_AREF:
2685+
*max = 0;
2686+
return 0;
2687+
case OPTIMIZED_METHOD_TYPE_STRUCT_ASET:
2688+
*max = 1;
2689+
return 0;
26842690
default:
26852691
break;
26862692
}

struct.c

Lines changed: 5 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ enum {
2828
AREF_HASH_THRESHOLD = 10
2929
};
3030

31-
const rb_iseq_t *rb_method_for_self_aref(VALUE name, VALUE arg, const struct rb_builtin_function *func);
32-
const rb_iseq_t *rb_method_for_self_aset(VALUE name, VALUE arg, const struct rb_builtin_function *func);
33-
3431
VALUE rb_cStruct;
3532
static ID id_members, id_back_members, id_keyword_init;
3633

@@ -229,32 +226,6 @@ rb_struct_getmember(VALUE obj, ID id)
229226
UNREACHABLE_RETURN(Qnil);
230227
}
231228

232-
static VALUE rb_struct_ref0(VALUE obj) {return RSTRUCT_GET(obj, 0);}
233-
static VALUE rb_struct_ref1(VALUE obj) {return RSTRUCT_GET(obj, 1);}
234-
static VALUE rb_struct_ref2(VALUE obj) {return RSTRUCT_GET(obj, 2);}
235-
static VALUE rb_struct_ref3(VALUE obj) {return RSTRUCT_GET(obj, 3);}
236-
static VALUE rb_struct_ref4(VALUE obj) {return RSTRUCT_GET(obj, 4);}
237-
static VALUE rb_struct_ref5(VALUE obj) {return RSTRUCT_GET(obj, 5);}
238-
static VALUE rb_struct_ref6(VALUE obj) {return RSTRUCT_GET(obj, 6);}
239-
static VALUE rb_struct_ref7(VALUE obj) {return RSTRUCT_GET(obj, 7);}
240-
static VALUE rb_struct_ref8(VALUE obj) {return RSTRUCT_GET(obj, 8);}
241-
static VALUE rb_struct_ref9(VALUE obj) {return RSTRUCT_GET(obj, 9);}
242-
243-
#define N_REF_FUNC numberof(ref_func)
244-
245-
static VALUE (*const ref_func[])(VALUE) = {
246-
rb_struct_ref0,
247-
rb_struct_ref1,
248-
rb_struct_ref2,
249-
rb_struct_ref3,
250-
rb_struct_ref4,
251-
rb_struct_ref5,
252-
rb_struct_ref6,
253-
rb_struct_ref7,
254-
rb_struct_ref8,
255-
rb_struct_ref9,
256-
};
257-
258229
static void
259230
rb_struct_modify(VALUE s)
260231
{
@@ -300,42 +271,16 @@ struct_pos_num(VALUE s, VALUE idx)
300271
return i;
301272
}
302273

303-
static VALUE
304-
opt_struct_aref(rb_execution_context_t *ec, VALUE self, VALUE idx)
305-
{
306-
long i = struct_pos_num(self, idx);
307-
return RSTRUCT_GET(self, i);
308-
}
309-
310-
static VALUE
311-
opt_struct_aset(rb_execution_context_t *ec, VALUE self, VALUE val, VALUE idx)
312-
{
313-
long i = struct_pos_num(self, idx);
314-
rb_struct_modify(self);
315-
RSTRUCT_SET(self, i, val);
316-
return val;
317-
}
318-
319-
static const struct rb_builtin_function struct_aref_builtin =
320-
RB_BUILTIN_FUNCTION(0, struct_aref, opt_struct_aref, 1, 0);
321-
static const struct rb_builtin_function struct_aset_builtin =
322-
RB_BUILTIN_FUNCTION(1, struct_aref, opt_struct_aset, 2, 0);
323-
324274
static void
325275
define_aref_method(VALUE nstr, VALUE name, VALUE off)
326276
{
327-
const rb_iseq_t *iseq = rb_method_for_self_aref(name, off, &struct_aref_builtin);
328-
iseq->body->builtin_inline_p = true;
329-
330-
rb_add_method_iseq(nstr, SYM2ID(name), iseq, NULL, METHOD_VISI_PUBLIC);
277+
rb_add_method_optimized(nstr, SYM2ID(name), OPTIMIZED_METHOD_TYPE_STRUCT_AREF, FIX2UINT(off), METHOD_VISI_PUBLIC);
331278
}
332279

333280
static void
334281
define_aset_method(VALUE nstr, VALUE name, VALUE off)
335282
{
336-
const rb_iseq_t *iseq = rb_method_for_self_aset(name, off, &struct_aset_builtin);
337-
338-
rb_add_method_iseq(nstr, SYM2ID(name), iseq, NULL, METHOD_VISI_PUBLIC);
283+
rb_add_method_optimized(nstr, SYM2ID(name), OPTIMIZED_METHOD_TYPE_STRUCT_ASET, FIX2UINT(off), METHOD_VISI_PUBLIC);
339284
}
340285

341286
static VALUE
@@ -386,13 +331,8 @@ setup_struct(VALUE nstr, VALUE members)
386331
ID id = SYM2ID(sym);
387332
VALUE off = LONG2NUM(i);
388333

389-
if (i < N_REF_FUNC) {
390-
rb_define_method_id(nstr, id, ref_func[i], 0);
391-
}
392-
else {
393-
define_aref_method(nstr, sym, off);
394-
}
395-
define_aset_method(nstr, ID2SYM(rb_id_attrset(id)), off);
334+
define_aref_method(nstr, sym, off);
335+
define_aset_method(nstr, ID2SYM(rb_id_attrset(id)), off);
396336
}
397337

398338
return nstr;
@@ -844,7 +784,7 @@ rb_struct_alloc(VALUE klass, VALUE values)
844784
VALUE
845785
rb_struct_new(VALUE klass, ...)
846786
{
847-
VALUE tmpargs[N_REF_FUNC], *mem = tmpargs;
787+
VALUE tmpargs[16], *mem = tmpargs;
848788
int size, i;
849789
va_list args;
850790

test/ruby/test_yjit.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ def foo(foo: 1+1)
408408
end
409409

410410
def test_invokebuiltin
411+
skip "Struct's getter/setter doesn't use invokebuiltin and YJIT doesn't support new logic"
412+
411413
assert_compiles(<<~RUBY)
412414
def foo(obj)
413415
obj.foo = 123

vm_eval.c

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,19 @@ vm_call0_cfunc(rb_execution_context_t *ec, struct rb_calling_info *calling, cons
163163
return vm_call0_cfunc_with_frame(ec, calling, argv);
164164
}
165165

166+
static void
167+
vm_call_check_arity(struct rb_calling_info *calling, int argc, const VALUE *argv)
168+
{
169+
if (calling->kw_splat &&
170+
calling->argc > 0 &&
171+
RB_TYPE_P(argv[calling->argc-1], T_HASH) &&
172+
RHASH_EMPTY_P(argv[calling->argc-1])) {
173+
calling->argc--;
174+
}
175+
176+
rb_check_arity(calling->argc, argc, argc);
177+
}
178+
166179
/* `ci' should point temporal value (on stack value) */
167180
static VALUE
168181
vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv)
@@ -196,27 +209,13 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
196209
ret = vm_call0_cfunc(ec, calling, argv);
197210
goto success;
198211
case VM_METHOD_TYPE_ATTRSET:
199-
if (calling->kw_splat &&
200-
calling->argc > 0 &&
201-
RB_TYPE_P(argv[calling->argc-1], T_HASH) &&
202-
RHASH_EMPTY_P(argv[calling->argc-1])) {
203-
calling->argc--;
204-
}
205-
206-
rb_check_arity(calling->argc, 1, 1);
212+
vm_call_check_arity(calling, 1, argv);
207213
VM_CALL_METHOD_ATTR(ret,
208214
rb_ivar_set(calling->recv, vm_cc_cme(cc)->def->body.attr.id, argv[0]),
209215
(void)0);
210216
goto success;
211217
case VM_METHOD_TYPE_IVAR:
212-
if (calling->kw_splat &&
213-
calling->argc > 0 &&
214-
RB_TYPE_P(argv[calling->argc-1], T_HASH) &&
215-
RHASH_EMPTY_P(argv[calling->argc-1])) {
216-
calling->argc--;
217-
}
218-
219-
rb_check_arity(calling->argc, 0, 0);
218+
vm_call_check_arity(calling, 0, argv);
220219
VM_CALL_METHOD_ATTR(ret,
221220
rb_attr_get(calling->recv, vm_cc_cme(cc)->def->body.attr.id),
222221
(void)0);
@@ -274,6 +273,14 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
274273
ret = rb_vm_invoke_proc(ec, proc, calling->argc, argv, calling->kw_splat, calling->block_handler);
275274
goto success;
276275
}
276+
case OPTIMIZED_METHOD_TYPE_STRUCT_AREF:
277+
vm_call_check_arity(calling, 0, argv);
278+
ret = vm_call_opt_struct_aref0(ec, ec->cfp, calling);
279+
goto success;
280+
case OPTIMIZED_METHOD_TYPE_STRUCT_ASET:
281+
vm_call_check_arity(calling, 1, argv);
282+
ret = vm_call_opt_struct_aset0(ec, ec->cfp, calling);
283+
goto success;
277284
default:
278285
rb_bug("vm_call0: unsupported optimized method type (%d)", vm_cc_cme(cc)->def->body.optimized.type);
279286
}

0 commit comments

Comments
 (0)