Skip to content

Commit 9ddfd2c

Browse files
jemmaissrofftenderloveeileencodesjhawthorn
committed
This commit implements the Object Shapes technique in CRuby.
Object Shapes is used for accessing instance variables and representing the "frozenness" of objects. Object instances have a "shape" and the shape represents some attributes of the object (currently which instance variables are set and the "frozenness"). Shapes form a tree data structure, and when a new instance variable is set on an object, that object "transitions" to a new shape in the shape tree. Each shape has an ID that is used for caching. The shape structure is independent of class, so objects of different types can have the same shape. For example: ```ruby class Foo def initialize # Starts with shape id 0 @A = 1 # transitions to shape id 1 @b = 1 # transitions to shape id 2 end end class Bar def initialize # Starts with shape id 0 @A = 1 # transitions to shape id 1 @b = 1 # transitions to shape id 2 end end foo = Foo.new # `foo` has shape id 2 bar = Bar.new # `bar` has shape id 2 ``` Both `foo` and `bar` instances have the same shape because they both set instance variables of the same name in the same order. This technique can help to improve inline cache hits as well as generate more efficient machine code in JIT compilers. This commit also adds some methods for debugging shapes on objects. See `RubyVM::Shape` for more details. For more context on Object Shapes, see [Feature: #18776] Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org> Co-Authored-By: Eileen M. Uchitelle <eileencodes@gmail.com> Co-Authored-By: John Hawthorn <john@hawthorn.email>
1 parent 2e88bca commit 9ddfd2c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2548
-946
lines changed

bootstraptest/test_attr.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,19 @@ class A
3434
print "ok"
3535
end
3636
}, '[ruby-core:15120]'
37+
38+
assert_equal %{ok}, %{
39+
class Big
40+
attr_reader :foo
41+
def initialize
42+
@foo = "ok"
43+
end
44+
end
45+
46+
obj = Big.new
47+
100.times do |i|
48+
obj.instance_variable_set(:"@ivar_\#{i}", i)
49+
end
50+
51+
Big.new.foo
52+
}

common.mk

Lines changed: 322 additions & 0 deletions
Large diffs are not rendered by default.

compile.c

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,20 +2058,7 @@ cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr)
20582058
static inline VALUE
20592059
get_ivar_ic_value(rb_iseq_t *iseq,ID id)
20602060
{
2061-
VALUE val;
2062-
struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table;
2063-
if (tbl) {
2064-
if (rb_id_table_lookup(tbl,id,&val)) {
2065-
return val;
2066-
}
2067-
}
2068-
else {
2069-
tbl = rb_id_table_create(1);
2070-
ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
2071-
}
2072-
val = INT2FIX(ISEQ_BODY(iseq)->ivc_size++);
2073-
rb_id_table_insert(tbl,id,val);
2074-
return val;
2061+
return INT2FIX(ISEQ_BODY(iseq)->ivc_size++);
20752062
}
20762063

20772064
static inline VALUE
@@ -2472,9 +2459,13 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
24722459
generated_iseq[code_index + 1 + j] = (VALUE)ic;
24732460
}
24742461
break;
2462+
case TS_IVC: /* inline ivar cache */
2463+
{
2464+
unsigned int ic_index = FIX2UINT(operands[j]);
2465+
vm_ic_attr_index_initialize(((IVC)&body->is_entries[ic_index]), INVALID_SHAPE_ID);
2466+
}
24752467
case TS_ISE: /* inline storage entry: `once` insn */
24762468
case TS_ICVARC: /* inline cvar cache */
2477-
case TS_IVC: /* inline ivar cache */
24782469
{
24792470
unsigned int ic_index = FIX2UINT(operands[j]);
24802471
IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache;
@@ -11514,6 +11505,11 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
1151411505

1151511506
ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op;
1151611507
code[code_index] = (VALUE)ic;
11508+
11509+
if (operand_type == TS_IVC) {
11510+
vm_ic_attr_index_initialize(((IVC)code[code_index]), INVALID_SHAPE_ID);
11511+
}
11512+
1151711513
}
1151811514
break;
1151911515
case TS_CALLDATA:

debug_counter.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ RB_DEBUG_COUNTER(frame_C2R)
130130
/* instance variable counts
131131
*
132132
* * ivar_get_ic_hit/miss: ivar_get inline cache (ic) hit/miss counts (VM insn)
133-
* * ivar_get_ic_miss_serial: ivar_get ic miss reason by serial (VM insn)
134133
* * ivar_get_ic_miss_unset: ... by unset (VM insn)
135134
* * ivar_get_ic_miss_noobject: ... by "not T_OBJECT" (VM insn)
136135
* * ivar_set_...: same counts with ivar_set (VM insn)
@@ -140,17 +139,17 @@ RB_DEBUG_COUNTER(frame_C2R)
140139
*/
141140
RB_DEBUG_COUNTER(ivar_get_ic_hit)
142141
RB_DEBUG_COUNTER(ivar_get_ic_miss)
143-
RB_DEBUG_COUNTER(ivar_get_ic_miss_serial)
144-
RB_DEBUG_COUNTER(ivar_get_ic_miss_unset)
145142
RB_DEBUG_COUNTER(ivar_get_ic_miss_noobject)
146143
RB_DEBUG_COUNTER(ivar_set_ic_hit)
147144
RB_DEBUG_COUNTER(ivar_set_ic_miss)
148-
RB_DEBUG_COUNTER(ivar_set_ic_miss_serial)
149-
RB_DEBUG_COUNTER(ivar_set_ic_miss_unset)
150145
RB_DEBUG_COUNTER(ivar_set_ic_miss_iv_hit)
151146
RB_DEBUG_COUNTER(ivar_set_ic_miss_noobject)
152147
RB_DEBUG_COUNTER(ivar_get_base)
153148
RB_DEBUG_COUNTER(ivar_set_base)
149+
RB_DEBUG_COUNTER(ivar_get_ic_miss_set)
150+
RB_DEBUG_COUNTER(ivar_get_cc_miss_set)
151+
RB_DEBUG_COUNTER(ivar_get_ic_miss_unset)
152+
RB_DEBUG_COUNTER(ivar_get_cc_miss_unset)
154153

