summaryrefslogtreecommitdiff
path: root/tool
diff options
context:
space:
mode:
Diffstat (limited to 'tool')
-rwxr-xr-xtool/insns2vm.rb15
-rwxr-xr-xtool/instruction.rb1250
-rw-r--r--tool/ruby_vm/controllers/application_controller.rb24
-rw-r--r--tool/ruby_vm/helpers/c_escape.rb120
-rw-r--r--tool/ruby_vm/helpers/dumper.rb108
-rw-r--r--tool/ruby_vm/helpers/scanner.rb49
-rw-r--r--tool/ruby_vm/loaders/insns_def.rb92
-rw-r--r--tool/ruby_vm/loaders/opt_insn_unif_def.rb34
-rw-r--r--tool/ruby_vm/loaders/opt_operand_def.rb57
-rw-r--r--tool/ruby_vm/loaders/vm_opts_h.rb37
-rw-r--r--tool/ruby_vm/models/attribute.rb44
-rw-r--r--tool/ruby_vm/models/bare_instructions.rb162
-rw-r--r--tool/ruby_vm/models/c_expr.rb41
-rw-r--r--tool/ruby_vm/models/instructions.rb22
-rw-r--r--tool/ruby_vm/models/instructions_unifications.rb43
-rw-r--r--tool/ruby_vm/models/operands_unifications.rb137
-rw-r--r--tool/ruby_vm/models/trace_instructions.rb71
-rw-r--r--tool/ruby_vm/models/typemap.rb61
-rw-r--r--tool/ruby_vm/scripts/insns2vm.rb88
-rw-r--r--tool/ruby_vm/views/_attributes.erb34
-rw-r--r--tool/ruby_vm/views/_c_expr.erb17
-rw-r--r--tool/ruby_vm/views/_copyright.erb31
-rw-r--r--tool/ruby_vm/views/_insn_entry.erb50
-rw-r--r--tool/ruby_vm/views/_insn_len_info.erb20
-rw-r--r--tool/ruby_vm/views/_insn_name_info.erb30
-rw-r--r--tool/ruby_vm/views/_insn_operand_info.erb43
-rw-r--r--tool/ruby_vm/views/_insn_stack_increase.erb53
-rw-r--r--tool/ruby_vm/views/_insn_type_chars.erb12
-rw-r--r--tool/ruby_vm/views/_notice.erb22
-rw-r--r--tool/ruby_vm/views/_trace_instruction.erb16
-rw-r--r--tool/ruby_vm/views/insns.inc.erb26
-rw-r--r--tool/ruby_vm/views/insns_info.inc.erb19
-rw-r--r--tool/ruby_vm/views/opt_sc.inc.erb40
-rw-r--r--tool/ruby_vm/views/optinsn.inc.erb71
-rw-r--r--tool/ruby_vm/views/optunifs.inc.erb21
-rw-r--r--tool/ruby_vm/views/vm.inc.erb30
-rw-r--r--tool/ruby_vm/views/vmtc.inc.erb21
37 files changed, 1751 insertions, 1260 deletions
diff --git a/tool/insns2vm.rb b/tool/insns2vm.rb
index ecbbb52643..e6fe64d189 100755
--- a/tool/insns2vm.rb
+++ b/tool/insns2vm.rb
@@ -3,16 +3,11 @@
# This is used by Makefile.in to generate .inc files.
# See Makefile.in for details.
-require 'optparse'
-
-Version = %w$Revision: 11626 $[1..-1]
-
-require "#{File.join(File.dirname(__FILE__), 'instruction')}"
+require_relative 'ruby_vm/scripts/insns2vm'
if $0 == __FILE__
- opts = ARGV.options
- maker = RubyVM::SourceCodeGenerator.def_options(opts)
- files = opts.parse!
- generator = maker.call
- generator.generate(files)
+ router(ARGV).each do |(path, generator)|
+ str = generator.generate path
+ path.write str, mode: 'wb:utf-8'
+ end
end
diff --git a/tool/instruction.rb b/tool/instruction.rb
deleted file mode 100755
index 21af9a5800..0000000000
--- a/tool/instruction.rb
+++ /dev/null
@@ -1,1250 +0,0 @@
-#!./miniruby
-# -*- coding: us-ascii -*-
-#
-# This library is used by insns2vm.rb as part of autogenerating
-# instruction files with .inc extensions like insns.inc and vm.inc.
-
-require 'erb'
-$:.unshift(File.dirname(__FILE__))
-require 'vpath'
-
-class RubyVM
- class Instruction
- def initialize name, opes, pops, rets, comm, body, tvars, sp_inc,
- orig = self, defopes = [], type = nil,
- nsc = [], psc = [[], []]
-
- @name = name
- @opes = opes # [[type, name], ...]
- @pops = pops # [[type, name], ...]
- @rets = rets # [[type, name], ...]
- @comm = comm # {:c => category, :e => en desc, :j => ja desc}
- @body = body # '...'
-
- @orig = orig
- @defopes = defopes
- @type = type
- @tvars = tvars
-
- @nextsc = nsc
- @pushsc = psc
- @sc = []
- @unifs = []
- @optimized = []
- @is_sc = false
- @sp_inc = sp_inc
- @trace = trace
- end
-
- def add_sc sci
- @sc << sci
- sci.set_sc
- end
-
- attr_reader :name, :opes, :pops, :rets
- attr_reader :body, :comm
- attr_reader :nextsc, :pushsc
- attr_reader :orig, :defopes, :type
- attr_reader :sc
- attr_reader :unifs, :optimized
- attr_reader :is_sc
- attr_reader :tvars
- attr_reader :sp_inc
- attr_accessor :trace
-
- def set_sc
- @is_sc = true
- end
-
- def add_unif insns
- @unifs << insns
- end
-
- def add_optimized insn
- @optimized << insn
- end
-
- def sp_increase_c_expr
- if(pops.any?{|t, v| v == '...'} ||
- rets.any?{|t, v| v == '...'})
- # user definition
- raise "no sp increase definition" if @sp_inc.nil?
- ret = "int inc = 0;\n"
-
- @opes.each_with_index{|(t, v), i|
- if (t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc)) ||
- (@defopes.any?{|t, val| re =~ val})
- ret << " int #{v} = FIX2INT(opes[#{i}]);\n"
- elsif (t == 'CALL_INFO' && ((re = /\b#{v}\b/n) =~ @sp_inc))
- ret << " CALL_INFO #{v} = (CALL_INFO)(opes[#{i}]);\n"
- end
- }
-
- @defopes.each_with_index{|((t, var), val), i|
- if t == 'rb_num_t' && val != '*' && /\b#{var}\b/ =~ @sp_inc
- ret << " #{t} #{var} = #{val};\n"
- end
- }
-
- ret << " #{@sp_inc};\n"
- ret << " return depth + inc;"
- ret
- else
- "return depth + #{rets.size - pops.size};"
- end
- end
-
- def inspect
- "#<Instruction:#{@name}>"
- end
- end
-
- class InstructionsLoader
- def initialize opts = {}
- @insns = []
- @insn_map = {}
-
- @vpath = opts[:VPATH] || File
- @use_const = opts[:use_const]
- @verbose = opts[:verbose]
- @destdir = opts[:destdir]
-
- (@vm_opts = load_vm_opts).each {|k, v|
- @vm_opts[k] = opts[k] if opts.key?(k)
- }
-
- load_insns_def opts[:"insns.def"] || 'insns.def'
-
- load_opt_operand_def opts[:"opope.def"] || 'defs/opt_operand.def'
- load_insn_unification_def opts[:"unif.def"] || 'defs/opt_insn_unif.def'
- make_stackcaching_insns if vm_opt?('STACK_CACHING')
- make_trace_insns
- end
-
- attr_reader :vpath
- attr_reader :destdir
-
- %w[use_const verbose].each do |attr|
- attr_reader attr
- alias_method "#{attr}?", attr
- remove_method attr
- end
-
- def [](s)
- @insn_map[s.to_s]
- end
-
- def each
- @insns.each{|insn|
- yield insn
- }
- end
-
- def size
- @insns.size
- end
-
- ###
- private
-
- def vm_opt? name
- @vm_opts[name]
- end
-
- def load_vm_opts file = nil
- file ||= 'vm_opts.h'
- opts = {}
- vpath.open(file) do |f|
- f.grep(/^\#define\s+OPT_([A-Z_]+)\s+(\d+)/) do
- opts[$1] = !$2.to_i.zero?
- end
- end
- opts
- end
-
- SKIP_COMMENT_PATTERN = Regexp.compile(Regexp.escape('/** ##skip'))
-
- include Enumerable
-
- def add_insn insn
- @insns << insn
- @insn_map[insn.name] = insn
- end
-
- def make_insn name, opes, pops, rets, comm, body, sp_inc
- add_insn Instruction.new(name, opes, pops, rets, comm, body, [], sp_inc)
- end
-
- # str -> [[type, var], ...]
- def parse_vars line
- raise unless /\((.*?)\)/ =~ line
- vars = $1.split(',')
- vars.map!{|v|
- if /\s*(\S+)\s+(\S+)\s*/ =~ v
- type = $1
- var = $2
- elsif /\s*\.\.\.\s*/ =~ v
- type = var = '...'
- else
- raise
- end
- [type, var]
- }
- vars
- end
-
- def parse_comment comm
- c = 'others'
- j = ''
- e = ''
- comm.each_line{|line|
- case line
- when /@c (.+)/
- c = $1
- when /@e (.+)/
- e = $1
- when /@e\s*$/
- e = ''
- when /@j (.+)$/
- j = $1
- when /@j\s*$/
- j = ''
- end
- }
- { :c => c,
- :e => e,
- :j => j,
- }
- end
-
- def load_insns_def file
- body = insn = opes = pops = rets = nil
- comment = ''
-
- vpath.open(file) {|f|
- f.instance_variable_set(:@line_no, 0)
- class << f
- def line_no
- @line_no
- end
- def gets
- @line_no += 1
- super
- end
- end
-
- while line = f.gets
- line.chomp!
- case line
-
- when SKIP_COMMENT_PATTERN
- while line = f.gets.chomp
- if /\s+\*\/$/ =~ line
- break
- end
- end
-
- # collect instruction comment
- when /^\/\*\*$/
- while line = f.gets
- if /\s+\*\/\s*$/ =~ line
- break
- else
- comment << line
- end
- end
-
- # start instruction body
- when /^DEFINE_INSN$/
- insn = f.gets.chomp
- opes = parse_vars(f.gets.chomp)
- pops = parse_vars(f.gets.chomp).reverse
- rets_str = f.gets.chomp
- rets = parse_vars(rets_str).reverse
- comment = parse_comment(comment)
- insn_in = true
- body = ''
-
-
- when /^\/\/ attr rb_snum_t sp_inc = (.+)$/
- sp_inc = 'inc +=' + $1
-
- when /^\{$/
- line_no = f.line_no
-
- # end instruction body
- when /^\}/
- if insn_in
- body.instance_variable_set(:@line_no, line_no)
- body.instance_variable_set(:@file, f.path)
- insn = make_insn(insn, opes, pops, rets, comment, body, sp_inc)
- insn_in = false
- comment = ''
- end
-
- else
- if insn_in
- body << line + "\n"
- end
- end
- end
- }
- end
-
- ## opt op
- def load_opt_operand_def file
- vpath.foreach(file) {|line|
- line = line.gsub(/\#.*/, '').strip
- next if line.length == 0
- break if /__END__/ =~ line
- /(\S+)\s+(.+)/ =~ line
- insn = $1
- opts = $2
- add_opt_operand insn, opts.split(/,/).map{|e| e.strip}
- } if file
- end
-
- def label_escape label
- label.gsub(/\(/, '_O_').
- gsub(/\)/, '_C_').
- gsub(/\*/, '_WC_')
- end
-
- def add_opt_operand insn_name, opts
- insn = @insn_map[insn_name]
- opes = insn.opes
-
- if opes.size != opts.size
- raise "operand size mismatch for #{insn.name} (opes: #{opes.size}, opts: #{opts.size})"
- end
-
- ninsn = insn.name + '_OP_' + opts.map{|e| label_escape(e)}.join('_')
- nopes = []
- defv = []
-
- opts.each_with_index{|e, i|
- if e == '*'
- nopes << opes[i]
- end
- defv << [opes[i], e]
- }
-
- make_insn_operand_optimized(insn, ninsn, nopes, defv)
- end
-
- def make_insn_operand_optimized orig_insn, name, opes, defopes
- comm = orig_insn.comm.dup
- comm[:c] = 'optimize'
- add_insn insn = Instruction.new(
- name, opes, orig_insn.pops, orig_insn.rets, comm,
- orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
- orig_insn, defopes)
- orig_insn.add_optimized insn
- end
-
- ## insn unif
- def load_insn_unification_def file
- vpath.foreach(file) {|line|
- line = line.gsub(/\#.*/, '').strip
- next if line.length == 0
- break if /__END__/ =~ line
- make_unified_insns line.split.map{|e|
- raise "unknown insn: #{e}" unless @insn_map[e]
- @insn_map[e]
- }
- } if file
- end
-
- def all_combination sets
- ret = sets.shift.map{|e| [e]}
-
- sets.each{|set|
- prev = ret
- ret = []
- prev.each{|ary|
- set.each{|e|
- eary = ary.dup
- eary << e
- ret << eary
- }
- }
- }
- ret
- end
-
- def make_unified_insns insns
- if vm_opt?('UNIFY_ALL_COMBINATION')
- insn_sets = insns.map{|insn|
- [insn] + insn.optimized
- }
-
- all_combination(insn_sets).each{|insns_set|
- make_unified_insn_each insns_set
- }
- else
- make_unified_insn_each insns
- end
- end
-
- def mk_private_val vals, i, redef
- vals.dup.map{|v|
- # v[0] : type
- # v[1] : var name
-
- v = v.dup
- if v[0] != '...'
- redef[v[1]] = v[0]
- v[1] = "#{v[1]}_#{i}"
- end
- v
- }
- end
-
- def mk_private_val2 vals, i, redef
- vals.dup.map{|v|
- # v[0][0] : type
- # v[0][1] : var name
- # v[1] : default val
-
- pv = v.dup
- v = pv[0] = pv[0].dup
- if v[0] != '...'
- redef[v[1]] = v[0]
- v[1] = "#{v[1]}_#{i}"
- end
- pv
- }
- end
-
- def make_unified_insn_each insns
- names = []
- opes = []
- pops = []
- rets = []
- comm = {
- :c => 'optimize',
- :e => 'unified insn',
- :j => 'unified insn',
- }
- body = ''
- passed = []
- tvars = []
- defopes = []
- sp_inc = ''
-
- insns.each_with_index{|insn, i|
- names << insn.name
- redef_vars = {}
-
- e_opes = mk_private_val(insn.opes, i, redef_vars)
- e_pops = mk_private_val(insn.pops, i, redef_vars)
- e_rets = mk_private_val(insn.rets, i, redef_vars)
- # ToDo: fix it
- e_defs = mk_private_val2(insn.defopes, i, redef_vars)
-
- passed_vars = []
- while pvar = e_pops.pop
- rvar = rets.pop
- if rvar
- raise "unsupported unif insn: #{insns.inspect}" if rvar[0] == '...'
- passed_vars << [pvar, rvar]
- tvars << rvar
- else
- e_pops.push pvar
- break
- end
- end
-
- opes.concat e_opes
- pops.concat e_pops
- rets.concat e_rets
- defopes.concat e_defs
- sp_inc << "#{insn.sp_inc}"
-
- body << "{ /* unif: #{i} */\n" +
- passed_vars.map{|rpvars|
- pv = rpvars[0]
- rv = rpvars[1]
- "#define #{pv[1]} #{rv[1]}"
- }.join("\n") +
- "\n" +
- redef_vars.map{|v, type|
- "#{type} #{v} = #{v}_#{i};"
- }.join("\n") + "\n"
- if line = insn.body.instance_variable_get(:@line_no)
- file = insn.body.instance_variable_get(:@file)
- body << "#line #{line+1} \"#{file}\"\n"
- body << insn.body
- body << "\n#line __CURRENT_LINE__ \"__CURRENT_FILE__\"\n"
- else
- body << insn.body
- end
- body << redef_vars.keys.map{|v|
- "#{v}_#{i} = #{v};"
- }.join("\n") +
- "\n" +
- passed_vars.map{|rpvars|
- "#undef #{rpvars[0][1]}"
- }.join("\n") +
- "\n}\n"
- }
-
- tvars_ary = []
- tvars.each{|tvar|
- unless opes.any?{|var|
- var[1] == tvar[1]
- } || defopes.any?{|pvar|
- pvar[0][1] == tvar[1]
- }
- tvars_ary << tvar
- end
- }
- add_insn insn = Instruction.new("UNIFIED_" + names.join('_'),
- opes, pops, rets.reverse, comm, body,
- tvars_ary, sp_inc)
- insn.defopes.replace defopes
- insns[0].add_unif [insn, insns]
- end
-
- ## sc
- SPECIAL_INSN_FOR_SC_AFTER = {
- /\Asend/ => [:a],
- /\Aend/ => [:a],
- /\Ayield/ => [:a],
- /\Aclassdef/ => [:a],
- /\Amoduledef/ => [:a],
- }
- FROM_SC = [[], [:a], [:b], [:a, :b], [:b, :a]]
-
- def make_stackcaching_insns
- pops = rets = nil
-
- @insns.dup.each{|insn|
- opops = insn.pops
- orets = insn.rets
- oopes = insn.opes
- ocomm = insn.comm
- oname = insn.name
-
- after = SPECIAL_INSN_FOR_SC_AFTER.find {|k, v| k =~ oname}
-
- insns = []
- FROM_SC.each{|from|
- name, pops, rets, pushs1, pushs2, nextsc =
- *calc_stack(insn, from, after, opops, orets)
-
- make_insn_sc(insn, name, oopes, pops, rets, [pushs1, pushs2], nextsc)
- }
- }
- end
-
- def make_trace_insns
- @insns.dup.each{|insn|
- body = <<-EOS
- vm_trace(ec, GET_CFP(), GET_PC());
- DISPATCH_ORIGINAL_INSN(#{insn.name});
- EOS
-
- trace_insn = Instruction.new(name = "trace_#{insn.name}",
- insn.opes, insn.pops, insn.rets, insn.comm,
- body, insn.tvars, insn.sp_inc)
- trace_insn.trace = true
- add_insn trace_insn
- }
- end
-
- def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc
- comm = orig_insn.comm.dup
- comm[:c] = 'optimize(sc)'
-
- scinsn = Instruction.new(
- name, opes, pops, rets, comm,
- orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
- orig_insn, orig_insn.defopes, :sc, nextsc, pushs)
-
- add_insn scinsn
- orig_insn.add_sc scinsn
- end
-
- def self.complement_name st
- "#{st[0] ? st[0] : 'x'}#{st[1] ? st[1] : 'x'}"
- end
-
- def add_stack_value st
- len = st.length
- if len == 0
- st[0] = :a
- [nil, :a]
- elsif len == 1
- if st[0] == :a
- st[1] = :b
- else
- st[1] = :a
- end
- [nil, st[1]]
- else
- st[0], st[1] = st[1], st[0]
- [st[1], st[1]]
- end
- end
-
- def calc_stack insn, ofrom, oafter, opops, orets
- from = ofrom.dup
- pops = opops.dup
- rets = orets.dup
- rest_scr = ofrom.dup
-
- pushs_before = []
- pushs= []
-
- pops.each_with_index{|e, i|
- if e[0] == '...'
- pushs_before = from
- from = []
- end
- r = from.pop
- break unless r
- pops[i] = pops[i].dup << r
- }
-
- if oafter
- from = oafter
- from.each_with_index{|r, i|
- rets[i] = rets[i].dup << r if rets[i]
- }
- else
- rets = rets.reverse
- rets.each_with_index{|e, i|
- break if e[0] == '...'
- pushed, r = add_stack_value from
- rets[i] = rets[i].dup << r
- if pushed
- if rest_scr.pop
- pushs << pushed
- end
-
- if i - 2 >= 0
- rets[i-2].pop
- end
- end
- }
- end
-
- if false #|| insn.name =~ /test3/
- p ofrom
- p pops
- p rets
- p pushs_before
- p pushs
- p from
- exit
- end
-
- ret = ["#{insn.name}_SC_#{InstructionsLoader.complement_name(ofrom)}_#{complement_name(from)}",
- pops, rets, pushs_before, pushs, from]
- end
- end
-
- class SourceCodeGenerator
- def initialize insns
- @insns = insns
- end
-
- attr_reader :insns
-
- def generate
- raise "should not reach here"
- end
-
- def vpath
- @insns.vpath
- end
-
- def verbose?
- @insns.verbose?
- end
-
- def use_const?
- @insns.use_const?
- end
-
- def template(name)
- ERB.new(vpath.read("template/#{name}"), nil, '%-')
- end
-
- def build_string
- @lines = []
- yield
- @lines.join("\n")
- end
-
- EMPTY_STRING = ''.freeze
-
- def commit str = EMPTY_STRING
- @lines << str
- end
-
- def comment str
- @lines << str if verbose?
- end
-
- def output_path(fn)
- d = @insns.destdir
- fn = File.join(d, fn) if d
- fn
- end
- end
-
- ###################################################################
- # vm.inc
- class VmBodyGenerator < SourceCodeGenerator
- # vm.inc
- def generate
- template('vm.inc.tmpl').result(binding)
- end
-
- def generate_from_insnname insnname
- make_insn_def @insns[insnname.to_s]
- end
-
- #######
- private
-
- def make_header_prepare_stack insn
- comment " /* prepare stack status */"
-
- push_ba = insn.pushsc
- raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0
-
- n = 0
- push_ba.each {|pushs| n += pushs.length}
- commit " CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, #{n});" if n > 0
- push_ba.each{|pushs|
- pushs.each{|r|
- commit " PUSH(SCREG(#{r}));"
- }
- }
- end
-
- def make_header_operands insn
- comment " /* declare and get from iseq */"
-
- vars = insn.opes
- n = 0
- ops = []
-
- vars.each_with_index{|(type, var), i|
- if type == '...'
- break
- end
-
- # skip make operands when body has no reference to this operand
- # TODO: really needed?
- re = /\b#{var}\b/n
- if re =~ insn.body or re =~ insn.sp_inc or insn.rets.any?{|t, v| re =~ v} or re =~ 'ic' or re =~ 'ci' or re =~ 'cc'
- ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});"
- end
-
- n += 1
- }
- @opn = n
-
- # reverse or not?
- # ops.join
- commit ops.reverse
- end
-
- def make_header_default_operands insn
- vars = insn.defopes
-
- vars.each{|e|
- next if e[1] == '*'
- if use_const?
- commit " const #{e[0][0]} #{e[0][1]} = #{e[1]};"
- else
- commit " #define #{e[0][1]} #{e[1]}"
- end
- }
- end
-
- def make_footer_default_operands insn
- comment " /* declare and initialize default opes */"
- if use_const?
- commit
- else
- vars = insn.defopes
-
- vars.each{|e|
- next if e[1] == '*'
- commit "#undef #{e[0][1]}"
- }
- end
- end
-
- def make_header_stack_pops insn
- comment " /* declare and pop from stack */"
-
- n = 0
- pops = []
- vars = insn.pops
- vars.each_with_index{|iter, i|
- type, var, r = *iter
- if type == '...'
- break
- end
- if r
- pops << " #{type} #{var} = SCREG(#{r});"
- else
- pops << " #{type} #{var} = TOPN(#{n});"
- n += 1
- end
- }
- @popn = n
-
- # reverse or not?
- commit pops.reverse
- end
-
- def make_header_temporary_vars insn
- comment " /* declare temporary vars */"
-
- insn.tvars.each{|var|
- commit " #{var[0]} #{var[1]};"
- }
- end
-
- def make_header_stack_val insn
- comment "/* declare stack push val */"
-
- vars = insn.opes + insn.pops + insn.defopes.map{|e| e[0]}
-
- insn.rets.each{|var|
- if vars.all?{|e| e[1] != var[1]} && var[1] != '...'
- commit " #{var[0]} #{var[1]};"
- end
- }
- end
-
- def make_header_analysis insn
- commit " COLLECT_USAGE_INSN(BIN(#{insn.name}));"
- insn.opes.each_with_index{|op, i|
- commit " COLLECT_USAGE_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});"
- }
- end
-
- def make_header_pc insn
- commit " ADD_PC(1+#{@opn});"
- commit " PREFETCH(GET_PC());"
- end
-
- def make_header_popn insn
- comment " /* management */"
- commit " POPN(#{@popn});" if @popn > 0
- end
-
- def make_header_debug insn
- comment " /* for debug */"
- commit " DEBUG_ENTER_INSN(\"#{insn.name}\");"
- end
-
- def make_header_defines insn
- commit " #define CURRENT_INSN_#{insn.name} 1"
- commit " #define INSN_IS_SC() #{insn.sc ? 0 : 1}"
- commit " #define INSN_LABEL(lab) LABEL_#{insn.name}_##lab"
- commit " #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}"
- end
-
- def each_footer_stack_val insn
- insn.rets.reverse_each{|v|
- break if v[1] == '...'
- yield v
- }
- end
-
- def make_footer_stack_val insn
- comment " /* push stack val */"
-
- n = 0
- each_footer_stack_val(insn){|v|
- n += 1 unless v[2]
- }
- commit " CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, #{n});" if n > 0
- each_footer_stack_val(insn){|v|
- if v[2]
- commit " SCREG(#{v[2]}) = #{v[1]};"
- else
- commit " PUSH(#{v[1]});"
- end
- }
- end
-
- def make_footer_undefs insn
- commit "#undef CURRENT_INSN_#{insn.name}"
- commit "#undef INSN_IS_SC"
- commit "#undef INSN_LABEL"
- commit "#undef LABEL_IS_SC"
- end
-
- def make_header insn
- label = insn.trace ? '' : "START_OF_ORIGINAL_INSN(#{insn.name});"
- commit "INSN_ENTRY(#{insn.name}){#{label}"
- make_header_prepare_stack insn
- commit "{"
- unless insn.trace
- make_header_stack_val insn
- make_header_default_operands insn
- make_header_operands insn
- make_header_stack_pops insn
- make_header_temporary_vars insn
- #
- make_header_debug insn
- make_header_pc insn
- make_header_popn insn
- make_header_defines insn
- make_header_analysis insn
- end
- commit "{"
- end
-
- def make_footer insn
- unless insn.trace
- make_footer_stack_val insn
- make_footer_default_operands insn
- make_footer_undefs insn
- end
- commit " END_INSN(#{insn.name});}}}"
- end
-
- def make_insn_def insn
- build_string do
- make_header insn
- if line = insn.body.instance_variable_get(:@line_no)
- file = insn.body.instance_variable_get(:@file)
- commit "#line #{line+1} \"#{file}\""
- commit insn.body
- commit '#line __CURRENT_LINE__ "__CURRENT_FILE__"'
- else
- commit insn.body
- end
- make_footer(insn)
- end
- end
- end
-
- ###################################################################
- # vmtc.inc
- class VmTCIncGenerator < SourceCodeGenerator
- def generate
- template('vmtc.inc.tmpl').result(binding)
- end
- end
-
- ###################################################################
- # insns_info.inc
- class InsnsInfoIncGenerator < SourceCodeGenerator
- def generate
- template('insns_info.inc.tmpl').result(binding)
- end
-
- ###
- private
-
- def op2typesig op
- case op
- when /^OFFSET/
- "TS_OFFSET"
- when /^rb_num_t/
- "TS_NUM"
- when /^lindex_t/
- "TS_LINDEX"
- when /^VALUE/
- "TS_VALUE"
- when /^ID/
- "TS_ID"
- when /GENTRY/
- "TS_GENTRY"
- when /^IC/
- "TS_IC"
- when /^CALL_INFO/
- "TS_CALLINFO"
- when /^CALL_CACHE/
- "TS_CALLCACHE"
- when /^\.\.\./
- "TS_VARIABLE"
- when /^CDHASH/
- "TS_CDHASH"
- when /^ISEQ/
- "TS_ISEQ"
- when /rb_insn_func_t/
- "TS_FUNCPTR"
- else
- raise "unknown op type: #{op}"
- end
- end
-
- TYPE_CHARS = {
- 'TS_OFFSET' => 'O',
- 'TS_NUM' => 'N',
- 'TS_LINDEX' => 'L',
- 'TS_VALUE' => 'V',
- 'TS_ID' => 'I',
- 'TS_GENTRY' => 'G',
- 'TS_IC' => 'K',
- 'TS_CALLINFO' => 'C',
- 'TS_CALLCACHE' => 'E',
- 'TS_CDHASH' => 'H',
- 'TS_ISEQ' => 'S',
- 'TS_VARIABLE' => '.',
- 'TS_FUNCPTR' => 'F',
- }
-
- def max_length(array)
- max = 0
- array.each do |i|
- if (n = i.length) > max
- max = n
- end
- end
- max
- end
- end
-
- ###################################################################
- # insns.inc
- class InsnsIncGenerator < SourceCodeGenerator
- def generate
- template('insns.inc.tmpl').result(binding)
- end
- end
-
- ###################################################################
- # minsns.inc
- class MInsnsIncGenerator < SourceCodeGenerator
- def generate
- template('minsns.inc.tmpl').result(binding)
- end
- end
-
- ###################################################################
- # optinsn.inc
- class OptInsnIncGenerator < SourceCodeGenerator
- def generate
- optinsn_inc
- end
-
- ###
- private
-
- def val_as_type op
- type = op[0][0]
- val = op[1]
-
- case type
- when /^long/, /^rb_num_t/, /^lindex_t/
- "INT2FIX(#{val})"
- when /^VALUE/
- val
- when /^ID/
- "INT2FIX(#{val})"
- when /^ISEQ/, /^rb_insn_func_t/
- val
- when /GENTRY/
- raise
- when /^\.\.\./
- raise
- else
- raise "type: #{type}"
- end
- end
-
- # optinsn.inc
- def optinsn_inc
- opt_insns_map = Hash.new{[]}
-
- @insns.each{|insn|
- next if insn.defopes.size == 0
- next if insn.type == :sc
- next if /^UNIFIED/ =~ insn.name.to_s
-
- originsn = insn.orig
- opt_insns_map[originsn] <<= insn
- }
-
- opt_insns_map.each_value do |optinsns|
- sorted = optinsns.sort_by {|opti|
- opti.defopes.find_all{|e| e[1] == '*'}.size
- }
- optinsns.replace(sorted)
- end
-
- template('optinsn.inc.tmpl').result(binding)
- end
- end
-
- ###################################################################
- # optunifs.inc
- class OptUnifsIncGenerator < SourceCodeGenerator
- def generate
- template('optunifs.inc.tmpl').result(binding)
- end
- end
-
- ###################################################################
- # opt_sc.inc
- class OptSCIncGenerator < SourceCodeGenerator
- def generate
- sc_insn_info = []
- @insns.each{|insn|
- insns = insn.sc
- if insns.size > 0
- insns = ['SC_ERROR'] + insns.map{|e| " BIN(#{e.name})"}
- else
- insns = Array.new(6){'SC_ERROR'}
- end
- sc_insn_info << " {\n#{insns.join(",\n")}}"
- }
- sc_insn_info = sc_insn_info.join(",\n")
-
- sc_insn_next = @insns.map{|insn|
- " SCS_#{InstructionsLoader.complement_name(insn.nextsc).upcase}" +
- (verbose? ? " /* #{insn.name} */" : '')
- }.join(",\n")
- template('opt_sc.inc.tmpl').result(binding)
- end
- end
-
- ###################################################################
- # yasmdata.rb
- class YASMDataRbGenerator < SourceCodeGenerator
- def generate
- insn_id2no = ''
- @insns.each_with_index{|insn, i|
- insn_id2no << " :#{insn.name} => #{i},\n"
- }
- template('yasmdata.rb').result(binding)
- end
- end
-
- ###################################################################
- # yarvarch.*
- class YARVDocGenerator < SourceCodeGenerator
- def generate
-
- end
-
- def desc lang
- d = ''
- i = 0
- cat = nil
- @insns.each{|insn|
- seq = insn.opes.map{|t,v| v}.join(' ')
- before = insn.pops.reverse.map{|t,v| v}.join(' ')
- after = insn.rets.reverse.map{|t,v| v}.join(' ')
-
- if cat != insn.comm[:c]
- d << "** #{insn.comm[:c]}\n\n"
- cat = insn.comm[:c]
- end
-
- d << "*** #{insn.name}\n"
- d << "\n"
- d << insn.comm[lang] << "\n\n"
- d << ":instruction sequence: 0x%02x " % i << seq << "\n"
- d << ":stack: #{before} => #{after}\n\n"
- i+=1
- }
- d
- end
-
- def desc_ja
- d = desc :j
- template('yarvarch.ja').result(binding)
- end
-
- def desc_en
- d = desc :e
- template('yarvarch.en').result(binding)
- end
- end
-
- class SourceCodeGenerator
- Files = { # codes
- 'vm.inc' => VmBodyGenerator,
- 'vmtc.inc' => VmTCIncGenerator,
- 'insns.inc' => InsnsIncGenerator,
- 'insns_info.inc' => InsnsInfoIncGenerator,
- # 'minsns.inc' => MInsnsIncGenerator,
- 'optinsn.inc' => OptInsnIncGenerator,
- 'optunifs.inc' => OptUnifsIncGenerator,
- 'opt_sc.inc' => OptSCIncGenerator,
- 'yasmdata.rb' => YASMDataRbGenerator,
- }
-
- def generate args = []
- args = Files.keys if args.empty?
- args.each{|fn|
- s = Files[fn].new(@insns).generate
- open(output_path(fn), 'w') {|f| f.puts(s)}
- }
- end
-
- def self.def_options(opt)
- opts = {
- :"insns.def" => 'insns.def',
- :"opope.def" => 'defs/opt_operand.def',
- :"unif.def" => 'defs/opt_insn_unif.def',
- }
-
- opt.on("-Dname", /\AOPT_(\w+)\z/, "enable VM option") {|s, v|
- opts[v] = true
- }
- opt.on("--enable=name[,name...]", Array,
- "enable VM options (without OPT_ prefix)") {|*a|
- a.each {|v| opts[v] = true}
- }
- opt.on("-Uname", /\AOPT_(\w+)\z/, "disable VM option") {|s, v|
- opts[v] = false
- }
- opt.on("--disable=name[,name...]", Array,
- "disable VM options (without OPT_ prefix)") {|*a|
- a.each {|v| opts[v] = false}
- }
- opt.on("-i", "--insnsdef=FILE", "--instructions-def",
- "instructions definition file") {|n|
- opts[:insns_def] = n
- }
- opt.on("-o", "--opt-operanddef=FILE", "--opt-operand-def",
- "vm option: operand definition file") {|n|
- opts[:opope_def] = n
- }
- opt.on("-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def",
- "vm option: instruction unification file") {|n|
- opts[:unif_def] = n
- }
- opt.on("-C", "--[no-]use-const",
- "use consts for default operands instead of macros") {|v|
- opts[:use_const] = v
- }
- opt.on("-d", "--destdir", "--output-directory=DIR",
- "make output file underneath DIR") {|v|
- opts[:destdir] = v
- }
- opt.on("-V", "--[no-]verbose") {|v|
- opts[:verbose] = v
- }
-
- vpath = VPath.new
- vpath.def_options(opt)
-
- proc {
- opts[:VPATH] = vpath
- build opts
- }
- end
-
- def self.build opts, vpath = ['./']
- opts[:VPATH] ||= VPath.new(*vpath)
- self.new InstructionsLoader.new(opts)
- end
- end
-end
diff --git a/tool/ruby_vm/controllers/application_controller.rb b/tool/ruby_vm/controllers/application_controller.rb
new file mode 100644
index 0000000000..bb86873d25
--- /dev/null
+++ b/tool/ruby_vm/controllers/application_controller.rb
@@ -0,0 +1,24 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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
+ path = Pathname.new i
+ dumper = RubyVM::Dumper.new i
+ 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..088df1562e
--- /dev/null
+++ b/tool/ruby_vm/helpers/c_escape.rb
@@ -0,0 +1,120 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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
+ return "/* #{str.strip.b.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/./n, 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
diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb
new file mode 100644
index 0000000000..8f14d90d1f
--- /dev/null
+++ b/tool/ruby_vm/helpers/dumper.rb
@@ -0,0 +1,108 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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
+
+ # I learned this handy "super-private" maneuver from @a_matsuda
+ # cf: https://github.com/rails/rails/pull/27363/files
+ using Module.new {
+ refine RubyVM::Dumper do
+ 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 __dir__
+ path += '../views'
+ path += spec
+ src = path.read mode: 'rt:utf-8:utf-8'
+ rescue Errno::ENOENT
+ raise "don't know how to generate #{path}"
+ else
+ erb = ERB.new src, nil, '%-'
+ erb.filename = path.realpath.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 str == "#pragma RubyVM reset source\n" then
+ return "#line #{lineno + 2} #{@file}\n"
+ else
+ return str
+ end
+ end
+
+ public
+
+ def do_render source, locals
+ erb = finderb source
+ bnd = @empty.dup
+ locals.each_pair do |k, v|
+ bnd.local_variable_set 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
+ end
+ }
+
+ def initialize path
+ @erb = {}
+ @empty = new_binding
+ dst = Pathname.new Dir.getwd
+ dst += path
+ @file = cstr dst.realdirpath.to_path
+ end
+
+ def render partial, locals: {}
+ return do_render "_#{partial}.erb", 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..3dce6ffdfe
--- /dev/null
+++ b/tool/ruby_vm/helpers/scanner.rb
@@ -0,0 +1,49 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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 'strscan'
+
+class RubyVM::Scanner
+ attr_reader :__FILE__
+ attr_reader :__LINE__
+
+ def initialize path
+ src = Pathname.new __dir__
+ src += path
+ @__LINE__ = 1
+ @__FILE__ = src.realpath.to_path
+ str = src.read mode: 'rt:utf-8:utf-8'
+ @scanner = StringScanner.new str
+ end
+
+ def eos?
+ @scanner.eos?
+ end
+
+ def scan re
+ ret = @__LINE__
+ match = @scanner.scan re
+ return unless match
+ @__LINE__ += match.count "\n"
+ return ret
+ end
+
+ def scan! re
+ scan re or raise sprintf "parse error at %s:%d near:\n %s...", \
+ @__FILE__, @__LINE__, @scanner.peek(32)
+ end
+
+ def [] key
+ return @scanner[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..58748c3ca6
--- /dev/null
+++ b/tool/ruby_vm/loaders/insns_def.rb
@@ -0,0 +1,92 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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 '../../../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 \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(/#{grammar}\g<ws>+/o)
+ split = -> (v) {
+ case v when /\Avoid\z/ then
+ []
+ else
+ v.split(/, */)
+ end
+ }
+
+ l1 = scanner.scan!(/#{grammar}\g<insn>/o)
+ name = scanner["insn:name"]
+ ope = split.(scanner["insn:opes"])
+ pop = split.(scanner["insn:pops"])
+ ret = split.(scanner["insn:rets"])
+
+ attrs = []
+ while l2 = scanner.scan(/#{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!(/#{grammar}\g<block>/o)
+ 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
+
+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..a5af409e71
--- /dev/null
+++ b/tool/ruby_vm/loaders/opt_insn_unif_def.rb
@@ -0,0 +1,34 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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(/ ^ (?: \#.* )? \n /x)
+ break if scanner.scan(/ ^ __END__ $ /x)
+
+ pos = scanner.scan!(/(?<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..18bcf7d441
--- /dev/null
+++ b/tool/ruby_vm/loaders/opt_operand_def.rb
@@ -0,0 +1,57 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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__
+grammer = %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(/ ^ __END__ $ /x)
+ next if scanner.scan(/#{grammer} \g<ws>+ /ox)
+
+ line = scanner.scan!(/#{grammer} \g<decl> /mox)
+ insn = scanner["insn"]
+ args = scanner["args"]
+ ary = []
+ until args.strip.empty? do
+ tmp = StringScanner.new args
+ tmp.scan(/#{grammer} \g<args> /mox)
+ ary << tmp["arg"]
+ args = tmp["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..f898fb36a4
--- /dev/null
+++ b/tool/ruby_vm/loaders/vm_opts_h.rb
@@ -0,0 +1,37 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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> \#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(/.*\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..0b6d6e09b2
--- /dev/null
+++ b/tool/ruby_vm/models/attribute.rb
@@ -0,0 +1,44 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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 insn:, name:, type:, location:, expr:
+ @insn = insn
+ @key = name
+ @expr = RubyVM::CExpr.new location: location, expr: expr
+ @type = type
+ end
+
+ def name
+ as_tr_cpp "attr #{@key} @ #{@insn.name}"
+ end
+
+ def pretty_name
+ "attr #{type} #{key} @ #{insn.pretty_name}"
+ end
+
+ def declaration
+ argv = @insn.opes.map {|o| o[:decl] }.join(', ')
+ sprintf '%s %s(%s)', @type, name, argv
+ end
+
+ def definition
+ argv = @insn.opes.map {|o| "MAYBE_UNUSED(#{o[:decl]})" }.join(",\n ")
+ argv = "\n #{argv}\n" if @insn.opes.size > 1
+ sprintf "%s\n%s(%s)", @type, name, argv
+ end
+end
diff --git a/tool/ruby_vm/models/bare_instructions.rb b/tool/ruby_vm/models/bare_instructions.rb
new file mode 100644
index 0000000000..c3a96b8f08
--- /dev/null
+++ b/tool/ruby_vm/models/bare_instructions.rb
@@ -0,0 +1,162 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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::BareInstructions
+ attr_reader :template, :name, :opes, :pops, :rets, :decls, :expr
+
+ def initialize template:, name:, location:, signature:, attributes:, expr:
+ @template = template
+ @name = name
+ @loc = location
+ @sig = signature
+ @expr = RubyVM::CExpr.new expr
+ @opes = typesplit @sig[:ope]
+ @pops = typesplit @sig[:pop].reject {|i| i == '...' }
+ @rets = typesplit @sig[:ret].reject {|i| i == '...' }
+ @attrs = attributes.map {|i|
+ RubyVM::Attribute.new insn: self, **i
+ }.each_with_object({}) {|a, h|
+ h[a.key] = a
+ }
+ @attrs_orig = @attrs.dup
+ 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 'CALL_ATTRIBUTE(%s)', [
+ name, @name, @opes.map {|i| i[:name] }
+ ].flatten.compact.join(', ')
+ end
+
+ def sp_inc
+ return @attrs.fetch "sp_inc" do |k|
+ return generate_attribute k, 'rb_snum_t', rets.size - pops.size
+ end
+ end
+
+ def has_attribute? k
+ @attrs_orig.has_key? k
+ end
+
+ def attributes
+ # need to generate predefined attribute defaults
+ sp_inc
+ # other_attribute
+ # ...
+ return @attrs.values
+ end
+
+ def width
+ return 1 + opes.size
+ end
+
+ def declarations
+ return @variables \
+ . values \
+ . group_by {|h| h[:type] } \
+ . map {|t, v| [t, v.map {|i| i[:name] }.sort ] } \
+ . map {|t, v| sprintf("%s %s", t, v.join(', ')) } \
+ . sort
+ 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
+ opes.map {|o|
+ c, _ = RubyVM::Typemap.fetch o[:type]
+ next c
+ }.join
+ end
+
+ def pushs_frame?
+ opes.any? {|o| /CALL_INFO/ =~ o[:type] }
+ end
+
+ def inspect
+ sprintf "#<%s@%s:%d>", @name, @loc[0], @loc[1]
+ end
+
+ private
+
+ def generate_attribute k, t, v
+ attr = RubyVM::Attribute.new \
+ insn: self, \
+ name: k, \
+ type: t, \
+ location: [], \
+ expr: v.to_s + ';'
+ return @attrs[k] = attr
+ 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 template: h, **h }
+
+ def self.fetch name
+ @instances.find do |insn|
+ insn.name == name
+ end or raise IndexError, "instruction not found: #{name}"
+ end
+
+ def self.to_a
+ @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..b19dd8bb48
--- /dev/null
+++ b/tool/ruby_vm/models/c_expr.rb
@@ -0,0 +1,41 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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 location:, expr:
+ @__FILE__ = location[0]
+ @__LINE__ = location[1]
+ @expr = 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
+ sprintf "#<%s:%d %s>", @__FILE__, @__LINE__, @expr
+ end
+end
diff --git a/tool/ruby_vm/models/instructions.rb b/tool/ruby_vm/models/instructions.rb
new file mode 100644
index 0000000000..5a04190a19
--- /dev/null
+++ b/tool/ruby_vm/models/instructions.rb
@@ -0,0 +1,22 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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_instructions'
+require_relative 'operands_unifications'
+require_relative 'instructions_unifications'
+
+RubyVM::Instructions = RubyVM::BareInstructions.to_a + \
+ RubyVM::OperandsUnifications.to_a + \
+ RubyVM::InstructionsUnifications.to_a
+
+require_relative 'trace_instructions'
+RubyVM::Instructions.freeze
diff --git a/tool/ruby_vm/models/instructions_unifications.rb b/tool/ruby_vm/models/instructions_unifications.rb
new file mode 100644
index 0000000000..346cebd709
--- /dev/null
+++ b/tool/ruby_vm/models/instructions_unifications.rb
@@ -0,0 +1,43 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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_instructions'
+
+class RubyVM::InstructionsUnifications
+ include RubyVM::CEscape
+
+ attr_reader :name
+
+ def initialize location:, signature:
+ @location = location
+ @name = namegen signature
+ @series = signature.map do |i|
+ RubyVM::BareInstructions.fetch 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.to_a
+ @instances
+ end
+end
diff --git a/tool/ruby_vm/models/operands_unifications.rb b/tool/ruby_vm/models/operands_unifications.rb
new file mode 100644
index 0000000000..c0ae0ece45
--- /dev/null
+++ b/tool/ruby_vm/models/operands_unifications.rb
@@ -0,0 +1,137 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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_instructions'
+
+class RubyVM::OperandsUnifications < RubyVM::BareInstructions
+ include RubyVM::CEscape
+
+ attr_reader :preamble, :original, :spec
+
+ def initialize location:, signature:
+ name = signature[0]
+ @original = RubyVM::BareInstructions.fetch name
+ template = @original.template
+ parts = compose location, signature, template[:signature]
+ json = template.dup
+ json[:location] = location
+ json[:signature] = parts[:signature]
+ json[:name] = parts[:name]
+ @preamble = parts[:preamble]
+ @spec = parts[:spec]
+ super template: template, **json
+ parts[:vars].each do |v|
+ @variables[v[:name]] ||= v
+ end
+ end
+
+ def operand_shift_of var
+ before = @original.opes.find_index var
+ after = @opes.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.opes[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
+
+ 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.opes
+ 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: " #{k[:name]} = #{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.to_a
+ @instances
+ end
+
+ def self.each_group
+ to_a.group_by(&:original).each_pair do |k, v|
+ yield k, v
+ end
+ end
+end
diff --git a/tool/ruby_vm/models/trace_instructions.rb b/tool/ruby_vm/models/trace_instructions.rb
new file mode 100644
index 0000000000..a99a933ac7
--- /dev/null
+++ b/tool/ruby_vm/models/trace_instructions.rb
@@ -0,0 +1,71 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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_instructions'
+
+class RubyVM::TraceInstructions
+ 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::Instructions.map {|i| new orig: i }
+
+ def self.to_a
+ @instances
+ end
+
+ RubyVM::Instructions.push(*to_a)
+end
diff --git a/tool/ruby_vm/models/typemap.rb b/tool/ruby_vm/models/typemap.rb
new file mode 100644
index 0000000000..d818ce9878
--- /dev/null
+++ b/tool/ruby_vm/models/typemap.rb
@@ -0,0 +1,61 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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_CACHE" => %w[E TS_CALLCACHE],
+ "CALL_INFO" => %w[C TS_CALLINFO],
+ "CDHASH" => %w[H TS_CDHASH],
+ "GENTRY" => %w[G TS_GENTRY],
+ "IC" => %w[K TS_IC],
+ "ID" => %w[I TS_ID],
+ "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],
+}
+
+# :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/scripts/insns2vm.rb b/tool/ruby_vm/scripts/insns2vm.rb
new file mode 100644
index 0000000000..b1c604b570
--- /dev/null
+++ b/tool/ruby_vm/scripts/insns2vm.rb
@@ -0,0 +1,88 @@
+#! /your/favourite/path/to/ruby
+# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
+# -*- 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'
+
+def router argv
+ targets = generate_parser.parse argv
+ return targets.map do |i|
+ next ApplicationController.new.generate i
+ end
+end
+
+def generate_parser
+ 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 beginnig 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 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 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 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 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 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
+ Dir.chdir dir
+ end
+
+ this.on "-V", "--[no-]verbose", <<~'end'
+ Please let us ignore this and be modest.
+ end
+ end
+end
diff --git a/tool/ruby_vm/views/_attributes.erb b/tool/ruby_vm/views/_attributes.erb
new file mode 100644
index 0000000000..258bf02906
--- /dev/null
+++ b/tool/ruby_vm/views/_attributes.erb
@@ -0,0 +1,34 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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
+%#
+typedef long OFFSET;
+typedef unsigned long lindex_t;
+typedef VALUE GENTRY;
+typedef rb_iseq_t *ISEQ;
+
+#define CALL_ATTRIBUTE(name, insn, ...) attr_ ## name ## _ ## insn(__VA_ARGS__)
+
+% 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..cebe4d7a5d
--- /dev/null
+++ b/tool/ruby_vm/views/_c_expr.erb
@@ -0,0 +1,17 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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/_copyright.erb b/tool/ruby_vm/views/_copyright.erb
new file mode 100644
index 0000000000..a449b0b735
--- /dev/null
+++ b/tool/ruby_vm/views/_copyright.erb
@@ -0,0 +1,31 @@
+%# -*- mode: c; style: ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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 consist of many small parts of codes copyrighted by each authors,
+ * 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 do not automatically define that of generated ones. This is
+ * the reason why this file is under unclear situation. All that 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..cebca8b8d0
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_entry.erb
@@ -0,0 +1,50 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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 %> */
+#define NAME_OF_CURRENT_INSN <%= insn.name %>
+INSN_ENTRY(<%= insn.name %>)
+{
+% unless insn.declarations.empty?
+ <%= insn.declarations.join(";\n ") %>;
+
+% end
+ START_OF_ORIGINAL_INSN(<%= insn.name %>);
+% insn.preamble.each do |konst|
+<%= render_c_expr konst -%>
+% end
+%
+% insn.opes.each_with_index do |ope, i|
+ <%= ope[:name] %> = (<%= ope[:type] %>)GET_OPERAND(<%= i + 1 %>);
+% end
+%
+% insn.pops.reverse_each.with_index.reverse_each do |pop, i|
+ <%= pop[:name] %> = <%= insn.cast_from_VALUE pop, "TOPN(#{i})"%>;
+% end
+ DEBUG_ENTER_INSN(<%=cstr insn.name %>);
+ ADD_PC(<%= insn.width %>);
+ PREFETCH(GET_PC());
+% unless insn.pops.empty?
+ POPN(<%= insn.pops.size %>);
+% end
+ COLLECT_USAGE_INSN(<%= insn.bin %>);
+% insn.opes.each_with_index do |ope, i|
+ COLLECT_USAGE_OPERAND(<%= insn.bin %>, <%= i %>, <%= ope[:name] %>);
+% end
+<%= render_c_expr insn.expr -%>
+% unless insn.rets.empty?
+ CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, <%= insn.rets.size %>);
+% insn.rets.each_with_index do |ret, i|
+ PUSH(<%= insn.cast_to_VALUE ret %>);
+% end
+% end
+%
+ END_INSN(<%= insn.name %>);
+}
+#undef NAME_OF_CURRENT_INSN
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..96b4629a2f
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_len_info.erb
@@ -0,0 +1,20 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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.
+CONSTFUNC(MAYBE_UNUSED(static int insn_len(VALUE insn)));
+
+int
+insn_len(VALUE i)
+{
+ static const char t[] = {
+% RubyVM::Instructions.each_slice 16 do |a|
+ <%= a.map(&:width).join(', ') -%>,
+% end
+ };
+
+ return t[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..da65632a28
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_name_info.erb
@@ -0,0 +1,30 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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.
+CONSTFUNC(MAYBE_UNUSED(static const char *insn_name(VALUE insn)));
+
+const char *
+insn_name(VALUE i)
+{
+ static const unsigned short o[] = {
+% a = RubyVM::Instructions.map {|i| i.name }
+% o = 0
+% a.each_slice 12 do |b|
+ <%= b.map {|i|
+ j = o; o += i.size + 1; sprintf("%4d", j)
+ }.join(', ') %>,
+% end
+ };
+ static const char t[] = {
+% a.each_slice 2 do |b|
+ <%= b.map {|i| sprintf("%-30s", cstr(i)) }.join(' "\0" ') %> "\0"
+% end
+ };
+
+ ASSERT_VM_INSTRUCTION_SIZE(o);
+ return &t[o[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..8e848ea915
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_operand_info.erb
@@ -0,0 +1,43 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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.
+MAYBE_UNUSED(static const char *insn_op_types(VALUE insn));
+MAYBE_UNUSED(static int insn_op_type(VALUE insn, long pos));
+
+const char *
+insn_op_types(VALUE i)
+{
+ static const unsigned short o[] = {
+% a = RubyVM::Instructions.map {|i| i.operands_info }
+% o = 0
+% a.each_slice 14 do |b|
+ <%= b.map {|i|
+ j = o; o += i.size + 1; sprintf("%3d", j)
+ }.join(', ') %>,
+% end
+ };
+ static const char t[] = {
+% a.each_slice 6 do |b|
+ <%= b.map {|i| sprintf("%-6s", cstr(i)) }.join(' "\0" ') %> "\0"
+% end
+ };
+
+ ASSERT_VM_INSTRUCTION_SIZE(o);
+
+ return &t[o[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_stack_increase.erb b/tool/ruby_vm/views/_insn_stack_increase.erb
new file mode 100644
index 0000000000..566e06c95e
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_stack_increase.erb
@@ -0,0 +1,53 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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.
+%#
+PUREFUNC(MAYBE_UNUSED(static int insn_stack_increase(int depth, int insn, const VALUE *opes)));
+PUREFUNC(static rb_snum_t insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes));
+
+rb_snum_t
+insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)
+{
+ static const signed char t[] = {
+% RubyVM::Instructions.each_slice 8 do |a|
+ <%= a.map { |i|
+ if i.has_attribute?('sp_inc')
+ '-127'
+ else
+ sprintf("%4d", i.rets.size - i.pops.size)
+ end
+ }.join(', ') -%>,
+% end
+ };
+ 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')
+ case <%= i.bin %>:
+ return CALL_ATTRIBUTE(sp_inc, <%= i.name %><%=
+ i.opes.map.with_index do |v, j|
+ k = i.cast_from_VALUE v, "opes[#{j}]"
+ next ", #{k}"
+ end.join
+ %>);
+% end
+ }
+}
+
+int
+insn_stack_increase(int depth, int insn, const VALUE *opes)
+{
+ enum ruby_vminsn_type itype = (enum ruby_vminsn_type)insn;
+ return depth + (int)insn_stack_increase_dispatch(itype, opes);
+}
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..296004d458
--- /dev/null
+++ b/tool/ruby_vm/views/_insn_type_chars.erb
@@ -0,0 +1,12 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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_pair do |k, (c, t)|
+ <%= t %> = '<%= c %>',
+% end
+};
diff --git a/tool/ruby_vm/views/_notice.erb b/tool/ruby_vm/views/_notice.erb
new file mode 100644
index 0000000000..8c6cdaf533
--- /dev/null
+++ b/tool/ruby_vm/views/_notice.erb
@@ -0,0 +1,22 @@
+%# -*- mode: c; style: ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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/_trace_instruction.erb b/tool/ruby_vm/views/_trace_instruction.erb
new file mode 100644
index 0000000000..30933a6f5a
--- /dev/null
+++ b/tool/ruby_vm/views/_trace_instruction.erb
@@ -0,0 +1,16 @@
+%# -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*-
+%# 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(), GET_PC());
+ DISPATCH_ORIGINAL_INSN(<%= insn.jump_destination %>);
+ END_INSN(<%= insn.name %>);
+}
diff --git a/tool/ruby_vm/views/insns.inc.erb b/tool/ruby_vm/views/insns.inc.erb
new file mode 100644
index 0000000000..78dddd69d1
--- /dev/null
+++ b/tool/ruby_vm/views/insns.inc.erb
@@ -0,0 +1,26 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# 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 YARV instruction list',
+ edit: __FILE__,
+} -%>
+
+/* BIN : Basic Instruction Name */
+#define BIN(n) YARVINSN_##n
+
+enum ruby_vminsn_type {
+% RubyVM::Instructions.each do |i|
+ <%= i.bin %>,
+% end
+ VM_INSTRUCTION_SIZE
+};
+
+#define ASSERT_VM_INSTRUCTION_SIZE(array) \
+ STATIC_ASSERT(numberof_##array, numberof(array) == VM_INSTRUCTION_SIZE)
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..e08a15e5ef
--- /dev/null
+++ b/tool/ruby_vm/views/insns_info.inc.erb
@@ -0,0 +1,19 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# 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__,
+} %>
+<%= render 'insn_type_chars' %>
+<%= render 'insn_name_info' %>
+<%= render 'insn_len_info' %>
+<%= render 'insn_operand_info' %>
+<%= render 'attributes' %>
+<%= render 'insn_stack_increase' %>
diff --git a/tool/ruby_vm/views/opt_sc.inc.erb b/tool/ruby_vm/views/opt_sc.inc.erb
new file mode 100644
index 0000000000..fdc9ee3d08
--- /dev/null
+++ b/tool/ruby_vm/views/opt_sc.inc.erb
@@ -0,0 +1,40 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# 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['STACK_CACHING']
+<%= render 'copyright' %>
+<%= render 'notice', locals: {
+ this_file: 'is for threaded code',
+ edit: __FILE__,
+} -%>
+
+#define SC_STATE_SIZE 6
+
+#define SCS_XX 1
+#define SCS_AX 2
+#define SCS_BX 3
+#define SCS_AB 4
+#define SCS_BA 5
+
+#define SC_ERROR 0xffffffff
+
+static const VALUE sc_insn_info[][SC_STATE_SIZE] = {
+#define NO_SC { SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR }
+% RubyVM::Instructions.each_slice 8 do |a|
+ <%= a.map{|i| 'NO_SC' }.join(', ') %>,
+% end
+#undef NO_SC
+};
+
+static const VALUE sc_insn_next[] = {
+% RubyVM::Instructions.each_slice 8 do |a|
+ <%= a.map{|i| 'SCS_XX' }.join(', ') %>,
+% end
+};
+
+ASSERT_VM_INSTRUCTION_SIZE(sc_insn_next);
diff --git a/tool/ruby_vm/views/optinsn.inc.erb b/tool/ruby_vm/views/optinsn.inc.erb
new file mode 100644
index 0000000000..6e30078cae
--- /dev/null
+++ b/tool/ruby_vm/views/optinsn.inc.erb
@@ -0,0 +1,71 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# 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::OperandsUnifications.each_group do |orig, unifs|
+ case <%= orig.bin %>:
+% unifs.each do |insn|
+
+ /* <%= insn.pretty_name %> */
+ if ( <%= insn.condition('op') %> ) {
+% insn.opes.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.opes.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::OperandsUnifications.each_group do |orig, unifs|
+% unifs.each do|insn|
+ case <%= insn.bin %>:
+% insn.spec.map{|(var,val)|val}.grep_v('*').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..29d8ca2855
--- /dev/null
+++ b/tool/ruby_vm/views/optunifs.inc.erb
@@ -0,0 +1,21 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# 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']
+% n = RubyVM::Instructions.size
+<%= 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[<%= n %>];
+
+ASSERT_VM_INSTRUCTION_SIZE(unified_insns_data);
diff --git a/tool/ruby_vm/views/vm.inc.erb b/tool/ruby_vm/views/vm.inc.erb
new file mode 100644
index 0000000000..24181fab95
--- /dev/null
+++ b/tool/ruby_vm/views/vm.inc.erb
@@ -0,0 +1,30 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# 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::BareInstructions.to_a.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::OperandsUnifications.to_a.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::InstructionsUnifications.to_a.each do |insn|
+<%= render 'insn_entry', locals: { insn: insn } -%>
+% end
+%
+% RubyVM::TraceInstructions.to_a.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..16886a1ea6
--- /dev/null
+++ b/tool/ruby_vm/views/vmtc.inc.erb
@@ -0,0 +1,21 @@
+/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
+
+%# 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 const void *const insns_address_table[] = {
+% RubyVM::Instructions.each do |i|
+ LABEL_PTR(<%= i.name %>),
+% end
+};
+
+ASSERT_VM_INSTRUCTION_SIZE(insns_address_table);