inline Primitive.cexpr!

We can obtain the verbatim source code of Primitive.cexpr!.  Why not
paste that content into the JITed program.
This commit is contained in:
卜部昌平 2020-07-10 11:49:50 +09:00
parent f66e0212ef
commit 9721f477c7
Notes: git 2020-07-13 08:56:53 +09:00
7 changed files with 74 additions and 32 deletions

View File

@ -15,7 +15,7 @@ class Integer
# Returns +true+ if +int+ is an even number.
def even?
Primitive.attr! 'inline'
Primitive.cexpr! 'int_even_p(self)'
Primitive.cexpr! 'rb_int_even_p(self)'
end
# call-seq:
@ -79,6 +79,6 @@ class Integer
# Returns +true+ if +int+ has a zero value.
def zero?
Primitive.attr! 'inline'
Primitive.cexpr! 'int_zero_p(self)'
Primitive.cexpr! 'rb_int_zero_p(self)'
end
end

View File

@ -75,7 +75,6 @@ VALUE rb_int_divmod(VALUE x, VALUE y);
VALUE rb_int_and(VALUE x, VALUE y);
VALUE rb_int_lshift(VALUE x, VALUE y);
VALUE rb_int_div(VALUE x, VALUE y);
VALUE rb_int_abs(VALUE num);
VALUE rb_int_odd_p(VALUE num);
int rb_int_positive_p(VALUE num);
int rb_int_negative_p(VALUE num);
@ -107,6 +106,9 @@ VALUE rb_float_equal(VALUE x, VALUE y);
int rb_float_cmp(VALUE x, VALUE y);
VALUE rb_float_eql(VALUE x, VALUE y);
VALUE rb_fix_aref(VALUE fix, VALUE idx);
VALUE rb_int_zero_p(VALUE num);
VALUE rb_int_even_p(VALUE num);
VALUE rb_int_abs(VALUE num);
MJIT_SYMBOL_EXPORT_END
static inline bool

View File

@ -35,6 +35,7 @@ VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
void rb_obj_copy_ivar(VALUE dest, VALUE obj);
VALUE rb_false(VALUE obj);
VALUE rb_convert_type_with_id(VALUE v, int t, const char* nam, ID mid);
VALUE rb_obj_size(VALUE self, VALUE args, VALUE obj);
MJIT_SYMBOL_EXPORT_END
static inline void

View File

