summaryrefslogtreecommitdiff
path: root/iseq.c
diff options
context:
space:
mode:
authortenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-03-09 20:11:45 (GMT)
committertenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-03-09 20:11:45 (GMT)
commit89529649768798d495f97e5439e18864a0259a31 (patch)
treeba13d7442ca20fdb723d5a4bd722692d2bfdfaa6 /iseq.c
parent2dafe8df55c7eb7412ffe760dd7f2b4bafcb1da8 (diff)
Add direct marking on iseq operands
Directly marking iseq operands allows us to eliminate the "mark array" stored on ISEQ objects, which will reduce the amount of memory ISEQ objects consume. This patch changes the iseq mark function to: * Directly marks ISEQ operands * Iterate over and mark child ISEQs It also introduces two flags on the ISEQ object. In order to mark instruction operands, we have to disassemble the instructions and find the instruction parameters and types. Instructions may also be translated to jump addresses. Instruction sequences may get marked by the GC *while* they're mid flight (being compiled). The `ISEQ_TRANSLATED` flag is used to indicate whether or not the instructions have been translated to jump addresses so that when we decode the instructions we know whether or not we need to go from jump location back to original instruction or not. Not all ISEQ objects have any markable objects embedded in their instructions. We can detect whether or not an ISEQ has markable objects in the instructions at compile time. If the instructions contain markable objects, we set a flag `ISEQ_MARKABLE_ISEQ` on the ISEQ object. This means that during the mark phase, we can skip decompilation if the flag is *not* set. In other words, we can avoid decompilation of we know in advance there is nothing to mark. `once` instructions have an operand that contains the result of a one-time compilation of a regex. Before this patch, that operand was called an "inline cache", even though the struct was actually an "inline storage". This patch changes the operand to be an "inline storage" so that we can differentiate between caches that need marking (the inline storage) and caches that don't need marking (inline cache). [ruby-core:84909] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62706 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'iseq.c')
-rw-r--r--iseq.c127
1 files changed, 118 insertions, 9 deletions
diff --git a/iseq.c b/iseq.c
index 27bf45f..9253fd1 100644
--- a/iseq.c
+++ b/iseq.c
@@ -115,6 +115,100 @@ rb_iseq_free(const rb_iseq_t *iseq)
RUBY_FREE_LEAVE("iseq");
}
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+static int
+rb_vm_insn_addr2insn2(const void *addr)
+{
+ int insn;
+ const void * const *table = rb_vm_get_insns_address_table();
+
+ for (insn = 0; insn < VM_INSTRUCTION_SIZE; insn++) {
+ if (table[insn] == addr) {
+ return insn;
+ }
+ }
+ rb_bug("rb_vm_insn_addr2insn: invalid insn address: %p", addr);
+}
+#endif
+
+static int
+rb_vm_insn_null_translator(const void *addr)
+{
+ return (int)addr;
+}
+
+typedef void iseq_value_itr_t(void *ctx, VALUE obj);
+typedef int rb_vm_insns_translator_t(const void *addr);
+
+static int
+iseq_extract_values(const VALUE *code, size_t pos, iseq_value_itr_t * func, void *data, rb_vm_insns_translator_t * translator)
+{
+ VALUE insn = translator((void *)code[pos]);
+ int len = insn_len(insn);
+ int op_no;
+ const char *types = insn_op_types(insn);
+
+ for (op_no = 0; types[op_no]; op_no++) {
+ char type = types[op_no];
+ switch (type) {
+ case TS_CDHASH:
+ case TS_ISEQ:
+ case TS_VALUE:
+ {
+ VALUE op = code[pos + op_no + 1];
+ if (!SPECIAL_CONST_P(op)) {
+ func(data, op);
+ }
+ break;
+ }
+ case TS_ISE:
+ {
+ union iseq_inline_storage_entry *const is = (union iseq_inline_storage_entry *)code[pos + op_no + 1];
+ if (is->once.value) {
+ func(data, is->once.value);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return len;
+}
+
+static void
+rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data)
+{
+ unsigned int size;
+ const VALUE *code;
+ size_t n;
+ rb_vm_insns_translator_t * translator;
+
+ size = iseq->body->iseq_size;
+ code = iseq->body->iseq_encoded;
+
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+ if (FL_TEST(iseq, ISEQ_TRANSLATED)) {
+ translator = rb_vm_insn_addr2insn2;
+ } else {
+ translator = rb_vm_insn_null_translator;
+ }
+#else
+ translator = rb_vm_insn_null_translator;
+#endif
+
+ for (n = 0; n < size;) {
+ n += iseq_extract_values(code, n, func, data, translator);
+ }
+}
+
+static void
+each_insn_value(void *ctx, VALUE obj)
+{
+ rb_gc_mark(obj);
+}
+
void
rb_iseq_mark(const rb_iseq_t *iseq)
{
@@ -123,13 +217,31 @@ rb_iseq_mark(const rb_iseq_t *iseq)
if (iseq->body) {
const struct rb_iseq_constant_body *body = iseq->body;
- RUBY_MARK_UNLESS_NULL(body->mark_ary);
+ if(FL_TEST(iseq, ISEQ_MARKABLE_ISEQ)) {
+ rb_iseq_each_value(iseq, each_insn_value, NULL);
+ }
+
+ rb_gc_mark(body->variable.coverage);
+ rb_gc_mark(body->variable.original_iseq);
rb_gc_mark(body->location.label);
rb_gc_mark(body->location.base_label);
rb_gc_mark(body->location.pathobj);
RUBY_MARK_UNLESS_NULL((VALUE)body->parent_iseq);
+
+ if (body->catch_table) {
+ const struct iseq_catch_table *table = body->catch_table;
+ unsigned int i;
+ for(i = 0; i < table->size; i++) {
+ const struct iseq_catch_table_entry *entry;
+ entry = &table->entries[i];
+ if (entry->iseq) {
+ rb_gc_mark((VALUE)entry->iseq);
+ }
+ }
+ }
}
+
if (FL_TEST(iseq, ISEQ_NOT_LOADED_YET)) {
rb_gc_mark(iseq->aux.loader.obj);
}
@@ -299,13 +411,6 @@ set_relation(rb_iseq_t *iseq, const rb_iseq_t *piseq)
}
}
-void
-rb_iseq_add_mark_object(const rb_iseq_t *iseq, VALUE obj)
-{
- /* TODO: check dedup */
- rb_ary_push(ISEQ_MARK_ARY(iseq), obj);
-}
-
static VALUE
prepare_iseq_build(rb_iseq_t *iseq,
VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location,
@@ -326,7 +431,9 @@ prepare_iseq_build(rb_iseq_t *iseq,
if (iseq != iseq->body->local_iseq) {
RB_OBJ_WRITE(iseq, &iseq->body->location.base_label, iseq->body->local_iseq->body->location.label);
}
- RB_OBJ_WRITE(iseq, &iseq->body->mark_ary, iseq_mark_ary_create(0));
+ ISEQ_COVERAGE_SET(iseq, Qnil);
+ ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
+ iseq->body->variable.flip_count = 0;
ISEQ_COMPILE_DATA_ALLOC(iseq);
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err_info);
@@ -1612,6 +1719,7 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
break;
case TS_IC:
+ case TS_ISE:
ret = rb_sprintf("<is:%"PRIdPTRDIFF">", (union iseq_inline_storage_entry *)op - iseq->body->is_entries);
break;
@@ -2356,6 +2464,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
}
break;
case TS_IC:
+ case TS_ISE:
{
union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)*seq;
rb_ary_push(ary, INT2FIX(is - iseq->body->is_entries));