1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/tool/mk_builtin_loader.rb
Koichi Sasada a3e6f52c17 rename __builtin_inline!(code) and introduce others.
rename __builtin_inline!(code) to __builtin_cstmt(code).
Also this commit introduce the following inlining C code features.

* __builtin_cstmt!(STMT)

(renamed from __builtin_inline!)

Define a function which run STMT implicitly and call this function at
evatuation time. Note that you need to return some value in STMT.
If there is a local variables (includes method parameters), you can
read these values.

  static VALUE func(ec, self) {
    VALUE x = ...;
    STMT
  }

Usage:
  def double a
    # a is readable from C code.
    __builtin_cstmt! 'return INT2FIX(FIX2INT(a) * 2);'
  end

* __builtin_cexpr!(EXPR)

Define a function which invoke EXPR implicitly like `__builtin_cstmt!`.
Different from cstmt!, which compiled with `return EXPR;`.
(`return` and `;` are added implicitly)

  static VALUE func(ec, self) {
    VALUE x = ...;
    return EXPPR;
  }

Usage:
  def double a
    __builtin_cexpr! 'INT2FIX(FIX2INT(a) * 2)'
  end

* __builtin_cconst!(EXPR)

Define a function which invoke EXPR implicitly like cexpr!.
However, the function is called once at compile time, not evaluated time.
Any local variables are not accessible (because there is no local variable
at compile time).

Usage:
  GCC = __builtin_cconst! '__GNUC__'

* __builtin_cinit!(STMT)

STMT are writtein in auto-generated code.
This code does not return any value.

Usage:

  __builtin_cinit! '#include <zlib.h>'
  def no_compression?
    __builtin_cconst! 'Z_NO_COMPRESSION ? Qtrue : Qfalse'
  end
2019-11-27 03:55:28 +09:00

169 lines
4.3 KiB
Ruby

def inline_text argc, prev_insn
raise "argc (#{argc}) of inline! should be 1" unless argc == 1
raise "1st argument should be string literal" unless prev_insn[0] == :putstring
prev_insn[1].rstrip
end
def collect_builtin base, iseq_ary, bs, inlines
code = iseq_ary[13]
params = iseq_ary[10]
prev_insn = nil
lineno = nil
code.each{|insn|
case insn
when Array
# ok
when Integer
lineno = insn
next
else
next
end
next unless Array === insn
case insn[0]
when :send
ci = insn[1]
if /\A__builtin_(.+)/ =~ ci[:mid]
func_name = $1
argc = ci[:orig_argc]
if /(.+)\!\z/ =~ func_name
case $1
when 'cstmt'
text = inline_text argc, prev_insn
func_name = "builtin_inline#{inlines.size}"
inlines << [func_name, [lineno, text, params]]
argc -= 1
when 'cexpr', 'cconst'
text = inline_text argc, prev_insn
code = "return #{text};"
func_name = "builtin_inline#{inlines.size}"
params = [] if $1 == 'cconst'
inlines << [func_name, [lineno, code, params]]
argc -= 1
when 'cinit'
text = inline_text argc, prev_insn
func_name = nil
inlines << [nil, [lineno, text, nil]]
argc -= 1
end
end
if bs[func_name] &&
bs[func_name] != argc
raise "same builtin function \"#{func_name}\", but different arity (was #{bs[func_name]} but #{argc})"
end
bs[func_name] = argc if func_name
end
else
insn[1..-1].each{|op|
if op.is_a?(Array) && op[0] == "YARVInstructionSequence/SimpleDataFormat"
collect_builtin base, op, bs, inlines
end
}
end
prev_insn = insn
}
end
# ruby mk_builtin_loader.rb TARGET_FILE.rb
# #=> generate TARGET_FILE.rbinc
#
def mk_builtin_header file
base = File.basename(file, '.rb')
ofile = "#{file}inc"
# bs = { func_name => argc }
collect_builtin(base, RubyVM::InstructionSequence.compile_file(file, false).to_a, bs = {}, inlines = [])
begin
f = open(ofile, 'w')
rescue Errno::EACCESS
# Fall back to the current directory
f = open(File.basename(ofile), 'w')
end
begin
f.puts "// -*- c -*-"
f.puts "// DO NOT MODIFY THIS FILE DIRECTLY."
f.puts "// auto-generated file"
f.puts "// by #{__FILE__}"
f.puts "// with #{file}"
f.puts
lineno = 6
line_file = file.gsub('\\', '/')
inlines.each{|name, (body_lineno, text, params)|
if name
f.puts "static VALUE #{name}(rb_execution_context_t *ec, const VALUE self) {"
lineno += 1
params.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
else
# cinit!
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.
lineno += 1
end
}
f.puts "static void load_#{base}(void)"
f.puts "{"
table = "#{base}_table"
f.puts " // table definition"
f.puts " static const struct rb_builtin_function #{table}[] = {"
bs.each.with_index{|(func, argc), i|
f.puts " RB_BUILTIN_FUNCTION(#{i}, #{func}, #{argc}),"
}
f.puts " RB_BUILTIN_FUNCTION(-1, NULL, 0),"
f.puts " };"
f.puts
f.puts " // arity_check"
f.puts "COMPILER_WARNING_PUSH"
f.puts "#if GCC_VERSION_SINCE(5, 1, 0) || __clang__"
f.puts "COMPILER_WARNING_ERROR(-Wincompatible-pointer-types)"
f.puts "#endif"
bs.each{|func, argc|
f.puts " if (0) rb_builtin_function_check_arity#{argc}(#{func});"
}
f.puts "COMPILER_WARNING_POP"
f.puts
f.puts " // load"
f.puts " rb_load_with_builtin_functions(#{base.dump}, #{table});"
f.puts "}"
ensure
f.close
end
end
ARGV.each{|file|
# feature.rb => load_feature.inc
mk_builtin_header file
}