155154
/* local variable counts
156155
*
@@ -321,6 +320,7 @@ RB_DEBUG_COUNTER(obj_imemo_parser_strterm)
321320
RB_DEBUG_COUNTER(obj_imemo_callinfo)
322321
RB_DEBUG_COUNTER(obj_imemo_callcache)
323322
RB_DEBUG_COUNTER(obj_imemo_constcache)
323+
RB_DEBUG_COUNTER(obj_imemo_shape)
324324

325325
/* ar_table */
326326
RB_DEBUG_COUNTER(artable_hint_hit)

ext/coverage/depend

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ coverage.o: $(top_srcdir)/ccan/check_type/check_type.h
165165
coverage.o: $(top_srcdir)/ccan/container_of/container_of.h
166166
coverage.o: $(top_srcdir)/ccan/list/list.h
167167
coverage.o: $(top_srcdir)/ccan/str/str.h
168+
coverage.o: $(top_srcdir)/constant.h
168169
coverage.o: $(top_srcdir)/gc.h
170+
coverage.o: $(top_srcdir)/id_table.h
169171
coverage.o: $(top_srcdir)/internal.h
170172
coverage.o: $(top_srcdir)/internal/array.h
171173
coverage.o: $(top_srcdir)/internal/compilers.h
@@ -176,12 +178,14 @@ coverage.o: $(top_srcdir)/internal/sanitizers.h
176178
coverage.o: $(top_srcdir)/internal/serial.h
177179
coverage.o: $(top_srcdir)/internal/static_assert.h
178180
coverage.o: $(top_srcdir)/internal/thread.h
181+
coverage.o: $(top_srcdir)/internal/variable.h
179182
coverage.o: $(top_srcdir)/internal/vm.h
180183
coverage.o: $(top_srcdir)/internal/warnings.h
181184
coverage.o: $(top_srcdir)/method.h
182185
coverage.o: $(top_srcdir)/node.h
183186
coverage.o: $(top_srcdir)/ruby_assert.h
184187
coverage.o: $(top_srcdir)/ruby_atomic.h
188+
coverage.o: $(top_srcdir)/shape.h
185189
coverage.o: $(top_srcdir)/thread_pthread.h
186190
coverage.o: $(top_srcdir)/vm_core.h
187191
coverage.o: $(top_srcdir)/vm_opts.h

ext/objspace/depend

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ objspace.o: $(top_srcdir)/internal/serial.h
350350
objspace.o: $(top_srcdir)/internal/static_assert.h
351351
objspace.o: $(top_srcdir)/internal/warnings.h
352352
objspace.o: $(top_srcdir)/node.h
353+
objspace.o: $(top_srcdir)/shape.h
353354
objspace.o: $(top_srcdir)/symbol.h
354355
objspace.o: objspace.c
355356
objspace.o: {$(VPATH)}id.h
@@ -533,7 +534,9 @@ objspace_dump.o: $(top_srcdir)/ccan/check_type/check_type.h
533534
objspace_dump.o: $(top_srcdir)/ccan/container_of/container_of.h
534535
objspace_dump.o: $(top_srcdir)/ccan/list/list.h
535536
objspace_dump.o: $(top_srcdir)/ccan/str/str.h
537+
objspace_dump.o: $(top_srcdir)/constant.h
536538
objspace_dump.o: $(top_srcdir)/gc.h
539+
objspace_dump.o: $(top_srcdir)/id_table.h
537540
objspace_dump.o: $(top_srcdir)/internal.h
538541
objspace_dump.o: $(top_srcdir)/internal/array.h
539542
objspace_dump.o: $(top_srcdir)/internal/compilers.h
@@ -544,12 +547,14 @@ objspace_dump.o: $(top_srcdir)/internal/sanitizers.h
544547
objspace_dump.o: $(top_srcdir)/internal/serial.h
545548
objspace_dump.o: $(top_srcdir)/internal/static_assert.h
546549
objspace_dump.o: $(top_srcdir)/internal/string.h
550+
objspace_dump.o: $(top_srcdir)/internal/variable.h
547551
objspace_dump.o: $(top_srcdir)/internal/vm.h
548552
objspace_dump.o: $(top_srcdir)/internal/warnings.h
549553
objspace_dump.o: $(top_srcdir)/method.h
550554
objspace_dump.o: $(top_srcdir)/node.h
551555
objspace_dump.o: $(top_srcdir)/ruby_assert.h
552556
objspace_dump.o: $(top_srcdir)/ruby_atomic.h
557+
objspace_dump.o: $(top_srcdir)/shape.h
553558
objspace_dump.o: $(top_srcdir)/thread_pthread.h
554559
objspace_dump.o: $(top_srcdir)/vm_core.h
555560
objspace_dump.o: $(top_srcdir)/vm_opts.h

ext/objspace/objspace.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self)
644644
INIT_IMEMO_TYPE_ID(imemo_callinfo);
645645
INIT_IMEMO_TYPE_ID(imemo_callcache);
646646
INIT_IMEMO_TYPE_ID(imemo_constcache);
647+
INIT_IMEMO_TYPE_ID(imemo_shape);
647648
#undef INIT_IMEMO_TYPE_ID
648649
}
649650

0 commit comments

Comments
 (0)