@ -794,6 +794,12 @@ int_zero_p(VALUE num)
return Qfalse;
}
VALUE
rb_int_zero_p(VALUE num)
{
return int_zero_p(num);
}
/*
* call-seq:
* num.nonzero? -> self or nil
@ -3250,6 +3256,12 @@ int_even_p(VALUE num)
}
}
VALUE
rb_int_even_p(VALUE num)
{
return int_even_p(num);
}
/*
* call-seq:
* int.allbits?(mask) -> true or false

View File

@ -591,7 +591,7 @@ rb_obj_itself(VALUE obj)
return obj;
}
static VALUE
VALUE
rb_obj_size(VALUE self, VALUE args, VALUE obj)
{
return LONG2FIX(1);

View File

@ -1,6 +1,8 @@
# Parse built-in script and make rbinc file
require 'ripper'
require 'stringio'
require_relative 'ruby_vm/helpers/c_escape'
def string_literal(lit, str = [])
while lit
@ -207,6 +209,29 @@ def collect_iseq iseq_ary
}
end
def generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_name)
f = StringIO.new
f.puts '{'
lineno += 1
locals.reverse_each.with_index{|param, i|
next unless Symbol === param
f.puts "MAYBE_UNUSED(const VALUE) #{param} = rb_vm_lvar(ec, #{-3 - i});"
lineno += 1
}
f.puts "#line #{body_lineno} \"#{line_file}\""
lineno += 1
f.puts text
lineno += text.count("\n") + 1
f.puts "#line #{lineno + 2} \"#{ofile}\"" # TODO: restore line number.
f.puts "}"
f.puts
lineno += 3
return lineno, f.string
end
def mk_builtin_header file
base = File.basename(file, '.rb')
ofile = "#{file}inc"
@ -244,23 +269,10 @@ def mk_builtin_header file
inlines.each{|cfunc_name, (body_lineno, text, locals, func_name)|
if String === cfunc_name
f.puts "static VALUE #{cfunc_name}(struct rb_execution_context_struct *ec, const VALUE self) {"
f.puts "static VALUE #{cfunc_name}(struct rb_execution_context_struct *ec, const VALUE self)"
lineno += 1
locals.reverse_each.with_index{|param, i|
next unless Symbol === param
f.puts "MAYBE_UNUSED(const VALUE) #{param} = rb_vm_lvar(ec, #{-3 - i});"
lineno += 1
}
f.puts "#line #{body_lineno} \"#{line_file}\""
lineno += 1
f.puts text
lineno += text.count("\n") + 1
f.puts "#line #{lineno + 2} \"#{ofile}\"" # TODO: restore line number.
f.puts "}"
lineno += 2
lineno, str = generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_name)
f.write str
else
# cinit!
f.puts "#line #{body_lineno} \"#{line_file}\""
@ -276,17 +288,32 @@ def mk_builtin_header file
f.puts %'static void'
f.puts %'mjit_compile_invokebuiltin_for_#{func}(FILE *f, long index)'
f.puts %'{'
f.puts %' if (index > 0) {'
f.puts %' fprintf(f, " const unsigned int lnum = GET_ISEQ()->body->local_table_size;\\n");'
f.puts %' fprintf(f, " const VALUE *argv = GET_EP() - lnum - VM_ENV_DATA_SIZE + 1 + %ld;\\n", index);'
f.puts %' }'
f.puts %' else if (index == 0) {'
f.puts %' fprintf(f, " const VALUE *argv = NULL;\\n");'
f.puts %' }'
f.puts %' else {'
f.puts %' fprintf(f, " const VALUE *argv = STACK_ADDR_FROM_TOP(%d);\\n", #{argc});'
f.puts %' }'
f.puts %' fprintf(f, " val = builtin_invoker#{argc}(ec, GET_SELF(), argv, %p);\\n", (const void *)#{cfunc_name});'
if inlines.has_key? cfunc_name
f.puts %' fprintf(f, " MAYBE_UNUSED(VALUE) self = GET_SELF();\\n");'
body_lineno, text, locals, func_name = inlines[cfunc_name]
lineno, str = generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_name)
str.each_line {|i|
f.printf(%' fprintf(f, "%%s", %s);\n', RubyVM::CEscape.rstring2cstr(i.sub(/^return\b/ , ' val =')))
}
else
decl = ', VALUE' * argc
argv = argc \
. times \
. map {|i|", argv[#{i}]"} \
. join('')
f.puts %' fprintf(f, " typedef VALUE (*func)(rb_execution_context_t *, VALUE#{decl});\\n");'
if argc > 0
f.puts %' if (index == -1) {'
f.puts %' fprintf(f, " const VALUE *argv = STACK_ADDR_FROM_TOP(%d);\\n", #{argc});'
f.puts %' }'
f.puts %' else {'
f.puts %' fprintf(f, " const unsigned int lnum = GET_ISEQ()->body->local_table_size;\\n");'
f.puts %' fprintf(f, " const VALUE *argv = GET_EP() - lnum - VM_ENV_DATA_SIZE + 1 + %ld;\\n", index);'
f.puts %' }'
end
f.puts %' fprintf(f, " func f = (func)%p\\n;", (const void *)#{cfunc_name});'
f.puts %' fprintf(f, " val = f(ec, GET_SELF()#{argv});\\n");'
end
f.puts %'}'
f.puts
}

View File

@ -46,7 +46,7 @@ module RubyVM::CEscape
# I believe this is the fastest implementation done in pure-ruby.
# Constants cached, gsub skips block evaluation, string literal optimized.
buf = str.b
buf.gsub! %r/./n, RString2CStr
buf.gsub! %r/./nm, RString2CStr
return %'"#{buf}"'
end