From 7798c73824cf2e8a3b76e3e64e8279e92f6b9000 Mon Sep 17 00:00:00 2001 From: Maple Ong Date: Thu, 10 Mar 2022 15:16:51 -0500 Subject: [PATCH 1/3] Check for leaf functions before inlining --- compile.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compile.c b/compile.c index 9399b885def351..33745bc31d1502 100644 --- a/compile.c +++ b/compile.c @@ -13038,7 +13038,10 @@ static bool inlineable_call(CALL_DATA cd) { unsigned int flags = vm_ci_flag(cd->ci); - if (flags & VM_CALL_ARGS_SIMPLE) { + const struct rb_callable_method_entry_struct * cme = vm_cc_cme(cd->cc); + + // Only inline simple and leaf functions + if (flags & VM_CALL_ARGS_SIMPLE && cme->def->body.iseq.iseqptr->body->builtin_inline_p) { return true; } else { From 1cac577a4b92b6126a9040764c948efa0b1e0fe1 Mon Sep 17 00:00:00 2001 From: Maple Ong Date: Fri, 11 Mar 2022 10:24:50 -0500 Subject: [PATCH 2/3] Add new func to check if callee contains method calls --- compile.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/compile.c b/compile.c index 33745bc31d1502..09b7ce243eee29 100644 --- a/compile.c +++ b/compile.c @@ -13034,6 +13034,27 @@ rb_iseq_ibf_load_extra_data(VALUE str) rb_iseq_t * iseq_alloc_for_inlining(const rb_iseq_t *original_iseq); bool rb_simple_iseq_p(const rb_iseq_t *iseq); +static bool +contains_method_calls(VALUE *code, size_t size, rb_vm_insns_translator_t * translator) +{ + int n = 0; + + // Scan the instructions looking for method calls + for (n = 0; n < size;) { + int insn_id = (int)translator((void *)code[n]); + int len = insn_len(insn_id); + + // Any time we find a method call + if (insn_id == BIN(opt_send_without_block)) { + return true; + } + + n += len; + } + + return false; +} + static bool inlineable_call(CALL_DATA cd) { From a303c420b123b77f5b1ad51f47248fd792dbe26c Mon Sep 17 00:00:00 2001 From: Maple Ong Date: Fri, 11 Mar 2022 10:30:21 -0500 Subject: [PATCH 3/3] Tighten definition of an inlinable call Now an inlineable call is a callee that either has the builtin inline flag set, or a callee that doesn't contain any method calls. We also need to check that the `cme` exists because sometimes it's null (and we don't know why yet). --- compile.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/compile.c b/compile.c index 09b7ce243eee29..6f2b812a1e49ba 100644 --- a/compile.c +++ b/compile.c @@ -13056,18 +13056,28 @@ contains_method_calls(VALUE *code, size_t size, rb_vm_insns_translator_t * trans } static bool -inlineable_call(CALL_DATA cd) +inlineable_call(CALL_DATA cd, rb_vm_insns_translator_t * translator) { unsigned int flags = vm_ci_flag(cd->ci); const struct rb_callable_method_entry_struct * cme = vm_cc_cme(cd->cc); - // Only inline simple and leaf functions - if (flags & VM_CALL_ARGS_SIMPLE && cme->def->body.iseq.iseqptr->body->builtin_inline_p) { - return true; - } - else { - return false; + if (cme) { + const rb_iseq_t * callee_iseq = cme->def->body.iseq.iseqptr; + VALUE * callee_iseq_code = callee_iseq->body->iseq_encoded; + size_t callee_iseq_size = callee_iseq->body->iseq_size; + bool contain_method_call = contains_method_calls(callee_iseq_code, callee_iseq_size, translator); + + // A leaf method is a method with the builtin inline flag set + // or a method that doesn't contain a method call + bool is_leaf_method = callee_iseq->body->builtin_inline_p || !contain_method_call; + + // Only inline simple and leaf methods + if (flags & VM_CALL_ARGS_SIMPLE && is_leaf_method) { + return true; + } } + + return false; } static int @@ -13094,7 +13104,7 @@ inline_iseqs(VALUE *code, size_t pos, iseq_value_itr_t * func, void *_ctx, rb_vm if (ctx->depth == 0 && insn_id == BIN(opt_send_without_block)) { CALL_DATA cd = (CALL_DATA)code[pos + 1]; - if (inlineable_call(cd)) { + if (inlineable_call(cd, translator)) { const struct rb_callable_method_entry_struct * cme = vm_cc_cme(cd->cc); // Convert the method call in to a linked list of the instructions @@ -13259,7 +13269,7 @@ find_max_local_table(VALUE *code, size_t pos, iseq_value_itr_t * func, void *_ct if (insn_id == BIN(opt_send_without_block)) { CALL_DATA cd = (CALL_DATA)code[pos + 1]; - if (inlineable_call(cd)) { + if (inlineable_call(cd, translator)) { const struct rb_callable_method_entry_struct * cme = vm_cc_cme(cd->cc); // Convert the method call in to a linked list of the instructions