summaryrefslogtreecommitdiff
path: root/tool/ruby_vm
diff options
context:
space:
mode:
Diffstat (limited to 'tool/ruby_vm')
-rw-r--r--tool/ruby_vm/controllers/application_controller.rb25
-rw-r--r--tool/ruby_vm/helpers/c_escape.rb130
-rw-r--r--tool/ruby_vm/helpers/dumper.rb109
-rw-r--r--tool/ruby_vm/helpers/scanner.rb52
-rw-r--r--tool/ruby_vm/loaders/insns_def.rb99
-rw-r--r--tool/ruby_vm/loaders/opt_insn_unif_def.rb33
-rw-r--r--tool/ruby_vm/loaders/opt_operand_def.rb55
-rw-r--r--tool/ruby_vm/loaders/vm_opts_h.rb36
-rw-r--r--tool/ruby_vm/models/attribute.rb58
-rw-r--r--tool/ruby_vm/models/bare_instruction.rb236
-rw-r--r--tool/ruby_vm/models/c_expr.rb44
-rw-r--r--tool/ruby_vm/models/instructions.rb23
-rw-r--r--tool/ruby_vm/models/instructions_unification.rb42
-rw-r--r--tool/ruby_vm/models/operands_unification.rb141
-rw-r--r--tool/ruby_vm/models/trace_instruction.rb70
-rw-r--r--tool/ruby_vm/models/typemap.rb62
-rw-r--r--tool/ruby_vm/models/zjit_instruction.rb56
-rw-r--r--tool/ruby_vm/scripts/converter.rb29
-rw-r--r--tool/ruby_vm/scripts/insns2vm.rb100
-rw-r--r--tool/ruby_vm/views/_attributes.erb35
-rw-r--r--tool/ruby_vm/views/_c_expr.erb17
-rw-r--r--tool/ruby_vm/views/_comptime_insn_stack_increase.erb71
-rw-r--r--tool/ruby_vm/views/_copyright.erb31
-rw-r--r--tool/ruby_vm/views/_insn_entry.erb75
-rw-r--r--tool/ruby_vm/views/_insn_leaf_info.erb18
-rw-r--r--tool/ruby_vm/views/_insn_len_info.erb36
-rw-r--r--tool/ruby_vm/views/_insn_name_info.erb59
-rw-r--r--tool/ruby_vm/views/_insn_operand_info.erb69
-rw-r--r--tool/ruby_vm/views/_insn_type_chars.erb32
-rw-r--r--tool/ruby_vm/views/_leaf_helpers.erb50
-rw-r--r--tool/ruby_vm/views/_notice.erb22
-rw-r--r--tool/ruby_vm/views/_sp_inc_helpers.erb37
-rw-r--r--tool/ruby_vm/views/_trace_instruction.erb21
-rw-r--r--tool/ruby_vm/views/_zjit_helpers.erb31
-rw-r--r--tool/ruby_vm/views/_zjit_instruction.erb12
-rw-r--r--tool/ruby_vm/views/insns.inc.erb41
-rw-r--r--tool/ruby_vm/views/insns_info.inc.erb26
-rw-r--r--tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb14
-rw-r--r--tool/ruby_vm/views/optinsn.inc.erb71
-rw-r--r--tool/ruby_vm/views/optunifs.inc.erb18
-rw-r--r--tool/ruby_vm/views/vm.inc.erb34
-rw-r--r--tool/ruby_vm/views/vmtc.inc.erb29
42 files changed, 2249 insertions, 0 deletions
diff --git a/tool/ruby_vm/controllers/application_controller.rb b/tool/ruby_vm/controllers/application_controller.rb
new file mode 100644
index 0000000000..f6c0e39600
--- /dev/null
+++ b/tool/ruby_vm/controllers/application_controller.rb
@@ -0,0 +1,25 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/dumper'
+require_relative '../models/instructions'
+require_relative '../models/typemap'
+require_relative '../loaders/vm_opts_h'
+
+class ApplicationController
+ def generate i, destdir, basedir
+ path = Pathname.new i
+ dst = destdir ? Pathname.new(destdir).join(i) : Pathname.new(i)
+ base = basedir ? Pathname.new(basedir) : Pathname.pwd
+ dumper = RubyVM::Dumper.new dst, base.expand_path
+ return [path, dumper]
+ end
+end
diff --git a/tool/ruby_vm/helpers/c_escape.rb b/tool/ruby_vm/helpers/c_escape.rb
new file mode 100644
index 0000000000..628cb0428b
--- /dev/null
+++ b/tool/ruby_vm/helpers/c_escape.rb
@@ -0,0 +1,130 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require 'securerandom'
+
+module RubyVM::CEscape
+ module_function
+
+ # generate comment, with escaps.
+ def commentify str
+ unless str = str.dump[/\A"\K.*(?="\z)/]
+ raise Encoding::CompatibilityError, "must be ASCII-compatible (#{str.encoding})"
+ end
+ return "/* #{str.gsub('*/', '*\\/').gsub('/*', '/\\*')} */"
+ end
+
+ # Mimic gensym of CL.
+ def gensym prefix = 'gensym_'
+ return as_tr_cpp "#{prefix}#{SecureRandom.uuid}"
+ end
+
+ # Mimic AS_TR_CPP() of autoconf.
+ def as_tr_cpp name
+ q = name.b
+ q.gsub! %r/[^a-zA-Z0-9_]/m, '_'
+ q.gsub! %r/_+/, '_'
+ return q
+ end
+
+ # Section 6.10.4 of ISO/IEC 9899:1999 specifies that the file name used for
+ # #line directive shall be a "character string literal". So this is needed.
+ #
+ # I'm not sure how many chars are allowed here, though. The standard
+ # specifies 4095 chars at most, after string concatenation (section 5.2.4.1).
+ # But it is easy to have a path that is longer than that.
+ #
+ # Here we ignore the standard. Just create single string literal of any
+ # needed length.
+ def rstring2cstr str
+ # 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/./nm, RString2CStr
+ return %'"#{buf}"'
+ end
+
+ RString2CStr = {
+ "\x00"=> "\\0", "\x01"=> "\\x1", "\x02"=> "\\x2", "\x03"=> "\\x3",
+ "\x04"=> "\\x4", "\x05"=> "\\x5", "\x06"=> "\\x6", "\a"=> "\\a",
+ "\b"=> "\\b", "\t"=> "\\t", "\n"=> "\\n", "\v"=> "\\v",
+ "\f"=> "\\f", "\r"=> "\\r", "\x0E"=> "\\xe", "\x0F"=> "\\xf",
+ "\x10"=>"\\x10", "\x11"=>"\\x11", "\x12"=>"\\x12", "\x13"=>"\\x13",
+ "\x14"=>"\\x14", "\x15"=>"\\x15", "\x16"=>"\\x16", "\x17"=>"\\x17",
+ "\x18"=>"\\x18", "\x19"=>"\\x19", "\x1A"=>"\\x1a", "\e"=>"\\x1b",
+ "\x1C"=>"\\x1c", "\x1D"=>"\\x1d", "\x1E"=>"\\x1e", "\x1F"=>"\\x1f",
+ " "=> " ", "!"=> "!", "\""=> "\\\"", "#"=> "#",
+ "$"=> "$", "%"=> "%", "&"=> "&", "'"=> "'",
+ "("=> "(", ")"=> ")", "*"=> "*", "+"=> "+",
+ ","=> ",", "-"=> "-", "."=> ".", "/"=> "/",
+ "0"=> "0", "1"=> "1", "2"=> "2", "3"=> "3",
+ "4"=> "4", "5"=> "5", "6"=> "6", "7"=> "7",
+ "8"=> "8", "9"=> "9", ":"=> ":", ";"=> ";",
+ "<"=> "<", "="=> "=", ">"=> ">", "?"=> "?",
+ "@"=> "@", "A"=> "A", "B"=> "B", "C"=> "C",
+ "D"=> "D", "E"=> "E", "F"=> "F", "G"=> "G",
+ "H"=> "H", "I"=> "I", "J"=> "J", "K"=> "K",
+ "L"=> "L", "M"=> "M", "N"=> "N", "O"=> "O",
+ "P"=> "P", "Q"=> "Q", "R"=> "R", "S"=> "S",
+ "T"=> "T", "U"=> "U", "V"=> "V", "W"=> "W",
+ "X"=> "X", "Y"=> "Y", "Z"=> "Z", "["=> "[",
+ "\\"=> "\\\\", "]"=> "]", "^"=> "^", "_"=> "_",
+ "`"=> "`", "a"=> "a", "b"=> "b", "c"=> "c",
+ "d"=> "d", "e"=> "e", "f"=> "f", "g"=> "g",
+ "h"=> "h", "i"=> "i", "j"=> "j", "k"=> "k",
+ "l"=> "l", "m"=> "m", "n"=> "n", "o"=> "o",
+ "p"=> "p", "q"=> "q", "r"=> "r", "s"=> "s",
+ "t"=> "t", "u"=> "u", "v"=> "v", "w"=> "w",
+ "x"=> "x", "y"=> "y", "z"=> "z", "{"=> "{",
+ "|"=> "|", "}"=> "}", "~"=> "~", "\x7F"=>"\\x7f",
+ "\x80"=>"\\x80", "\x81"=>"\\x81", "\x82"=>"\\x82", "\x83"=>"\\x83",
+ "\x84"=>"\\x84", "\x85"=>"\\x85", "\x86"=>"\\x86", "\x87"=>"\\x87",
+ "\x88"=>"\\x88", "\x89"=>"\\x89", "\x8A"=>"\\x8a", "\x8B"=>"\\x8b",
+ "\x8C"=>"\\x8c", "\x8D"=>"\\x8d", "\x8E"=>"\\x8e", "\x8F"=>"\\x8f",
+ "\x90"=>"\\x90", "\x91"=>"\\x91", "\x92"=>"\\x92", "\x93"=>"\\x93",
+ "\x94"=>"\\x94", "\x95"=>"\\x95", "\x96"=>"\\x96", "\x97"=>"\\x97",
+ "\x98"=>"\\x98", "\x99"=>"\\x99", "\x9A"=>"\\x9a", "\x9B"=>"\\x9b",
+ "\x9C"=>"\\x9c", "\x9D"=>"\\x9d", "\x9E"=>"\\x9e", "\x9F"=>"\\x9f",
+ "\xA0"=>"\\xa0", "\xA1"=>"\\xa1", "\xA2"=>"\\xa2", "\xA3"=>"\\xa3",
+ "\xA4"=>"\\xa4", "\xA5"=>"\\xa5", "\xA6"=>"\\xa6", "\xA7"=>"\\xa7",
+ "\xA8"=>"\\xa8", "\xA9"=>"\\xa9", "\xAA"=>"\\xaa", "\xAB"=>"\\xab",
+ "\xAC"=>"\\xac", "\xAD"=>"\\xad", "\xAE"=>"\\xae", "\xAF"=>"\\xaf",
+ "\xB0"=>"\\xb0", "\xB1"=>"\\xb1", "\xB2"=>"\\xb2", "\xB3"=>"\\xb3",
+ "\xB4"=>"\\xb4", "\xB5"=>"\\xb5", "\xB6"=>"\\xb6", "\xB7"=>"\\xb7",
+ "\xB8"=>"\\xb8", "\xB9"=>"\\xb9", "\xBA"=>"\\xba", "\xBB"=>"\\xbb",
+ "\xBC"=>"\\xbc", "\xBD"=>"\\xbd", "\xBE"=>"\\xbe", "\xBF"=>"\\xbf",
+ "\xC0"=>"\\xc0", "\xC1"=>"\\xc1", "\xC2"=>"\\xc2", "\xC3"=>"\\xc3",
+ "\xC4"=>"\\xc4", "\xC5"=>"\\xc5", "\xC6"=>"\\xc6", "\xC7"=>"\\xc7",
+ "\xC8"=>"\\xc8", "\xC9"=>"\\xc9", "\xCA"=>"\\xca", "\xCB"=>"\\xcb",
+ "\xCC"=>"\\xcc", "\xCD"=>"\\xcd", "\xCE"=>"\\xce", "\xCF"=>"\\xcf",
+ "\xD0"=>"\\xd0", "\xD1"=>"\\xd1", "\xD2"=>"\\xd2", "\xD3"=>"\\xd3",
+ "\xD4"=>"\\xd4", "\xD5"=>"\\xd5", "\xD6"=>"\\xd6", "\xD7"=>"\\xd7",
+ "\xD8"=>"\\xd8", "\xD9"=>"\\xd9", "\xDA"=>"\\xda", "\xDB"=>"\\xdb",
+ "\xDC"=>"\\xdc", "\xDD"=>"\\xdd", "\xDE"=>"\\xde", "\xDF"=>"\\xdf",
+ "\xE0"=>"\\xe0", "\xE1"=>"\\xe1", "\xE2"=>"\\xe2", "\xE3"=>"\\xe3",
+ "\xE4"=>"\\xe4", "\xE5"=>"\\xe5", "\xE6"=>"\\xe6", "\xE7"=>"\\xe7",
+ "\xE8"=>"\\xe8", "\xE9"=>"\\xe9", "\xEA"=>"\\xea", "\xEB"=>"\\xeb",
+ "\xEC"=>"\\xec", "\xED"=>"\\xed", "\xEE"=>"\\xee", "\xEF"=>"\\xef",
+ "\xF0"=>"\\xf0", "\xF1"=>"\\xf1", "\xF2"=>"\\xf2", "\xF3"=>"\\xf3",
+ "\xF4"=>"\\xf4", "\xF5"=>"\\xf5", "\xF6"=>"\\xf6", "\xF7"=>"\\xf7",
+ "\xF8"=>"\\xf8", "\xF9"=>"\\xf9", "\xFA"=>"\\xfa", "\xFB"=>"\\xfb",
+ "\xFC"=>"\\xfc", "\xFD"=>"\\xfd", "\xFE"=>"\\xfe", "\xFF"=>"\\xff",
+ }.freeze
+ private_constant :RString2CStr
+end
+
+unless defined? ''.b
+ class String
+ def b
+ return dup.force_encoding 'binary'
+ end
+ end
+end
diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb
new file mode 100644
index 0000000000..f0758dd44f
--- /dev/null
+++ b/tool/ruby_vm/helpers/dumper.rb
@@ -0,0 +1,109 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require 'pathname'
+require 'erb'
+require_relative 'c_escape'
+
+class RubyVM::Dumper
+ include RubyVM::CEscape
+ private
+
+ def new_binding
+ # This `eval 'binding'` does not return the current binding
+ # but creates one on top of it.
+ return eval 'binding'
+ end
+
+ def new_erb spec
+ path = Pathname.new(__FILE__)
+ path = (path.relative_path_from(Pathname.pwd) rescue path).dirname
+ path += '../views'
+ path += Pathname.pwd.join(spec).expand_path.to_s.sub("#{@base}/", '')
+ src = path.expand_path.read mode: 'rt:utf-8:utf-8'
+ rescue Errno::ENOENT
+ raise "don't know how to generate #{path}"
+ else
+ erb = ERB.new(src, trim_mode: '%-')
+ erb.filename = path.to_path
+ return erb
+ end
+
+ def finderb spec
+ return @erb.fetch spec do |k|
+ erb = new_erb k
+ @erb[k] = erb
+ end
+ end
+
+ def replace_pragma_line str, lineno
+ if /#(\s*)pragma RubyVM reset source\n/ =~ str then
+ return "##{$1}line #{lineno + 2} #{@file}\n"
+ else
+ return str
+ end
+ end
+
+ def local_variable_set bnd, var, val
+ eval '__locals__ ||= {}', bnd
+ locals = eval '__locals__', bnd
+ locals[var] = val
+ eval "#{var} = __locals__[:#{var}]", bnd
+ test = eval "#{var}", bnd
+ raise unless test == val
+ end
+
+ public
+
+ def do_render source, locals
+ erb = finderb source
+ bnd = @empty.dup
+ locals.each_pair do |k, v|
+ local_variable_set bnd, k, v
+ end
+ return erb.result bnd
+ end
+
+ def replace_pragma str
+ return str \
+ . each_line \
+ . with_index \
+ . map {|i, j| replace_pragma_line i, j } \
+ . join
+ end
+
+ def initialize dst, base
+ @erb = {}
+ @empty = new_binding
+ @file = cstr dst.to_path
+ @base = base
+ end
+
+ def render partial, opts = { :locals => {} }
+ return do_render "_#{partial}.erb", opts[:locals]
+ end
+
+ def generate template
+ str = do_render "#{template}.erb", {}
+ return replace_pragma str
+ end
+
+ private
+
+ # view helpers
+
+ alias cstr rstring2cstr
+ alias comm commentify
+
+ def render_c_expr expr
+ render 'c_expr', locals: { expr: expr, }
+ end
+end
diff --git a/tool/ruby_vm/helpers/scanner.rb b/tool/ruby_vm/helpers/scanner.rb
new file mode 100644
index 0000000000..8998abb2d3
--- /dev/null
+++ b/tool/ruby_vm/helpers/scanner.rb
@@ -0,0 +1,52 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require 'pathname'
+
+# Poor man's StringScanner.
+# Sadly https://bugs.ruby-lang.org/issues/8343 is not backported to 2.0. We
+# have to do it by hand.
+class RubyVM::Scanner
+ attr_reader :__FILE__
+ attr_reader :__LINE__
+
+ def initialize path
+ src = Pathname.new(__FILE__)
+ src = (src.relative_path_from(Pathname.pwd) rescue src).dirname
+ src += path
+ @__LINE__ = 1
+ @__FILE__ = src.to_path
+ @str = src.read mode: 'rt:utf-8:utf-8'
+ @pos = 0
+ end
+
+ def eos?
+ return @pos >= @str.length
+ end
+
+ def scan re
+ ret = @__LINE__
+ @last_match = @str.match re, @pos
+ return unless @last_match
+ @__LINE__ += @last_match.to_s.count "\n"
+ @pos = @last_match.end 0
+ return ret
+ end
+
+ def scan! re
+ scan re or raise sprintf "parse error at %s:%d near:\n %s...", \
+ @__FILE__, @__LINE__, @str[@pos, 32]
+ end
+
+ def [] key
+ return @last_match[key]
+ end
+end
diff --git a/tool/ruby_vm/loaders/insns_def.rb b/tool/ruby_vm/loaders/insns_def.rb
new file mode 100644
index 0000000000..d45d0ba83c
--- /dev/null
+++ b/tool/ruby_vm/loaders/insns_def.rb
@@ -0,0 +1,99 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/scanner'
+require_relative './vm_opts_h'
+
+json = []
+scanner = RubyVM::Scanner.new '../../../insns.def'
+path = scanner.__FILE__
+grammar = %r'
+ (?<comment> /[*] [^*]* [*]+ (?: [^*/] [^*]* [*]+ )* / ){0}
+ (?<keyword> typedef | extern | static | auto | register |
+ struct | union | enum ){0}
+ (?<C> (?: \g<block> | [^{}]+ )* ){0}
+ (?<block> \{ \g<ws>* \g<C> \g<ws>* \} ){0}
+ (?<ws> \g<comment> | \s ){0}
+ (?<ident> [_a-zA-Z] [0-9_a-zA-Z]* ){0}
+ (?<type> (?: \g<keyword> \g<ws>+ )* \g<ident> ){0}
+ (?<arg> \g<type> \g<ws>+ \g<ident> ){0}
+ (?<argv> (?# empty ) |
+ void |
+ (?: \.\.\. | \g<arg>) (?: \g<ws>* , \g<ws>* \g<arg> \g<ws>* )* ){0}
+ (?<pragma> \g<ws>* // \s* attr \g<ws>+
+ (?<pragma:type> \g<type> ) \g<ws>+
+ (?<pragma:name> \g<ident> ) \g<ws>*
+ = \g<ws>*
+ (?<pragma:expr> .+?; ) \g<ws>* ){0}
+ (?<insn> DEFINE_INSN(_IF\((?<insn:if>\w+)\))? \g<ws>+
+ (?<insn:name> \g<ident> ) \g<ws>*
+ [(] \g<ws>* (?<insn:opes> \g<argv> ) \g<ws>* [)] \g<ws>*
+ [(] \g<ws>* (?<insn:pops> \g<argv> ) \g<ws>* [)] \g<ws>*
+ [(] \g<ws>* (?<insn:rets> \g<argv> ) \g<ws>* [)] \g<ws>* ){0}
+'x
+
+until scanner.eos? do
+ next if scanner.scan(/\G#{grammar}\g<ws>+/o)
+ split = lambda {|v|
+ case v when /\Avoid\z/ then
+ []
+ else
+ v.split(/, */)
+ end
+ }
+
+ l1 = scanner.scan!(/\G#{grammar}\g<insn>/o)
+ name = scanner["insn:name"]
+ opt = scanner["insn:if"]
+ ope = split.(scanner["insn:opes"])
+ pop = split.(scanner["insn:pops"])
+ ret = split.(scanner["insn:rets"])
+ if ope.include?("...")
+ raise sprintf("parse error at %s:%d:%s: operands cannot be variadic",
+ scanner.__FILE__, scanner.__LINE__, name)
+ end
+
+ attrs = []
+ while l2 = scanner.scan(/\G#{grammar}\g<pragma>/o) do
+ attrs << {
+ location: [path, l2],
+ name: scanner["pragma:name"],
+ type: scanner["pragma:type"],
+ expr: scanner["pragma:expr"],
+ }
+ end
+
+ l3 = scanner.scan!(/\G#{grammar}\g<block>/o)
+ if opt.nil? || RubyVM::VmOptsH[opt]
+ json << {
+ name: name,
+ location: [path, l1],
+ signature: {
+ name: name,
+ ope: ope,
+ pop: pop,
+ ret: ret,
+ },
+ attributes: attrs,
+ expr: {
+ location: [path, l3],
+ expr: scanner["block"],
+ },
+ }
+ end
+end
+
+RubyVM::InsnsDef = json
+
+if __FILE__ == $0 then
+ require 'json'
+ JSON.dump RubyVM::InsnsDef, STDOUT
+end
diff --git a/tool/ruby_vm/loaders/opt_insn_unif_def.rb b/tool/ruby_vm/loaders/opt_insn_unif_def.rb
new file mode 100644
index 0000000000..0750f1823a
--- /dev/null
+++ b/tool/ruby_vm/loaders/opt_insn_unif_def.rb
@@ -0,0 +1,33 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/scanner'
+
+json = []
+scanner = RubyVM::Scanner.new '../../../defs/opt_insn_unif.def'
+path = scanner.__FILE__
+until scanner.eos? do
+ next if scanner.scan(/\G ^ (?: \#.* )? \n /x)
+ break if scanner.scan(/\G ^ __END__ $ /x)
+
+ pos = scanner.scan!(/\G (?<series> (?: [\ \t]* \w+ )+ ) \n /mx)
+ json << {
+ location: [path, pos],
+ signature: scanner["series"].strip.split
+ }
+end
+
+RubyVM::OptInsnUnifDef = json
+
+if __FILE__ == $0 then
+ require 'json'
+ JSON.dump RubyVM::OptInsnUnifDef, STDOUT
+end
diff --git a/tool/ruby_vm/loaders/opt_operand_def.rb b/tool/ruby_vm/loaders/opt_operand_def.rb
new file mode 100644
index 0000000000..e08509a433
--- /dev/null
+++ b/tool/ruby_vm/loaders/opt_operand_def.rb
@@ -0,0 +1,55 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/scanner'
+
+json = []
+scanner = RubyVM::Scanner.new '../../../defs/opt_operand.def'
+path = scanner.__FILE__
+grammar = %r/
+ (?<comment> \# .+? \n ){0}
+ (?<ws> \g<comment> | \s ){0}
+ (?<insn> \w+ ){0}
+ (?<paren> \( (?: \g<paren> | [^()]+)* \) ){0}
+ (?<expr> (?: \g<paren> | [^(),\ \n] )+ ){0}
+ (?<remain> \g<expr> ){0}
+ (?<arg> \g<expr> ){0}
+ (?<extra> , \g<ws>* \g<remain> ){0}
+ (?<args> \g<arg> \g<extra>* ){0}
+ (?<decl> \g<insn> \g<ws>+ \g<args> \n ){0}
+/mx
+
+until scanner.eos? do
+ break if scanner.scan(/\G ^ __END__ $ /x)
+ next if scanner.scan(/\G#{grammar} \g<ws>+ /ox)
+
+ line = scanner.scan!(/\G#{grammar} \g<decl> /mox)
+ insn = scanner["insn"]
+ args = scanner["args"]
+ ary = []
+ until args.strip.empty? do
+ md = /\G#{grammar} \g<args> /mox.match(args)
+ ary << md["arg"]
+ args = md["remain"]
+ break unless args
+ end
+ json << {
+ location: [path, line],
+ signature: [insn, ary]
+ }
+end
+
+RubyVM::OptOperandDef = json
+
+if __FILE__ == $0 then
+ require 'json'
+ JSON.dump RubyVM::OptOperandDef, STDOUT
+end
diff --git a/tool/ruby_vm/loaders/vm_opts_h.rb b/tool/ruby_vm/loaders/vm_opts_h.rb
new file mode 100644
index 0000000000..d626ea0296
--- /dev/null
+++ b/tool/ruby_vm/loaders/vm_opts_h.rb
@@ -0,0 +1,36 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/scanner'
+
+json = {}
+scanner = RubyVM::Scanner.new '../../../vm_opts.h'
+grammar = %r/
+ (?<ws> \u0020 ){0}
+ (?<key> \w+ ){0}
+ (?<value> 0|1 ){0}
+ (?<define> \G \#define \g<ws>+ OPT_\g<key> \g<ws>+ \g<value> \g<ws>*\n )
+/mx
+
+until scanner.eos? do
+ if scanner.scan grammar then
+ json[scanner['key']] = ! scanner['value'].to_i.zero? # not nonzero?
+ else
+ scanner.scan(/\G.*\n/)
+ end
+end
+
+RubyVM::VmOptsH = json
+
+if __FILE__ == $0 then
+ require 'json'
+ JSON.dump RubyVM::VmOptsH, STDOUT
+end
diff --git a/tool/ruby_vm/models/attribute.rb b/tool/ruby_vm/models/attribute.rb
new file mode 100644
index 0000000000..177b701b92
--- /dev/null
+++ b/tool/ruby_vm/models/attribute.rb
@@ -0,0 +1,58 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative 'c_expr'
+
+class RubyVM::Attribute
+ include RubyVM::CEscape
+ attr_reader :insn, :key, :type, :expr
+
+ def initialize opts = {}
+ @insn = opts[:insn]
+ @key = opts[:name]
+ @expr = RubyVM::CExpr.new location: opts[:location], expr: opts[:expr]
+ @type = opts[:type]
+ @ope_decls = @insn.operands.map do |operand|
+ decl = operand[:decl]
+ if @key == 'comptime_sp_inc' && operand[:type] == 'CALL_DATA'
+ decl = decl.gsub('CALL_DATA', 'CALL_INFO').gsub('cd', 'ci')
+ end
+ decl
+ end
+ end
+
+ def name
+ as_tr_cpp "attr #{@key} @ #{@insn.name}"
+ end
+
+ def pretty_name
+ "attr #{type} #{key} @ #{insn.pretty_name}"
+ end
+
+ def declaration
+ if @ope_decls.empty?
+ argv = "void"
+ else
+ argv = @ope_decls.join(', ')
+ end
+ sprintf '%s %s(%s)', @type, name, argv
+ end
+
+ def definition
+ if @ope_decls.empty?
+ argv = "void"
+ else
+ argv = @ope_decls.map {|decl| "MAYBE_UNUSED(#{decl})" }.join(",\n ")
+ argv = "\n #{argv}\n" if @ope_decls.size > 1
+ end
+ sprintf "%s\n%s(%s)", @type, name, argv
+ end
+end
diff --git a/tool/ruby_vm/models/bare_instruction.rb b/tool/ruby_vm/models/bare_instruction.rb
new file mode 100644
index 0000000000..f87dd74179
--- /dev/null
+++ b/tool/ruby_vm/models/bare_instruction.rb
@@ -0,0 +1,236 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../loaders/insns_def'
+require_relative 'c_expr'
+require_relative 'typemap'
+require_relative 'attribute'
+
+class RubyVM::BareInstruction
+ attr_reader :template, :name, :operands, :pops, :rets, :decls, :expr
+
+ def initialize opts = {}
+ @template = opts[:template]
+ @name = opts[:name]
+ @loc = opts[:location]
+ @sig = opts[:signature]
+ @expr = RubyVM::CExpr.new opts[:expr]
+ @operands = typesplit @sig[:ope]
+ @pops = typesplit @sig[:pop].reject {|i| i == '...' }
+ @rets = typesplit @sig[:ret].reject {|i| i == '...' }
+ @attrs = opts[:attributes].map {|i|
+ RubyVM::Attribute.new i.merge(:insn => self)
+ }.each_with_object({}) {|a, h|
+ h[a.key] = a
+ }
+ @attrs_orig = @attrs.dup
+ check_attribute_consistency
+ predefine_attributes
+ end
+
+ def pretty_name
+ n = @sig[:name]
+ o = @sig[:ope].map{|i| i[/\S+$/] }.join ', '
+ p = @sig[:pop].map{|i| i[/\S+$/] }.join ', '
+ r = @sig[:ret].map{|i| i[/\S+$/] }.join ', '
+ return sprintf "%s(%s)(%s)(%s)", n, o, p, r
+ end
+
+ def bin
+ return "BIN(#{name})"
+ end
+
+ def call_attribute name
+ return sprintf 'attr_%s_%s(%s)', name, @name, \
+ @operands.map {|i| i[:name] }.compact.join(', ')
+ end
+
+ def has_attribute? k
+ @attrs_orig.has_key? k
+ end
+
+ def attributes
+ return @attrs \
+ . sort_by {|k, _| k } \
+ . map {|_, v| v }
+ end
+
+ def width
+ return 1 + operands.size
+ end
+
+ def declarations
+ return @variables \
+ . values \
+ . group_by {|h| h[:type] } \
+ . sort_by {|t, v| t } \
+ . map {|t, v| [t, v.map {|i| i[:name] }.sort ] } \
+ . map {|t, v|
+ sprintf("MAYBE_UNUSED(%s) %s", t, v.join(', '))
+ }
+ end
+
+ def preamble
+ # preamble makes sense for operand unifications
+ return []
+ end
+
+ def sc?
+ # sc stands for stack caching.
+ return false
+ end
+
+ def cast_to_VALUE var, expr = var[:name]
+ RubyVM::Typemap.typecast_to_VALUE var[:type], expr
+ end
+
+ def cast_from_VALUE var, expr = var[:name]
+ RubyVM::Typemap.typecast_from_VALUE var[:type], expr
+ end
+
+ def operands_info
+ operands.map {|o|
+ c, _ = RubyVM::Typemap.fetch o[:type]
+ next c
+ }.join
+ end
+
+ def handles_sp?
+ /\b(false|0)\b/ !~ @attrs.fetch('handles_sp').expr.expr
+ end
+
+ def handle_canary stmt
+ # Stack canary is basically a good thing that we want to add, however:
+ #
+ # - When the instruction returns variadic number of return values,
+ # it is not easy to tell where is the stack top. We can't but
+ # skip it.
+ #
+ # - When the instruction body is empty (like putobject), we can
+ # say for 100% sure that canary is a waste of time.
+ #
+ # So we skip canary for those cases.
+ return '' if @sig[:ret].any? {|i| i == '...' }
+ return '' if @expr.blank?
+ return " #{stmt};\n"
+ end
+
+ def inspect
+ sprintf "#<%s %s@%s:%d>", self.class.name, @name, @loc[0], @loc[1]
+ end
+
+ def has_ope? var
+ return @operands.any? {|i| i[:name] == var[:name] }
+ end
+
+ def has_pop? var
+ return @pops.any? {|i| i[:name] == var[:name] }
+ end
+
+ def use_call_data?
+ @use_call_data ||=
+ @variables.find { |_, var_info| var_info[:type] == 'CALL_DATA' }
+ end
+
+ def zjit_profile?
+ @attrs.fetch('zjit_profile').expr.expr != 'false;'
+ end
+
+ private
+
+ def check_attribute_consistency
+ if has_attribute?('sp_inc') \
+ && use_call_data? \
+ && !has_attribute?('comptime_sp_inc')
+ # As the call cache caches information that can only be obtained at
+ # runtime, we do not need it when compiling from AST to bytecode. This
+ # attribute defines an expression that computes the stack pointer
+ # increase based on just the call info to avoid reserving space for the
+ # call cache at compile time. In the expression, all call data operands
+ # are mapped to their call info counterpart. Additionally, all mentions
+ # of `cd` in the operand name are replaced with `ci`.
+ raise "Please define attribute `comptime_sp_inc` for `#{@name}`"
+ end
+ end
+
+ def generate_attribute t, k, v
+ @attrs[k] ||= RubyVM::Attribute.new \
+ insn: self, \
+ name: k, \
+ type: t, \
+ location: [], \
+ expr: v.to_s + ';'
+ return @attrs[k] ||= attr
+ end
+
+ def predefine_attributes
+ # Beware: order matters here because some attribute depends another.
+ generate_attribute 'const char*', 'name', "insn_name(#{bin})"
+ generate_attribute 'enum ruby_vminsn_type', 'bin', bin
+ generate_attribute 'rb_num_t', 'open', operands.size
+ generate_attribute 'rb_num_t', 'popn', pops.size
+ generate_attribute 'rb_num_t', 'retn', rets.size
+ generate_attribute 'rb_num_t', 'width', width
+ generate_attribute 'rb_snum_t', 'sp_inc', rets.size - pops.size
+ generate_attribute 'bool', 'handles_sp', default_definition_of_handles_sp
+ generate_attribute 'bool', 'leaf', default_definition_of_leaf
+ generate_attribute 'bool', 'zjit_profile', false
+ end
+
+ def default_definition_of_handles_sp
+ # Insn with ISEQ should yield it; can handle sp.
+ return operands.any? {|o| o[:type] == 'ISEQ' }
+ end
+
+ def default_definition_of_leaf
+ # Insn that handles SP can never be a leaf.
+ if not has_attribute? 'handles_sp' then
+ return ! default_definition_of_handles_sp
+ elsif handles_sp? then
+ return "! #{call_attribute 'handles_sp'}"
+ else
+ return true
+ end
+ end
+
+ def typesplit a
+ @variables ||= {}
+ a.map do |decl|
+ md = %r'
+ (?<comment> /[*] [^*]* [*]+ (?: [^*/] [^*]* [*]+ )* / ){0}
+ (?<ws> \g<comment> | \s ){0}
+ (?<ident> [_a-zA-Z] [0-9_a-zA-Z]* ){0}
+ (?<type> (?: \g<ident> \g<ws>+ )* \g<ident> ){0}
+ (?<var> \g<ident> ){0}
+ \G \g<ws>* \g<type> \g<ws>+ \g<var>
+ 'x.match(decl)
+ @variables[md['var']] ||= {
+ decl: decl,
+ type: md['type'],
+ name: md['var'],
+ }
+ end
+ end
+
+ @instances = RubyVM::InsnsDef.map {|h|
+ new h.merge(:template => h)
+ }
+
+ def self.find(name)
+ @instances.find do |insn|
+ insn.name == name
+ end or raise IndexError, "instruction not found: #{name}"
+ end
+
+ def self.all
+ @instances
+ end
+end
diff --git a/tool/ruby_vm/models/c_expr.rb b/tool/ruby_vm/models/c_expr.rb
new file mode 100644
index 0000000000..095ff4f1d9
--- /dev/null
+++ b/tool/ruby_vm/models/c_expr.rb
@@ -0,0 +1,44 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/c_escape.rb'
+
+class RubyVM::CExpr
+ include RubyVM::CEscape
+
+ attr_reader :__FILE__, :__LINE__, :expr
+
+ def initialize opts = {}
+ @__FILE__ = opts[:location][0]
+ @__LINE__ = opts[:location][1]
+ @expr = opts[:expr]
+ end
+
+ # blank, in sense of C program.
+ RE = %r'\A{\g<s>*}\z|\A(?<s>\s|/[*][^*]*[*]+([^*/][^*]*[*]+)*/)*\z'
+ if RUBY_VERSION > '2.4' then
+ def blank?
+ RE.match? @expr
+ end
+ else
+ def blank?
+ RE =~ @expr
+ end
+ end
+
+ def inspect
+ if @__LINE__
+ sprintf "#<%s:%d %s>", @__FILE__, @__LINE__, @expr
+ else
+ sprintf "#<%s %s>", @__FILE__, @expr
+ end
+ end
+end
diff --git a/tool/ruby_vm/models/instructions.rb b/tool/ruby_vm/models/instructions.rb
new file mode 100644
index 0000000000..7be7064b14
--- /dev/null
+++ b/tool/ruby_vm/models/instructions.rb
@@ -0,0 +1,23 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative 'bare_instruction'
+require_relative 'operands_unification'
+require_relative 'instructions_unification'
+require_relative 'trace_instruction'
+require_relative 'zjit_instruction'
+
+RubyVM::Instructions = RubyVM::BareInstruction.all +
+ RubyVM::OperandsUnification.all +
+ RubyVM::InstructionsUnification.all +
+ RubyVM::TraceInstruction.all +
+ RubyVM::ZJITInstruction.all
+RubyVM::Instructions.freeze
diff --git a/tool/ruby_vm/models/instructions_unification.rb b/tool/ruby_vm/models/instructions_unification.rb
new file mode 100644
index 0000000000..5c798e6d54
--- /dev/null
+++ b/tool/ruby_vm/models/instructions_unification.rb
@@ -0,0 +1,42 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/c_escape'
+require_relative '../loaders/opt_insn_unif_def'
+require_relative 'bare_instruction'
+
+class RubyVM::InstructionsUnification
+ include RubyVM::CEscape
+
+ attr_reader :name
+
+ def initialize opts = {}
+ @location = opts[:location]
+ @name = namegen opts[:signature]
+ @series = opts[:signature].map do |i|
+ RubyVM::BareInstruction.find(i) # Misshit is fatal
+ end
+ end
+
+ private
+
+ def namegen signature
+ as_tr_cpp ['UNIFIED', *signature].join('_')
+ end
+
+ @instances = RubyVM::OptInsnUnifDef.map do |h|
+ new h
+ end
+
+ def self.all
+ @instances
+ end
+end
diff --git a/tool/ruby_vm/models/operands_unification.rb b/tool/ruby_vm/models/operands_unification.rb
new file mode 100644
index 0000000000..ce118648ca
--- /dev/null
+++ b/tool/ruby_vm/models/operands_unification.rb
@@ -0,0 +1,141 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/c_escape'
+require_relative '../loaders/opt_operand_def'
+require_relative 'bare_instruction'
+
+class RubyVM::OperandsUnification < RubyVM::BareInstruction
+ include RubyVM::CEscape
+
+ attr_reader :preamble, :original, :spec
+
+ def initialize opts = {}
+ name = opts[:signature][0]
+ @original = RubyVM::BareInstruction.find(name)
+ template = @original.template
+ parts = compose opts[:location], opts[:signature], template[:signature]
+ json = template.dup
+ json[:location] = opts[:location]
+ json[:signature] = parts[:signature]
+ json[:name] = parts[:name]
+ @preamble = parts[:preamble]
+ @spec = parts[:spec]
+ super json.merge(:template => template)
+ @konsts = parts[:vars]
+ @konsts.each do |v|
+ @variables[v[:name]] ||= v
+ end
+ end
+
+ def operand_shift_of var
+ before = @original.operands.find_index var
+ after = @operands.find_index var
+ raise "no #{var} for #{@name}" unless before and after
+ return before - after
+ end
+
+ def condition ptr
+ # :FIXME: I'm not sure if this method should be in model?
+ exprs = @spec.each_with_index.map do |(var, val), i|
+ case val when '*' then
+ next nil
+ else
+ type = @original.operands[i][:type]
+ expr = RubyVM::Typemap.typecast_to_VALUE type, val
+ next "#{ptr}[#{i}] == #{expr}"
+ end
+ end
+ exprs.compact!
+ if exprs.size == 1 then
+ return exprs[0]
+ else
+ exprs.map! {|i| "(#{i})" }
+ return exprs.join ' && '
+ end
+ end
+
+ def has_ope? var
+ super or @konsts.any? {|i| i[:name] == var[:name] }
+ end
+
+ private
+
+ def namegen signature
+ insn, argv = *signature
+ wcary = argv.map do |i|
+ case i when '*' then
+ 'WC'
+ else
+ i
+ end
+ end
+ as_tr_cpp [insn, *wcary].join(', ')
+ end
+
+ def compose location, spec, template
+ name = namegen spec
+ *, argv = *spec
+ opes = @original.operands
+ if opes.size != argv.size
+ raise sprintf("operand size mismatch for %s (%s's: %d, given: %d)",
+ name, template[:name], opes.size, argv.size)
+ else
+ src = []
+ mod = []
+ spec = []
+ vars = []
+ argv.each_index do |i|
+ j = argv[i]
+ k = opes[i]
+ spec[i] = [k, j]
+ case j when '*' then
+ # operand is from iseq
+ mod << k[:decl]
+ else
+ # operand is inside C
+ vars << k
+ src << {
+ location: location,
+ expr: " const #{k[:decl]} = #{j};"
+ }
+ end
+ end
+ src.map! {|i| RubyVM::CExpr.new i }
+ return {
+ name: name,
+ signature: {
+ name: name,
+ ope: mod,
+ pop: template[:pop],
+ ret: template[:ret],
+ },
+ preamble: src,
+ vars: vars,
+ spec: spec
+ }
+ end
+ end
+
+ @instances = RubyVM::OptOperandDef.map do |h|
+ new h
+ end
+
+ def self.all
+ @instances
+ end
+
+ def self.each_group
+ all.group_by(&:original).each_pair do |k, v|
+ yield k, v
+ end
+ end
+end
diff --git a/tool/ruby_vm/models/trace_instruction.rb b/tool/ruby_vm/models/trace_instruction.rb
new file mode 100644
index 0000000000..6a3ad53c44
--- /dev/null
+++ b/tool/ruby_vm/models/trace_instruction.rb
@@ -0,0 +1,70 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require_relative '../helpers/c_escape'
+require_relative 'bare_instruction'
+
+class RubyVM::TraceInstruction
+ include RubyVM::CEscape
+
+ attr_reader :name
+
+ def initialize orig
+ @orig = orig
+ @name = as_tr_cpp "trace @ #{@orig.name}"
+ end
+
+ def pretty_name
+ return sprintf "%s(...)(...)(...)", @name
+ end
+
+ def jump_destination
+ return @orig.name
+ end
+
+ def bin
+ return sprintf "BIN(%s)", @name
+ end
+
+ def width
+ return @orig.width
+ end
+
+ def operands_info
+ return @orig.operands_info
+ end
+
+ def rets
+ return ['...']
+ end
+
+ def pops
+ return ['...']
+ end
+
+ def attributes
+ return []
+ end
+
+ def has_attribute? *;
+ return false
+ end
+
+ private
+
+ @instances = (RubyVM::BareInstruction.all +
+ RubyVM::OperandsUnification.all +
+ RubyVM::InstructionsUnification.all).map {|i| new(i) }
+
+ def self.all
+ @instances
+ end
+end
diff --git a/tool/ruby_vm/models/typemap.rb b/tool/ruby_vm/models/typemap.rb
new file mode 100644
index 0000000000..68ef5a41a5
--- /dev/null
+++ b/tool/ruby_vm/models/typemap.rb
@@ -0,0 +1,62 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+RubyVM::Typemap = {
+ "..." => %w[. TS_VARIABLE],
+ "CALL_DATA" => %w[C TS_CALLDATA],
+ "CDHASH" => %w[H TS_CDHASH],
+ "IC" => %w[K TS_IC],
+ "IVC" => %w[A TS_IVC],
+ "ICVARC" => %w[J TS_ICVARC],
+ "ID" => %w[I TS_ID],
+ "ISE" => %w[T TS_ISE],
+ "ISEQ" => %w[S TS_ISEQ],
+ "OFFSET" => %w[O TS_OFFSET],
+ "VALUE" => %w[V TS_VALUE],
+ "lindex_t" => %w[L TS_LINDEX],
+ "rb_insn_func_t" => %w[F TS_FUNCPTR],
+ "rb_num_t" => %w[N TS_NUM],
+ "RB_BUILTIN" => %w[R TS_BUILTIN],
+}
+
+# :FIXME: should this method be here?
+class << RubyVM::Typemap
+ def typecast_from_VALUE type, val
+ # see also iseq_set_sequence()
+ case type
+ when '...'
+ raise "cast not possible: #{val}"
+ when 'VALUE' then
+ return val
+ when 'rb_num_t', 'lindex_t' then
+ return "NUM2LONG(#{val})"
+ when 'ID' then
+ return "SYM2ID(#{val})"
+ else
+ return "(#{type})(#{val})"
+ end
+ end
+
+ def typecast_to_VALUE type, val
+ case type
+ when 'VALUE' then
+ return val
+ when 'ISEQ', 'rb_insn_func_t' then
+ return "(VALUE)(#{val})"
+ when 'rb_num_t', 'lindex_t'
+ "LONG2NUM(#{val})"
+ when 'ID' then
+ return "ID2SYM(#{val})"
+ else
+ raise ":FIXME: TBW for #{type}"
+ end
+ end
+end
diff --git a/tool/ruby_vm/models/zjit_instruction.rb b/tool/ruby_vm/models/zjit_instruction.rb
new file mode 100644
index 0000000000..04764e4c61
--- /dev/null
+++ b/tool/ruby_vm/models/zjit_instruction.rb
@@ -0,0 +1,56 @@
+require_relative '../helpers/c_escape'
+require_relative 'bare_instruction'
+
+# Profile YARV instructions to optimize code generated by ZJIT
+class RubyVM::ZJITInstruction
+ include RubyVM::CEscape
+
+ attr_reader :name
+
+ def initialize(orig)
+ @orig = orig
+ @name = as_tr_cpp "zjit @ #{@orig.name}"
+ end
+
+ def pretty_name
+ return sprintf "%s(...)(...)(...)", @name
+ end
+
+ def jump_destination
+ return @orig.name
+ end
+
+ def bin
+ return sprintf "BIN(%s)", @name
+ end
+
+ def width
+ return @orig.width
+ end
+
+ def operands_info
+ return @orig.operands_info
+ end
+
+ def rets
+ return ['...']
+ end
+
+ def pops
+ return ['...']
+ end
+
+ def attributes
+ return []
+ end
+
+ def has_attribute?(*)
+ return false
+ end
+
+ @instances = RubyVM::BareInstruction.all.filter(&:zjit_profile?).map {|i| new(i) }
+
+ def self.all
+ @instances
+ end
+end
diff --git a/tool/ruby_vm/scripts/converter.rb b/tool/ruby_vm/scripts/converter.rb
new file mode 100644
index 0000000000..4e7c28d67b
--- /dev/null
+++ b/tool/ruby_vm/scripts/converter.rb
@@ -0,0 +1,29 @@
+# This script was needed only once when I converted the old insns.def.
+# Consider historical.
+#
+# ruby converter.rb insns.def | sponge insns.def
+
+BEGIN { $str = ARGF.read }
+END { puts $str }
+
+# deal with spaces
+$str.gsub! %r/\r\n|\r|\n|\z/, "\n"
+$str.gsub! %r/([^\t\n]*)\t/ do
+ x = $1
+ y = 8 - x.length % 8
+ next x + ' ' * y
+end
+$str.gsub! %r/\s+$/, "\n"
+
+# deal with comments
+$str.gsub! %r/@c.*?@e/m, ''
+$str.gsub! %r/@j.*?\*\//m, '*/'
+$str.gsub! %r/\n(\s*\n)+/, "\n\n"
+$str.gsub! %r/\/\*\*?\s*\n\s*/, "/* "
+$str.gsub! %r/\n\s+\*\//, " */"
+$str.gsub! %r/^(?!.*\/\*.+\*\/$)(.+?)\s*\*\//, "\\1\n */"
+
+# deal with sp_inc
+$str.gsub! %r/ \/\/ inc -= (.*)/, ' // inc += -\\1'
+$str.gsub! %r/\s+\/\/ inc \+= (.*)/, "\n// attr rb_snum_t sp_inc = \\1;"
+$str.gsub! %r/;;$/, ";"
diff --git a/tool/ruby_vm/scripts/insns2vm.rb b/tool/ruby_vm/scripts/insns2vm.rb
new file mode 100644
index 0000000000..ad8603b1a8
--- /dev/null
+++ b/tool/ruby_vm/scripts/insns2vm.rb
@@ -0,0 +1,100 @@
+# -*- Ruby -*-
+# -*- frozen_string_literal: true; -*-
+# -*- warn_indent: true; -*-
+#
+# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+#
+# This file is a part of the programming language Ruby. Permission is hereby
+# granted, to either redistribute and/or modify this file, provided that the
+# conditions mentioned in the file COPYING are met. Consult the file for
+# details.
+
+require 'optparse'
+require_relative '../controllers/application_controller.rb'
+
+module RubyVM::Insns2VM
+ def self.router argv
+ options = { destdir: nil, basedir: nil }
+ targets = generate_parser(options).parse argv
+ return targets.map do |i|
+ next ApplicationController.new.generate i, options[:destdir], options[:basedir]
+ end
+ end
+
+ def self.generate_parser(options)
+ OptionParser.new do |this|
+ this.on "-I", "--srcdir=DIR", <<-'end'
+ Historically this option has been passed to the script. This is
+ supposedly because at the beginning the script was placed
+ outside of the ruby source tree. Decades passed since the merge
+ of YARV, now I can safely assume this feature is obsolescent.
+ Just ignore the passed value here.
+ end
+
+ this.on "-L", "--vpath=SPEC", <<-'end'
+ Likewise, this option is no longer supported.
+ end
+
+ this.on "--path-separator=SEP", /\A(?:\W\z|\.(\W).+)/, <<-'end'
+ Old script says this option is a "separator for vpath". I am
+ confident we no longer need this option.
+ end
+
+ this.on "-Dname", "--enable=name[,name...]", Array, <<-'end'
+ This option was used to override VM option that is defined in
+ vm_opts.h. Now it is officially unsupported because vm_opts.h to
+ remain mismatched with this option must break things. Just edit
+ vm_opts.h directly.
+ end
+
+ this.on "-Uname", "--disable=name[,name...]", Array, <<-'end'
+ This option was used to override VM option that is defined in
+ vm_opts.h. Now it is officially unsupported because vm_opts.h to
+ remain mismatched with this option must break things. Just edit
+ vm_opts.h directly.
+ end
+
+ this.on "-i", "--insnsdef=FILE", "--instructions-def", <<-'end'
+ This option was used to specify alternative path to insns.def. For
+ the same reason to ignore -I, we no longer support this.
+ end
+
+ this.on "-o", "--opt-operanddef=FILE", "--opt-operand-def", <<-'end'
+ This option was used to specify alternative path to opt_operand.def.
+ For the same reason to ignore -I, we no longer support this.
+ end
+
+ this.on "-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def", <<-'end'
+ This option was used to specify alternative path to
+ opt_insn_unif.def. For the same reason to ignore -I, we no
+ longer support this.
+ end
+
+ this.on "-C", "--[no-]use-const", <<-'end'
+ We use const whenever possible now so this option is ignored.
+ The author believes that C compilers can constant-fold.
+ end
+
+ this.on "-d", "--destdir", "--output-directory=DIR", <<-'begin' do |dir|
+ THIS IS THE ONLY OPTION THAT WORKS today. Change destination
+ directory from the current working directory to the given path.
+ begin
+ raise "directory was not found in '#{dir}'" unless Dir.exist?(dir)
+ options[:destdir] = dir
+ end
+
+ this.on "--basedir=DIR", <<-'begin' do |dir|
+ Change the base directory from the current working directory
+ to the given path. Used for searching the source template.
+ begin
+ raise "directory was not found in '#{dir}'" unless Dir.exist?(dir)
+ options[:basedir] = dir
+ end
+
+ this.on "-V", "--[no-]verbose", <<-'end'
+ Please let us ignore this and be modest.
+ end
+ end
+ end
+ private_class_method :generate_parser
+end
diff --git a/tool/ruby_vm/views/_attributes.erb b/tool/ruby_vm/views/_attributes.erb
new file mode 100644
index 0000000000..89a89817af
--- /dev/null
+++ b/tool/ruby_vm/views/_attributes.erb
@@ -0,0 +1,35 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%#
+#ifndef RUBY_VM_EXEC_H
+/* can't #include "vm_exec.h" here... */
+typedef long OFFSET;
+typedef unsigned long lindex_t;
+typedef VALUE GENTRY;
+typedef rb_iseq_t *ISEQ;
+#endif
+
+% attrs = RubyVM::Instructions.map(&:attributes).flatten
+%
+% attrs.each do |a|
+PUREFUNC(MAYBE_UNUSED(static <%= a.declaration %>));
+% end
+%
+% attrs.each do |a|
+
+/* <%= a.pretty_name %> */
+<%= a.definition %>
+{
+% str = render_c_expr a.expr
+% case str when /\A#/ then
+ return
+<%= str -%>
+% else
+ return <%= str -%>
+% end
+}
+% end
diff --git a/tool/ruby_vm/views/_c_expr.erb b/tool/ruby_vm/views/_c_expr.erb
new file mode 100644
index 0000000000..4e1b0ec883
--- /dev/null
+++ b/tool/ruby_vm/views/_c_expr.erb
@@ -0,0 +1,17 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%;
+% if expr.blank?
+% # empty
+% elsif ! expr.__LINE__
+<%= expr.expr %>
+% else
+#line <%= expr.__LINE__ %> <%=cstr expr.__FILE__ %>
+<%= expr.expr %>
+#pragma RubyVM reset source
+% end
diff --git a/tool/ruby_vm/views/_comptime_insn_stack_increase.erb b/tool/ruby_vm/views/_comptime_insn_stack_increase.erb
new file mode 100644
index 0000000000..8bb28db1c1
--- /dev/null
+++ b/tool/ruby_vm/views/_comptime_insn_stack_increase.erb
@@ -0,0 +1,71 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%#
+%
+% stack_increase = proc do |i|
+% if i.has_attribute?('sp_inc')
+% '-127'
+% else
+% sprintf("%4d", i.rets.size - i.pops.size)
+% end
+% end
+% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') }
+%
+PUREFUNC(MAYBE_UNUSED(static int comptime_insn_stack_increase(int depth, int insn, const VALUE *opes)));
+PUREFUNC(static rb_snum_t comptime_insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes));
+
+rb_snum_t
+comptime_insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)
+{
+ static const signed char t[] = {
+% insns.each_slice(8) do |row|
+ <%= row.map(&stack_increase).join(', ') -%>,
+% end
+#if USE_ZJIT
+% zjit_insns.each_slice(8) do |row|
+ <%= row.map(&stack_increase).join(', ') -%>,
+% end
+#endif
+ };
+ signed char c = t[insn];
+
+ ASSERT_VM_INSTRUCTION_SIZE(t);
+ if (c != -127) {
+ return c;
+ }
+ else switch(insn) {
+ default:
+ UNREACHABLE;
+% RubyVM::Instructions.each do |i|
+% next unless i.has_attribute?('sp_inc')
+% attr_function =
+% if i.has_attribute?('comptime_sp_inc')
+% "attr_comptime_sp_inc_#{i.name}"
+% else
+% "attr_sp_inc_#{i.name}"
+% end
+ case <%= i.bin %>:
+ return <%= attr_function %>(<%=
+ i.operands.map.with_index do |v, j|
+ if v[:type] == 'CALL_DATA' && i.has_attribute?('comptime_sp_inc')
+ v = v.dup
+ v[:type] = 'CALL_INFO'
+ end
+ i.cast_from_VALUE v, "opes[#{j}]"
+ end.join(", ")
+ %>);
+% end
+ }
+}
+
+int
+comptime_insn_stack_increase(int depth, int insn, const VALUE *opes)
+{
+ enum ruby_vminsn_type itype = (enum ruby_vminsn_type)insn;
+ return depth + (int)comptime_insn_stack_increase_dispatch(itype, opes);
+}
diff --git a/tool/ruby_vm/views/_copyright.erb b/tool/ruby_vm/views/_copyright.erb
new file mode 100644
index 0000000000..2154146f0d
--- /dev/null
+++ b/tool/ruby_vm/views/_copyright.erb
@@ -0,0 +1,31 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%;
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%;
+%;
+%# Below is the licensing term for the generated output, not this erb file.
+/* This is an auto-generated file and is a part of the programming language
+ * Ruby. The person who created a program to generate this file (``I''
+ * hereafter) would like to refrain from defining licensing of this generated
+ * source code.
+ *
+ * This file consists of many small parts of codes copyrighted by each author,
+ * not only the ``I'' person. Those original authors agree with some
+ * open-source license. I believe that the license we agree is the condition
+ * mentioned in the file COPYING. It states "4. You may modify and include
+ * the part of the software into any other software ...". But the problem is,
+ * the license never makes it clear if such modified parts still remain in the
+ * same license, or not. The fact that we agree with the source code's
+ * licensing terms does not automatically define that of generated ones. This
+ * is the reason why this file is under an unclear situation. All what I know
+ * is that above provision guarantees this file to exist.
+ *
+ * Please let me hesitate to declare something about this nuanced contract. I
+ * am not in the position to take over other authors' license to merge into my
+ * one. Changing them to (say) GPLv3 is not doable by myself. Perhaps someday
+ * it might turn out to be okay to say this file is under a license. I wish
+ * the situation would become more clear in the future. */
diff --git a/tool/ruby_vm/views/_insn_entry.erb b/tool/ruby_vm/views/_insn_entry.erb
new file mode 100644
index 0000000000..6ec33461c4
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_entry.erb
@@ -0,0 +1,75 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%;
+% body = render_c_expr(insn.expr).gsub(/^#/, '# ')
+
+/* insn <%= insn.pretty_name %> */
+INSN_ENTRY(<%= insn.name %>)
+{
+ /* ### Declare that we have just entered into an instruction. ### */
+ START_OF_ORIGINAL_INSN(<%= insn.name %>);
+ DEBUG_ENTER_INSN(<%=cstr insn.name %>);
+
+ /* ### Declare and assign variables. ### */
+% insn.preamble.each do |konst|
+<%= render_c_expr konst -%>
+% end
+%
+% insn.operands.each_with_index do |ope, i|
+ <%= ope[:decl] %> = (<%= ope[:type] %>)GET_OPERAND(<%= i + 1 %>);
+% end
+# define INSN_ATTR(x) <%= insn.call_attribute(' ## x ## ') %>
+ const bool MAYBE_UNUSED(leaf) = INSN_ATTR(leaf);
+% insn.pops.reverse_each.with_index.reverse_each do |pop, i|
+ <%= pop[:decl] %> = <%= insn.cast_from_VALUE pop, "TOPN(#{i})"%>;
+% end
+%
+% insn.rets.each do |ret|
+% next if insn.has_ope?(ret) or insn.has_pop?(ret)
+ <%= ret[:decl] %>;
+% end
+
+ /* ### Instruction preambles. ### */
+ ADD_PC(INSN_ATTR(width));
+% if insn.handles_sp?
+ POPN(INSN_ATTR(popn));
+% end
+<%= insn.handle_canary "SETUP_CANARY(leaf)" -%>
+ COLLECT_USAGE_INSN(INSN_ATTR(bin));
+% insn.operands.each_with_index do |ope, i|
+ COLLECT_USAGE_OPERAND(INSN_ATTR(bin), <%= i %>, <%= ope[:name] %>);
+% end
+% unless body.empty?
+
+ /* ### Here we do the instruction body. ### */
+%# NAME_OF_CURRENT_INSN is used in vm_exec.h
+# define NAME_OF_CURRENT_INSN <%= insn.name %>
+<%= body -%>
+# undef NAME_OF_CURRENT_INSN
+% end
+
+ /* ### Instruction trailers. ### */
+ CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, INSN_ATTR(retn));
+<%= insn.handle_canary "CHECK_CANARY(leaf, INSN_ATTR(bin))" -%>
+% if insn.handles_sp?
+% insn.rets.reverse_each do |ret|
+ PUSH(<%= insn.cast_to_VALUE ret %>);
+% end
+% else
+ INC_SP(INSN_ATTR(sp_inc));
+% insn.rets.reverse_each.with_index do |ret, i|
+ TOPN(<%= i %>) = <%= insn.cast_to_VALUE ret %>;
+ VM_ASSERT(!RB_TYPE_P(TOPN(<%= i %>), T_NONE));
+ VM_ASSERT(!RB_TYPE_P(TOPN(<%= i %>), T_MOVED));
+% end
+% end
+# undef INSN_ATTR
+
+ /* ### Leave the instruction. ### */
+ END_INSN(<%= insn.name %>);
+}
diff --git a/tool/ruby_vm/views/_insn_leaf_info.erb b/tool/ruby_vm/views/_insn_leaf_info.erb
new file mode 100644
index 0000000000..f30366ffda
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_leaf_info.erb
@@ -0,0 +1,18 @@
+MAYBE_UNUSED(static bool insn_leaf(int insn, const VALUE *opes));
+static bool
+insn_leaf(int insn, const VALUE *opes)
+{
+ switch (insn) {
+% RubyVM::Instructions.each do |insn|
+% next if insn.is_a?(RubyVM::TraceInstruction) || insn.is_a?(RubyVM::ZJITInstruction)
+ case <%= insn.bin %>:
+ return attr_leaf_<%= insn.name %>(<%=
+ insn.operands.map.with_index do |ope, i|
+ "(#{ope[:type]})opes[#{i}]"
+ end.join(', ')
+ %>);
+% end
+ default:
+ return false;
+ }
+}
diff --git a/tool/ruby_vm/views/_insn_len_info.erb b/tool/ruby_vm/views/_insn_len_info.erb
new file mode 100644
index 0000000000..b29a405918
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_len_info.erb
@@ -0,0 +1,36 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%
+% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') }
+%
+CONSTFUNC(MAYBE_UNUSED(static int insn_len(VALUE insn)));
+
+RUBY_SYMBOL_EXPORT_BEGIN /* for debuggers */
+extern const uint8_t rb_vm_insn_len_info[VM_INSTRUCTION_SIZE];
+RUBY_SYMBOL_EXPORT_END
+
+#ifdef RUBY_VM_INSNS_INFO
+const uint8_t rb_vm_insn_len_info[] = {
+% insns.each_slice(23) do |row|
+ <%= row.map(&:width).join(', ') -%>,
+% end
+#if USE_ZJIT
+% zjit_insns.each_slice(23) do |row|
+ <%= row.map(&:width).join(', ') -%>,
+% end
+#endif
+};
+
+ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_len_info);
+#endif
+
+int
+insn_len(VALUE i)
+{
+ return rb_vm_insn_len_info[i];
+}
diff --git a/tool/ruby_vm/views/_insn_name_info.erb b/tool/ruby_vm/views/_insn_name_info.erb
new file mode 100644
index 0000000000..2862908631
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_name_info.erb
@@ -0,0 +1,59 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%
+% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') }
+%
+% next_offset = 0
+% name_offset = proc do |i|
+% offset = sprintf("%4d", next_offset)
+% next_offset += i.name.length + 1 # insn.name + \0
+% offset
+% end
+%
+CONSTFUNC(MAYBE_UNUSED(static const char *insn_name(VALUE insn)));
+
+RUBY_SYMBOL_EXPORT_BEGIN /* for debuggers */
+extern const int rb_vm_max_insn_name_size;
+extern const char rb_vm_insn_name_base[];
+extern const unsigned short rb_vm_insn_name_offset[VM_INSTRUCTION_SIZE];
+RUBY_SYMBOL_EXPORT_END
+
+#ifdef RUBY_VM_INSNS_INFO
+%# "trace_" is longer than "zjit_", so USE_ZJIT doesn't impact the max name size.
+const int rb_vm_max_insn_name_size = <%= RubyVM::Instructions.map { |i| i.name.size }.max %>;
+
+const char rb_vm_insn_name_base[] =
+% insns.each do |i|
+ <%= cstr i.name %> "\0"
+% end
+#if USE_ZJIT
+% zjit_insns.each do |i|
+ <%= cstr i.name %> "\0"
+% end
+#endif
+ ;
+
+const unsigned short rb_vm_insn_name_offset[] = {
+% insns.each_slice(12) do |row|
+ <%= row.map(&name_offset).join(', ') %>,
+% end
+#if USE_ZJIT
+% zjit_insns.each_slice(12) do |row|
+ <%= row.map(&name_offset).join(', ') %>,
+% end
+#endif
+};
+
+ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_name_offset);
+#endif
+
+const char *
+insn_name(VALUE i)
+{
+ return &rb_vm_insn_name_base[rb_vm_insn_name_offset[i]];
+}
diff --git a/tool/ruby_vm/views/_insn_operand_info.erb b/tool/ruby_vm/views/_insn_operand_info.erb
new file mode 100644
index 0000000000..410869fcd3
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_operand_info.erb
@@ -0,0 +1,69 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%
+% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') }
+%
+% operands_info = proc { |i| sprintf("%-6s", cstr(i.operands_info)) }
+%
+% next_offset = 0
+% op_offset = proc do |i|
+% offset = sprintf("%3d", next_offset)
+% next_offset += i.operands_info.length + 1 # insn.operands_info + \0
+% offset
+% end
+%
+CONSTFUNC(MAYBE_UNUSED(static const char *insn_op_types(VALUE insn)));
+CONSTFUNC(MAYBE_UNUSED(static int insn_op_type(VALUE insn, long pos)));
+
+RUBY_SYMBOL_EXPORT_BEGIN /* for debuggers */
+extern const char rb_vm_insn_op_base[];
+extern const unsigned short rb_vm_insn_op_offset[VM_INSTRUCTION_SIZE];
+RUBY_SYMBOL_EXPORT_END
+
+#ifdef RUBY_VM_INSNS_INFO
+const char rb_vm_insn_op_base[] =
+% insns.each_slice(5) do |row|
+ <%= row.map(&operands_info).join(' "\0" ') %> "\0"
+% end
+#if USE_ZJIT
+% zjit_insns.each_slice(5) do |row|
+ <%= row.map(&operands_info).join(' "\0" ') %> "\0"
+% end
+#endif
+ ;
+
+const unsigned short rb_vm_insn_op_offset[] = {
+% insns.each_slice(12) do |row|
+ <%= row.map(&op_offset).join(', ') %>,
+% end
+#if USE_ZJIT
+% zjit_insns.each_slice(12) do |row|
+ <%= row.map(&op_offset).join(', ') %>,
+% end
+#endif
+};
+
+ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_op_offset);
+#endif
+
+const char *
+insn_op_types(VALUE i)
+{
+ return &rb_vm_insn_op_base[rb_vm_insn_op_offset[i]];
+}
+
+int
+insn_op_type(VALUE i, long j)
+{
+ if (j >= insn_len(i)) {
+ return 0;
+ }
+ else {
+ return insn_op_types(i)[j];
+ }
+}
diff --git a/tool/ruby_vm/views/_insn_type_chars.erb b/tool/ruby_vm/views/_insn_type_chars.erb
new file mode 100644
index 0000000000..27daec6c6d
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_type_chars.erb
@@ -0,0 +1,32 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%
+enum ruby_insn_type_chars {
+% RubyVM::Typemap.each_value do |(c, t)|
+ <%= t %> = '<%= c %>',
+% end
+};
+
+static inline union iseq_inline_storage_entry *
+ISEQ_IS_ENTRY_START(const struct rb_iseq_constant_body *body, char op_type)
+{
+ unsigned int relative_ic_offset = 0;
+ switch (op_type) {
+ case TS_IC:
+ relative_ic_offset += body->ise_size;
+ case TS_ISE:
+ relative_ic_offset += body->icvarc_size;
+ case TS_ICVARC:
+ relative_ic_offset += body->ivc_size;
+ case TS_IVC:
+ break;
+ default:
+ rb_bug("Wrong op type");
+ }
+ return &body->is_entries[relative_ic_offset];
+}
diff --git a/tool/ruby_vm/views/_leaf_helpers.erb b/tool/ruby_vm/views/_leaf_helpers.erb
new file mode 100644
index 0000000000..2756fa2dec
--- /dev/null
+++ b/tool/ruby_vm/views/_leaf_helpers.erb
@@ -0,0 +1,50 @@
+%# -*- C -*-
+%# Copyright (c) 2018 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%;
+#line <%= __LINE__ + 1 %> <%=cstr __FILE__ %>
+
+#include "iseq.h"
+
+static bool
+leafness_of_defined(rb_num_t op_type)
+{
+ /* see also: vm_insnhelper.c:vm_defined() */
+ switch (op_type) {
+ case DEFINED_IVAR:
+ case DEFINED_GVAR:
+ case DEFINED_CVAR:
+ case DEFINED_YIELD:
+ case DEFINED_REF:
+ case DEFINED_ZSUPER:
+ return true;
+ case DEFINED_CONST:
+ case DEFINED_CONST_FROM:
+ /* has rb_autoload_load(); */
+ return false;
+ case DEFINED_FUNC:
+ case DEFINED_METHOD:
+ /* calls #respond_to_missing? */
+ return false;
+ default:
+ rb_bug("unknown operand %ld: blame @shyouhei.", op_type);
+ }
+}
+
+static bool
+leafness_of_checkmatch(rb_num_t flag)
+{
+ /* see also: vm_insnhelper.c:check_match() */
+ if (flag == VM_CHECKMATCH_TYPE_WHEN) {
+ return true;
+ }
+ else {
+ /* has rb_funcallv() */
+ return false;
+ }
+}
+#pragma RubyVM reset source
diff --git a/tool/ruby_vm/views/_notice.erb b/tool/ruby_vm/views/_notice.erb
new file mode 100644
index 0000000000..d17e019727
--- /dev/null
+++ b/tool/ruby_vm/views/_notice.erb
@@ -0,0 +1,22 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%;
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%;
+%;
+/*******************************************************************/
+/*******************************************************************/
+/*******************************************************************/
+/**
+ This file <%= this_file %>.
+
+ ----
+ This file is auto generated by insns2vm.rb
+ DO NOT TOUCH!
+
+ If you want to fix something, you must edit <%= cstr edit %>
+ or tool/insns2vm.rb
+ */
diff --git a/tool/ruby_vm/views/_sp_inc_helpers.erb b/tool/ruby_vm/views/_sp_inc_helpers.erb
new file mode 100644
index 0000000000..740fe10142
--- /dev/null
+++ b/tool/ruby_vm/views/_sp_inc_helpers.erb
@@ -0,0 +1,37 @@
+%# -*- C -*-
+%# Copyright (c) 2018 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%;
+#line <%= __LINE__ + 1 %> <%=cstr __FILE__ %>
+
+static rb_snum_t
+sp_inc_of_sendish(const struct rb_callinfo *ci)
+{
+ /* Send-ish instructions will:
+ *
+ * 1. Pop block argument, if any.
+ * 2. Pop ordinal arguments.
+ * 3. Pop receiver.
+ * 4. Push return value.
+ */
+ const int argb = (vm_ci_flag(ci) & (VM_CALL_ARGS_BLOCKARG | VM_CALL_FORWARDING)) ? 1 : 0;
+ const int argc = vm_ci_argc(ci);
+ const int recv = 1;
+ const int retn = 1;
+
+ /* 1. 2. 3. 4. */
+ return 0 - argb - argc - recv + retn;
+}
+
+static rb_snum_t
+sp_inc_of_invokeblock(const struct rb_callinfo *ci)
+{
+ /* sp_inc of invokeblock is almost identical to that of sendish
+ * instructions, except that it does not pop receiver. */
+ return sp_inc_of_sendish(ci) + 1;
+}
+#pragma RubyVM reset source
diff --git a/tool/ruby_vm/views/_trace_instruction.erb b/tool/ruby_vm/views/_trace_instruction.erb
new file mode 100644
index 0000000000..3588207d39
--- /dev/null
+++ b/tool/ruby_vm/views/_trace_instruction.erb
@@ -0,0 +1,21 @@
+%# -*- C -*-
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%;
+
+/* insn <%= insn.pretty_name %> */
+INSN_ENTRY(<%= insn.name %>)
+{
+ vm_trace(ec, GET_CFP());
+% if insn.name =~
+% /\Atrace_opt_(plus|minus|mult|div|mod|eq|neq|lt|le|gt|ge|ltlt|and|or|aref|aset|length|size|empty_p|nil_p|succ|not|regexpmatch2)\z/
+% jump_dest = "opt_send_without_block"
+% end
+ <%= 'ADD_PC(1);' if insn.name == 'trace_opt_neq' %>
+ DISPATCH_ORIGINAL_INSN(<%= jump_dest || insn.jump_destination %>);
+ END_INSN(<%= insn.name %>);
+}
diff --git a/tool/ruby_vm/views/_zjit_helpers.erb b/tool/ruby_vm/views/_zjit_helpers.erb
new file mode 100644
index 0000000000..1185dbd9d8
--- /dev/null
+++ b/tool/ruby_vm/views/_zjit_helpers.erb
@@ -0,0 +1,31 @@
+#if USE_ZJIT
+
+MAYBE_UNUSED(static int vm_bare_insn_to_zjit_insn(int insn));
+static int
+vm_bare_insn_to_zjit_insn(int insn)
+{
+ switch (insn) {
+% RubyVM::ZJITInstruction.all.each do |insn|
+ case BIN(<%= insn.jump_destination %>):
+ return <%= insn.bin %>;
+% end
+ default:
+ return insn;
+ }
+}
+
+MAYBE_UNUSED(static int vm_zjit_insn_to_bare_insn(int insn));
+static int
+vm_zjit_insn_to_bare_insn(int insn)
+{
+ switch (insn) {
+% RubyVM::ZJITInstruction.all.each do |insn|
+ case <%= insn.bin %>:
+ return BIN(<%= insn.jump_destination %>);
+% end
+ default:
+ return insn;
+ }
+}
+
+#endif
diff --git a/tool/ruby_vm/views/_zjit_instruction.erb b/tool/ruby_vm/views/_zjit_instruction.erb
new file mode 100644
index 0000000000..7fd657697c
--- /dev/null
+++ b/tool/ruby_vm/views/_zjit_instruction.erb
@@ -0,0 +1,12 @@
+#if USE_ZJIT
+
+/* insn <%= insn.pretty_name %> */
+INSN_ENTRY(<%= insn.name %>)
+{
+ START_OF_ORIGINAL_INSN(<%= insn.name %>);
+ rb_zjit_profile_insn(BIN(<%= insn.jump_destination %>), ec);
+ DISPATCH_ORIGINAL_INSN(<%= insn.jump_destination %>);
+ END_INSN(<%= insn.name %>);
+}
+
+#endif
diff --git a/tool/ruby_vm/views/insns.inc.erb b/tool/ruby_vm/views/insns.inc.erb
new file mode 100644
index 0000000000..6521a89b8a
--- /dev/null
+++ b/tool/ruby_vm/views/insns.inc.erb
@@ -0,0 +1,41 @@
+/* -*- C -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%
+% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') }
+%
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'contains YARV instruction list',
+ edit: __FILE__,
+} -%>
+
+#ifndef INSNS_INC
+#define INSNS_INC 1
+
+/* BIN : Basic Instruction Name */
+#define BIN(n) YARVINSN_##n
+
+enum ruby_vminsn_type {
+% insns.each do |i|
+ <%= i.bin %>,
+% end
+#if USE_ZJIT
+% zjit_insns.each do |i|
+ <%= i.bin %>,
+% end
+#endif
+ VM_INSTRUCTION_SIZE
+};
+
+#define VM_BARE_INSTRUCTION_SIZE <%= RubyVM::Instructions.count { |i| i.name !~ /\A(trace|zjit)_/ } %>
+
+#define ASSERT_VM_INSTRUCTION_SIZE(array) \
+ STATIC_ASSERT(numberof_##array, numberof(array) == VM_INSTRUCTION_SIZE)
+
+#endif
diff --git a/tool/ruby_vm/views/insns_info.inc.erb b/tool/ruby_vm/views/insns_info.inc.erb
new file mode 100644
index 0000000000..48dd0e8832
--- /dev/null
+++ b/tool/ruby_vm/views/insns_info.inc.erb
@@ -0,0 +1,26 @@
+/* -*- C -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'contains instruction information for yarv instruction sequence.',
+ edit: __FILE__,
+} %>
+#ifndef INSNS_INFO_INC
+#define INSNS_INFO_INC 1
+<%= render 'insn_type_chars' %>
+<%= render 'insn_name_info' %>
+<%= render 'insn_len_info' %>
+<%= render 'insn_operand_info' %>
+<%= render 'leaf_helpers' %>
+<%= render 'sp_inc_helpers' %>
+<%= render 'zjit_helpers' %>
+<%= render 'attributes' %>
+<%= render 'insn_leaf_info' %>
+<%= render 'comptime_insn_stack_increase' %>
+#endif
diff --git a/tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb b/tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb
new file mode 100644
index 0000000000..793528af5d
--- /dev/null
+++ b/tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb
@@ -0,0 +1,14 @@
+module RubyVM::RJIT # :nodoc: all
+ Instruction = Data.define(:name, :bin, :len, :operands)
+
+ INSNS = {
+% RubyVM::Instructions.each_with_index do |insn, i|
+ <%= i %> => Instruction.new(
+ name: :<%= insn.name %>,
+ bin: <%= i %>, # BIN(<%= insn.name %>)
+ len: <%= insn.width %>, # insn_len
+ operands: <%= (insn.operands unless insn.name.start_with?(/trace_|zjit_/)).inspect %>,
+ ),
+% end
+ }
+end
diff --git a/tool/ruby_vm/views/optinsn.inc.erb b/tool/ruby_vm/views/optinsn.inc.erb
new file mode 100644
index 0000000000..9d9cf0a43a
--- /dev/null
+++ b/tool/ruby_vm/views/optinsn.inc.erb
@@ -0,0 +1,71 @@
+/* -*- C -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+<%= render 'copyright' -%>
+<%= render 'notice', locals: {
+ this_file: 'is for threaded code',
+ edit: __FILE__,
+} -%>
+
+static INSN *
+insn_operands_unification(INSN *iobj)
+{
+#ifdef OPT_OPERANDS_UNIFICATION
+ VALUE *op = iobj->operands;
+
+ switch (iobj->insn_id) {
+ default:
+ /* do nothing */;
+ break;
+
+% RubyVM::OperandsUnification.each_group do |orig, unifs|
+ case <%= orig.bin %>:
+% unifs.each do |insn|
+
+ /* <%= insn.pretty_name %> */
+ if ( <%= insn.condition('op') %> ) {
+% insn.operands.each_with_index do |o, x|
+% n = insn.operand_shift_of(o)
+% if n != 0 then
+ op[<%= x %>] = op[<%= x + n %>];
+% end
+% end
+ iobj->insn_id = <%= insn.bin %>;
+ iobj->operand_size = <%= insn.operands.size %>;
+ break;
+ }
+% end
+
+ break;
+% end
+ }
+#endif
+ return iobj;
+}
+
+int
+rb_insn_unified_local_var_level(VALUE insn)
+{
+#ifdef OPT_OPERANDS_UNIFICATION
+ /* optimize rule */
+ switch (insn) {
+ default:
+ return -1; /* do nothing */;
+% RubyVM::OperandsUnification.each_group do |orig, unifs|
+% unifs.each do|insn|
+ case <%= insn.bin %>:
+% insn.spec.map{|(var,val)|val}.reject{|i| i == '*' }.each do |val|
+ return <%= val %>;
+% break
+% end
+% end
+% end
+ }
+#endif
+ return -1;
+}
diff --git a/tool/ruby_vm/views/optunifs.inc.erb b/tool/ruby_vm/views/optunifs.inc.erb
new file mode 100644
index 0000000000..c096712936
--- /dev/null
+++ b/tool/ruby_vm/views/optunifs.inc.erb
@@ -0,0 +1,18 @@
+/* -*- C -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+% raise ':FIXME:TBW' if RubyVM::VmOptsH['INSTRUCTIONS_UNIFICATION']
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'is for threaded code',
+ edit: __FILE__,
+} -%>
+
+/* Let .bss section automatically initialize this variable */
+/* cf. Section 6.7.8 of ISO/IEC 9899:1999 */
+static const int *const *const unified_insns_data[VM_INSTRUCTION_SIZE];
diff --git a/tool/ruby_vm/views/vm.inc.erb b/tool/ruby_vm/views/vm.inc.erb
new file mode 100644
index 0000000000..38bf5f05ae
--- /dev/null
+++ b/tool/ruby_vm/views/vm.inc.erb
@@ -0,0 +1,34 @@
+/* -*- C -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'is VM main loop',
+ edit: __FILE__,
+} -%>
+
+#include "vm_insnhelper.h"
+% RubyVM::BareInstruction.all.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::OperandsUnification.all.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::InstructionsUnification.all.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::ZJITInstruction.all.each do |insn|
+<%= render 'zjit_instruction', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::TraceInstruction.all.each do |insn|
+<%= render 'trace_instruction', locals: { insn: insn } -%>
+% end
diff --git a/tool/ruby_vm/views/vmtc.inc.erb b/tool/ruby_vm/views/vmtc.inc.erb
new file mode 100644
index 0000000000..39dc8bfa6b
--- /dev/null
+++ b/tool/ruby_vm/views/vmtc.inc.erb
@@ -0,0 +1,29 @@
+/* -*- C -*- */
+
+%# Copyright (c) 2017 Urabe, Shyouhei. All rights reserved.
+%#
+%# This file is a part of the programming language Ruby. Permission is hereby
+%# granted, to either redistribute and/or modify this file, provided that the
+%# conditions mentioned in the file COPYING are met. Consult the file for
+%# details.
+%
+% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') }
+%
+<%= render 'copyright' -%>
+<%= render 'notice', locals: {
+ this_file: 'is for threaded code',
+ edit: __FILE__,
+} -%>
+
+static const void *const insns_address_table[] = {
+% insns.each do |i|
+ LABEL_PTR(<%= i.name %>),
+% end
+#if USE_ZJIT
+% zjit_insns.each do |i|
+ LABEL_PTR(<%= i.name %>),
+% end
+#endif
+};
+
+ASSERT_VM_INSTRUCTION_SIZE(insns_address_table);