Introduce Primitive.attr! to annotate 'inline' (#3242)

[Feature #15589]
This commit is contained in:
Takashi Kokubun 2020-06-20 17:13:03 -07:00 коммит произвёл GitHub
Родитель d95249ade3
Коммит 7561db8c00
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 74 добавлений и 2 удалений

Просмотреть файл

@ -0,0 +1,36 @@
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

Просмотреть файл

@ -7274,6 +7274,11 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
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)
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)
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)
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);

Просмотреть файл

@ -4,6 +4,7 @@ class Integer
#
# Returns +true+ if +num+ has a zero value.
def zero?
Primitive.attr! 'inline'
Primitive.cexpr! 'int_zero_p(self)'
end
end

Просмотреть файл

@ -374,7 +374,12 @@ inlinable_iseq_p(const struct rb_iseq_constant_body *body)
// * 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) {

Просмотреть файл

@ -135,6 +135,12 @@ def collect_builtin base, tree, name, bs, inlines, params = nil
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

Просмотреть файл

@ -63,12 +63,26 @@ switch (insn) {
}
% 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) {
b->stack_size += <%= insn.call_attribute('sp_inc') %>;
b->finish_p = TRUE;
break;
}
% end
%
% # Main insn implementation generated by insns.def

Просмотреть файл

@ -418,6 +418,7 @@ struct rb_iseq_constant_body {
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. */