ruby-changes:61872
From: Takashi <ko1@a...>
Date: Sun, 21 Jun 2020 09:13:19 +0900 (JST)
Subject: [ruby-changes:61872] 7561db8c00 (master): Introduce Primitive.attr! to annotate 'inline' (#3242)
https://git.ruby-lang.org/ruby.git/commit/?id=7561db8c00 From 7561db8c009bb79a75024fa4ed0350bfb3d0626c Mon Sep 17 00:00:00 2001 From: Takashi Kokubun <takashikkbn@g...> Date: Sat, 20 Jun 2020 17:13:03 -0700 Subject: Introduce Primitive.attr! to annotate 'inline' (#3242) [Feature #15589] diff --git a/benchmark/mjit_int_zero_p.yml b/benchmark/mjit_int_zero_p.yml new file mode 100644 index 0000000..91e8ea0 --- /dev/null +++ b/benchmark/mjit_int_zero_p.yml @@ -0,0 +1,36 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/mjit_int_zero_p.yml#L1 +prelude: | + def mjit_zero?(int) + int.zero? + end + + def mjit_eq_0(int) + int == 0 + end + + def warmup(sym, int) + if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? + jit_min_calls = 10000 + i = 0 + while i < jit_min_calls + send(sym, int) + i += 1 + end + RubyVM::MJIT.pause + end + end + +benchmark: + - name: 0.zero? + prelude: warmup(:mjit_zero?, 0) + script: mjit_zero?(0) + - name: 1.zero? + prelude: warmup(:mjit_zero?, 1) + script: mjit_zero?(1) + - name: 0 == 0 + prelude: warmup(:mjit_eq_0, 0) + script: mjit_eq_0(0) + - name: 1 == 0 + prelude: warmup(:mjit_eq_0, 1) + script: mjit_eq_0(1) + +loop_count: 40000000 diff --git a/compile.c b/compile.c index 972a299..77af316 100644 --- a/compile.c +++ b/compile.c @@ -7274,6 +7274,11 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co https://github.com/ruby/ruby/blob/trunk/compile.c#L7274 GET_VM()->builtin_inline_index++; return COMPILE_OK; } + else if (strcmp("attr!", builtin_func) == 0) { + // There's only "inline" attribute for now + iseq->body->builtin_inline_p = true; + return COMPILE_OK; + } if (1) { rb_bug("can't find builtin function:%s", builtin_func); @@ -10815,6 +10820,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/compile.c#L10820 ibf_dump_write_small_value(dump, body->ci_size); ibf_dump_write_small_value(dump, body->stack_max); ibf_dump_write_small_value(dump, body->catch_except_p); + ibf_dump_write_small_value(dump, body->builtin_inline_p); #undef IBF_BODY_OFFSET @@ -10920,6 +10926,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) https://github.com/ruby/ruby/blob/trunk/compile.c#L10926 const unsigned int ci_size = (unsigned int)ibf_load_small_value(load, &reading_pos); const unsigned int stack_max = (unsigned int)ibf_load_small_value(load, &reading_pos); const char catch_except_p = (char)ibf_load_small_value(load, &reading_pos); + const bool builtin_inline_p = (bool)ibf_load_small_value(load, &reading_pos); #undef IBF_BODY_OFFSET @@ -10958,6 +10965,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) https://github.com/ruby/ruby/blob/trunk/compile.c#L10965 load_body->location.code_location.end_pos.lineno = location_code_location_end_pos_lineno; load_body->location.code_location.end_pos.column = location_code_location_end_pos_column; load_body->catch_except_p = catch_except_p; + load_body->builtin_inline_p = builtin_inline_p; load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, is_size); ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data); diff --git a/integer.rb b/integer.rb index b1e288d..cc2e589 100644 --- a/integer.rb +++ b/integer.rb @@ -4,6 +4,7 @@ class Integer https://github.com/ruby/ruby/blob/trunk/integer.rb#L4 # # Returns +true+ if +num+ has a zero value. def zero? + Primitive.attr! 'inline' Primitive.cexpr! 'int_zero_p(self)' end end diff --git a/mjit_compile.c b/mjit_compile.c index fe5c241..c4c31aa 100644 --- a/mjit_compile.c +++ b/mjit_compile.c @@ -374,7 +374,12 @@ inlinable_iseq_p(const struct rb_iseq_constant_body *body) https://github.com/ruby/ruby/blob/trunk/mjit_compile.c#L374 // * Do not require `cfp->sp` motion // * Do not move `cfp->pc` // * Do not read any `cfp->pc` - if (insn != BIN(leave) && insn_may_depend_on_sp_or_pc(insn, body->iseq_encoded + (pos + 1))) + if (insn == BIN(invokebuiltin) || insn == BIN(opt_invokebuiltin_delegate) || insn == BIN(opt_invokebuiltin_delegate_leave)) { + // builtin insn's inlinability is handled by `Primitive.attr! 'inline'` per iseq + if (!body->builtin_inline_p) + return false; + } + else if (insn != BIN(leave) && insn_may_depend_on_sp_or_pc(insn, body->iseq_encoded + (pos + 1))) return false; // At this moment, `cfp->ep` in an inlined method is not working. switch (insn) { diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 0155159..9d4b640 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -135,6 +135,12 @@ def collect_builtin base, tree, name, bs, inlines, params = nil https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L135 if /(.+)\!\z/ =~ func_name case $1 + when 'attr' + text = inline_text(argc, args.first) + if text != 'inline' + raise "Only 'inline' is allowed to be annotated (but got: '#{text}')" + end + break when 'cstmt' text = inline_text argc, args.first diff --git a/tool/ruby_vm/views/mjit_compile.inc.erb b/tool/ruby_vm/views/mjit_compile.inc.erb index 0d3678d..fa2e52e 100644 --- a/tool/ruby_vm/views/mjit_compile.inc.erb +++ b/tool/ruby_vm/views/mjit_compile.inc.erb @@ -63,12 +63,26 @@ switch (insn) { https://github.com/ruby/ruby/blob/trunk/tool/ruby_vm/views/mjit_compile.inc.erb#L63 } % when 'getinstancevariable', 'setinstancevariable' <%= render 'mjit_compile_ivar', locals: { insn: insn } -%> -% when 'leave' +% when 'leave', 'opt_invokebuiltin_delegate_leave' + { +% # opt_invokebuiltin_delegate_leave also implements leave insn. We need to handle it here for inlining. +% if insn.name == 'opt_invokebuiltin_delegate_leave' + RB_BUILTIN bf = (RB_BUILTIN)operands[0]; + rb_num_t index = (rb_num_t)operands[0]; + fprintf(f, "{\n"); + fprintf(f, " VALUE val;\n"); + fprintf(f, " RB_BUILTIN bf = (RB_BUILTIN)0x%"PRIxVALUE";\n", operands[0]); + fprintf(f, " rb_num_t index = (rb_num_t)0x%"PRIxVALUE";\n", operands[1]); + fprintf(f, <%= rstring2cstr(insn.expr.expr.lines.find { |l| l =~ / vm_invoke_builtin_delegate\(/ }).gsub("\n", '\n') %>); + fprintf(f, " stack[0] = val;\n"); + fprintf(f, "}\n"); +% else if (b->stack_size != 1) { if (mjit_opts.warnings || mjit_opts.verbose) fprintf(stderr, "MJIT warning: Unexpected JIT stack_size on leave: %d\n", b->stack_size); status->success = false; } +% end % # Skip vm_pop_frame for inlined call if (status->inlined_iseqs != NULL) { // the current ISeq is NOT being inlined % # Cancel on interrupts to make leave insn leaf @@ -84,6 +98,7 @@ switch (insn) { https://github.com/ruby/ruby/blob/trunk/tool/ruby_vm/views/mjit_compile.inc.erb#L98 b->stack_size += <%= insn.call_attribute('sp_inc') %>; b->finish_p = TRUE; break; + } % end % % # Main insn implementation generated by insns.def diff --git a/vm_core.h b/vm_core.h index 4f122ca..fd49c24 100644 --- a/vm_core.h +++ b/vm_core.h @@ -418,6 +418,7 @@ struct rb_iseq_constant_body { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L418 unsigned int stack_max; /* for stack overflow check */ char catch_except_p; /* If a frame of this ISeq may catch exception, set TRUE */ + bool builtin_inline_p; // This ISeq's builtin func is safe to be inlined by MJIT #if USE_MJIT /* The following fields are MJIT related info. */ -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/