summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--common.mk4
-rw-r--r--lib/vm/instruction.rb1331
-rw-r--r--tool/insns2vm.rb1299
4 files changed, 1346 insertions, 1296 deletions
diff --git a/ChangeLog b/ChangeLog
index 2d6addca06..00f37a1954 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Fri Jun 29 14:48:18 2007 Koichi Sasada <ko1@atdot.net>
+
+ * tool/insns2vm.rb, lib/vm/instruction.rb: move process body
+ to lib/vm/instruction.rb.
+
+ * common.mk: fix aotc rule.
+ experimental. bin/ruby2cext is not added yet.
+
Fri Jun 29 11:23:09 2007 Nobuyoshi Nakada <nobu@ruby-lang.org>
* parse.y (dsym): return non-null NODE even if yyerror(). based on a
diff --git a/common.mk b/common.mk
index d9b0f15c75..1c99013072 100644
--- a/common.mk
+++ b/common.mk
@@ -650,8 +650,8 @@ benchmark-each: $(PROGRAM) PHONY
tbench: $(PROGRAM) PHONY
$(RUNRUBY) $(srcdir)/benchmark/run.rb bmx $(OPT) --ruby=./$(PROGRAM) --matzruby=$(MATZRUBY) --opts=-I$(srcdir)/lib
-aotc:
- $(RUBY) -I$(srcdir) -I. $(srcdir)/tool/aotcompile.rb $(INSNS2VMOPT)
+aotc: $(PROGRAM) PHONY
+ ./$(PROGRAM) -I$(srcdir)/lib $(srcdir)/bin/ruby2cext $(srcdir)/test.rb
vmasm: vm.$(ASMEXT)
diff --git a/lib/vm/instruction.rb b/lib/vm/instruction.rb
new file mode 100644
index 0000000000..4a9c23f82d
--- /dev/null
+++ b/lib/vm/instruction.rb
@@ -0,0 +1,1331 @@
+#
+#
+#
+
+require 'erb'
+
+module 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
+ 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
+
+ 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 definision
+ 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'
+ ret << " unsigned long #{v} = FIX2INT(opes[#{i}]);\n"
+ end
+ }
+ @defopes.each_with_index{|((t, var), val), i|
+ if t == 'rb_num_t' && val != '*'
+ ret << " unsigned long #{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]
+
+ (@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"] || 'opt_operand.def'
+ load_insn_unification_def opts[:"unif.def"] || 'opt_insn_unif.def'
+ make_stackcaching_insns if vm_opt?('STACK_CACHING')
+ end
+
+ attr_reader :vpath
+
+ %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 = ''
+
+ if /\/\/(.+)/ =~ rets_str
+ sp_inc = $1
+ else
+ sp_inc = nil
+ end
+
+ raise unless /^\{$/ =~ f.gets.chomp
+ 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 mismatcvh 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_optimiized(insn, ninsn, nopes, defv)
+ end
+
+ def make_insn_operand_optimiized 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|
+ "#define #{v} #{v}_#{i}"
+ }.join("\n") + "\n" +
+ insn.body +
+ passed_vars.map{|rpvars|
+ "#undef #{rpvars[0][1]}"
+ }.join("\n") +
+ "\n" +
+ redef_vars.keys.map{|v|
+ "#undef #{v}"
+ }.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
+
+ after = nil
+ SPECIAL_INSN_FOR_SC_AFTER.any?{|k, v|
+ if k =~ insn.name
+ after = v
+ break
+ end
+ }
+
+ 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_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
+ end
+
+ ###################################################################
+ # vm.inc
+ class VmBodyGenerator < SourceCodeGenerator
+ # vm.inc
+ def generate
+ vm_body = ''
+ @insns.each{|insn|
+ vm_body << "\n"
+ vm_body << make_insn_def(insn)
+ }
+ src = vpath.read('template/vm.inc.tmpl')
+ ERB.new(src).result(binding)
+ end
+
+ def generate_from_insnname insnname
+ make_insn_def @insns[insnname.to_s]
+ end
+
+ #######
+ private
+
+ def make_header_prepare_stack insn
+ ret = []
+ push_ba = insn.pushsc
+ raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0
+
+ push_ba.each{|pushs|
+ pushs.each{|r|
+ ret << " PUSH(SCREG(#{r}));"
+ }
+ }
+ ret.join("\n") + "\n"
+ end
+
+ def make_header_operands insn
+ vars = insn.opes
+ n = 0
+ ops = []
+
+ vars.each_with_index{|(type, var), i|
+ if type == '...'
+ break
+ end
+
+ ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});"
+ n += 1
+ }
+ @opn = n
+
+ # reverse or not?
+ # ops.join
+ ops.reverse.join("\n") + "\n"
+ end
+
+ def make_header_default_operands insn
+ ret = []
+ vars = insn.defopes
+
+ vars.each{|e|
+ next if e[1] == '*'
+ if use_const?
+ ret << " const #{e[0][0]} #{e[0][1]} = #{e[1]};"
+ else
+ ret << " #define #{e[0][1]} #{e[1]}"
+ end
+ }
+ ret.join("\n") + "\n"
+ end
+
+ def make_footer_default_operands insn
+ if use_const?
+ "\n"
+ else
+ ret = []
+ vars = insn.defopes
+
+ vars.each{|e|
+ next if e[1] == '*'
+ ret << "#undef #{e[0][1]}\n"
+ }
+ ret.join("\n") + "\n"
+ end
+ end
+
+ def make_header_stack_pops insn
+ 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?
+ pops.reverse.join("\n") + "\n"
+ end
+
+ def make_header_temporary_vars insn
+ ret = []
+ insn.tvars.each{|var|
+ ret << " #{var[0]} #{var[1]};"
+ }
+ ret.join("\n") + "\n"
+ end
+
+ def make_header_stack_val insn
+ ret = []
+ 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] != '...'
+ ret << " #{var[0]} #{var[1]};"
+ end
+ }
+ ret.join("\n") + "\n"
+ end
+
+ def make_footer_stack_val insn
+ ret = []
+ insn.rets.reverse_each{|v|
+ if v[1] == '...'
+ break
+ end
+ if v[2]
+ ret << " SCREG(#{v[2]}) = #{v[1]};"
+ else
+ ret << " PUSH(#{v[1]});"
+ end
+ }
+ ret.join("\n") + "\n"
+ end
+
+ def make_header insn
+ ret = "\nINSN_ENTRY(#{insn.name}){\n"
+ ret += " /* prepare stack status */\n" if verbose?
+ ret += make_header_prepare_stack insn
+ ret += "{\n"
+ ret += " /* declare stack push val */\n" if verbose?
+ ret += make_header_stack_val insn
+ ret += " /* declare and initialize default opes */\n" if verbose?
+ ret += make_header_default_operands insn
+ ret += " /* declare and get from iseq */\n" if verbose?
+ ret += make_header_operands insn
+ ret += " /* declare and pop from stack */\n" if verbose?
+ ret += make_header_stack_pops insn
+ ret += " /* declare temporary vars */\n" if verbose?
+ ret += make_header_temporary_vars insn
+
+ ret += " /* for debug */\n" if verbose?
+ ret += " DEBUG_ENTER_INSN(\"#{insn.name}\");\n"
+ ret += " /* management */\n" if verbose?
+ ret += " ADD_PC(1+#{@opn});\n"
+ ret += " PREFETCH(GET_PC());\n"
+ ret += " POPN(#{@popn});\n" if @popn > 0
+ ret += " #define CURRENT_INSN_#{insn.name} 1\n"
+ ret += " #define INSN_IS_SC() #{insn.sc ? 0 : 1}\n"
+ ret += " #define INSN_LABEL(lab) LABEL_#{insn.name}_##lab\n"
+
+ ret += " #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}\n"
+
+ ret += " USAGE_ANALYSIS_INSN(BIN(#{insn.name}));\n"
+ insn.opes.each_with_index{|op, i|
+ ret += " USAGE_ANALYSIS_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});\n"
+ }
+ ret += "{\n"
+ end
+
+ def make_footer insn
+ ret = ''
+ ret = " /* push stack val */\n" if verbose?
+ ret += make_footer_stack_val insn
+ # debug info
+
+ # epilogue
+ ret += make_footer_default_operands insn
+ ret += "#undef CURRENT_INSN_#{insn.name}\n"
+ ret += "#undef INSN_IS_SC\n"
+ ret += "#undef INSN_LABEL\n"
+ ret += "#undef LABEL_IS_SC\n"
+ ret += " END_INSN(#{insn.name});\n}}\n"
+ ret += "}\n"
+ end
+
+ def make_insn_def insn
+ ret = make_header insn
+ if line = insn.body.instance_variable_get(:@line_no)
+ file = insn.body.instance_variable_get(:@file)
+ ret << "#line #{line+1} \"#{file}\"" << "\n"
+ ret << insn.body
+ ret << '#line __CURRENT_LINE__ "vm.inc"' << "\n"
+ else
+ ret << insn.body
+ end
+ ret << make_footer(insn)
+ end
+ end
+
+ ###################################################################
+ # vmtc.inc
+ class VmTCIncGenerator < SourceCodeGenerator
+ def generate
+ insns_table = ''
+ insns_end_table = ''
+
+ @insns.each{|insn|
+ insns_table << " LABEL_PTR(#{insn.name}),\n"
+ }
+ @insns.each{|insn|
+ insns_end_table << " ELABEL_PTR(#{insn.name}),\n"
+ }
+ ERB.new(vpath.read('template/vmtc.inc.tmpl')).result(binding)
+ end
+ end
+
+ ###################################################################
+ # insns_info.inc
+ class InsnsInfoIncGenerator < SourceCodeGenerator
+ def generate
+ insns_info_inc
+ end
+
+ ###
+ private
+
+ def op2typesig op
+ case op
+ when /^OFFSET/
+ "TS_OFFSET"
+ when /^rb_num_t/
+ "TS_NUM"
+ when /^lindex_t/
+ "TS_LINDEX"
+ when /^dindex_t/
+ "TS_DINDEX"
+ when /^VALUE/
+ "TS_VALUE"
+ when /^ID/
+ "TS_ID"
+ when /GENTRY/
+ "TS_GENTRY"
+ when /^IC/
+ "TS_IC"
+ when /^\.\.\./
+ "TS_VARIABLE"
+ when /^CDHASH/
+ "TS_CDHASH"
+ when /^ISEQ/
+ "TS_ISEQ"
+ else
+ raise "unknown op type: #{op}"
+ end
+ end
+
+ TYPE_CHARS = {
+ 'TS_OFFSET' => 'O',
+ 'TS_NUM' => 'N',
+ 'TS_LINDEX' => 'L',
+ 'TS_DINDEX' => 'D',
+ 'TS_VALUE' => 'V',
+ 'TS_ID' => 'I',
+ 'TS_GENTRY' => 'G',
+ 'TS_IC' => 'C',
+ 'TS_CDHASH' => 'H',
+ 'TS_ISEQ' => 'S',
+ 'TS_VARIABLE' => '.',
+ }
+
+ # insns_info.inc
+ def insns_info_inc
+ # insn_type_chars
+ insn_type_chars = TYPE_CHARS.map{|t, c|
+ "#define #{t} '#{c}'"
+ }.join("\n")
+
+ # insn_names
+ insn_names = ''
+ @insns.each{|insn|
+ insn_names << " \"#{insn.name}\",\n"
+ }
+
+ # operands info
+ operands_info = ''
+ operands_num_info = ''
+ @insns.each{|insn|
+ opes = insn.opes
+ operands_info << ' '
+ ot = opes.map{|type, var|
+ TYPE_CHARS.fetch(op2typesig(type))
+ }
+ operands_info << "\"#{ot.join}\"" << ", \n"
+
+ num = opes.size + 1
+ operands_num_info << " #{num},\n"
+ }
+
+ # stack num
+ stack_num_info = ''
+ @insns.each{|insn|
+ num = insn.rets.size
+ stack_num_info << " #{num},\n"
+ }
+
+ # stack increase
+ stack_increase = ''
+ @insns.each{|insn|
+ stack_increase << <<-EOS
+ case BIN(#{insn.name}):{
+ #{insn.sp_increase_c_expr}
+ }
+ EOS
+ }
+ ERB.new(vpath.read('template/insns_info.inc.tmpl')).result(binding)
+ end
+ end
+
+ ###################################################################
+ # insns.inc
+ class InsnsIncGenerator < SourceCodeGenerator
+ def generate
+ insns = ''
+ i=0
+ @insns.each{|insn|
+ insns << " %-30s = %d,\n" % ["BIN(#{insn.name})", i]
+ i+=1
+ }
+ ERB.new(vpath.read('template/insns.inc.tmpl')).result(binding)
+ end
+ end
+
+ ###################################################################
+ # minsns.inc
+ class MInsnsIncGenerator < SourceCodeGenerator
+ def generate
+ defs = ''
+ i=0
+ @insns.each{|insn|
+ defs << " rb_define_const(mYarvInsns, %-30s, INT2FIX(%d));\n" %
+ ["\"I#{insn.name}\"", i]
+ i+=1
+ }
+ ERB.new(vpath.read('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/, /^dindex_t/
+ "INT2FIX(#{val})"
+ when /^VALUE/
+ val
+ when /^ID/
+ "INT2FIX(#{val})"
+ when /^ISEQ/
+ val
+ when /GENTRY/
+ raise
+ when /^\.\.\./
+ raise
+ else
+ raise "type: #{type}"
+ end
+ end
+
+ # optinsn.inc
+ def optinsn_inc
+ rule = ''
+ opt_insns_map = Hash.new{|h, k| h[k] = []}
+
+ @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{|originsn, optinsns|
+ rule += "case BIN(#{originsn.name}):\n"
+
+ optinsns.sort_by{|opti|
+ opti.defopes.find_all{|e| e[1] == '*'}.size
+ }.each{|opti|
+ rule += " if(\n"
+ i = 0
+ rule += ' ' + opti.defopes.map{|opinfo|
+ i += 1
+ next if opinfo[1] == '*'
+ "insnobj->operands[#{i-1}] == #{val_as_type(opinfo)}\n"
+ }.compact.join('&& ')
+ rule += " ){\n"
+ idx = 0
+ n = 0
+ opti.defopes.each{|opinfo|
+ if opinfo[1] == '*'
+ if idx != n
+ rule += " insnobj->operands[#{idx}] = insnobj->operands[#{n}];\n"
+ end
+ idx += 1
+ else
+ # skip
+ end
+ n += 1
+ }
+ rule += " insnobj->insn_id = BIN(#{opti.name});\n"
+ rule += " insnobj->operand_size = #{idx};\n"
+ rule += " break;\n }\n"
+ }
+ rule += " break;\n";
+ }
+ ERB.new(vpath.read('template/optinsn.inc.tmpl')).result(binding)
+ end
+ end
+
+ ###################################################################
+ # optunifs.inc
+ class OptUnifsIncGenerator < SourceCodeGenerator
+ def generate
+ unif_insns_each = ''
+ unif_insns = ''
+ unif_insns_data = []
+
+ insns = @insns.find_all{|insn| !insn.is_sc}
+ insns.each{|insn|
+ size = insn.unifs.size
+ if size > 0
+ insn.unifs.sort_by{|unif| -unif[1].size}.each_with_index{|unif, i|
+
+ uni_insn, uni_insns = *unif
+ uni_insns = uni_insns[1..-1]
+ unif_insns_each << "static int UNIFIED_#{insn.name}_#{i}[] = {" +
+ " BIN(#{uni_insn.name}), #{uni_insns.size + 2}, \n " +
+ uni_insns.map{|e| "BIN(#{e.name})"}.join(", ") + "};\n"
+ }
+ else
+
+ end
+ if size > 0
+ unif_insns << "static int *UNIFIED_#{insn.name}[] = {(int *)#{size+1}, \n"
+ unif_insns << (0...size).map{|e| " UNIFIED_#{insn.name}_#{e}"}.join(",\n") + "};\n"
+ unif_insns_data << " UNIFIED_#{insn.name}"
+ else
+ unif_insns_data << " 0"
+ end
+ }
+ unif_insns_data = "static int **unified_insns_data[] = {\n" +
+ unif_insns_data.join(",\n") + "};\n"
+ ERB.new(vpath.read('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")
+ ERB.new(vpath.read('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"
+ }
+ ERB.new(vpath.read('template/yasmdata.rb.tmpl')).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 #{seq}\n" % i
+ d << ":stack: #{before} => #{after}\n\n"
+ i+=1
+ }
+ d
+ end
+
+ def desc_ja
+ d = desc :j
+ ERB.new(vpath.read('template/yarvarch.ja')).result(binding)
+ end
+
+ def desc_en
+ d = desc :e
+ ERB.new(vpath.read('template/yarvarch.en')).result(binding)
+ end
+ end
+
+ module VPATH
+ def search(meth, base, *rest)
+ begin
+ meth.call(base, *rest)
+ rescue Errno::ENOENT => error
+ each do |dir|
+ return meth.call(File.join(dir, base), *rest) rescue nil
+ end
+ raise error
+ end
+ end
+
+ def process(*args, &block)
+ search(File.method(__callee__), *args, &block)
+ end
+
+ alias stat process
+ alias lstat process
+
+ def open(*args)
+ f = search(File.method(:open), *args)
+ if block_given?
+ begin
+ yield f
+ ensure
+ f.close unless f.closed?
+ end
+ else
+ f
+ end
+ end
+
+ def read(*args)
+ open(*args) {|f| f.read}
+ end
+
+ def foreach(file, *args, &block)
+ open(file) {|f| f.each(*args, &block)}
+ end
+
+ def self.def_options(opt)
+ vpath = []
+ path_sep = ':'
+
+ opt.on("-I", "--srcdir=DIR", "add a directory to search path") {|dir|
+ vpath |= [dir]
+ }
+ opt.on("-L", "--vpath=PATH LIST", "add directories to search path") {|dirs|
+ vpath |= dirs.split(path_sep)
+ }
+ opt.on("--path-separator=SEP", /\A\W\z/, "separator for vpath") {|sep|
+ path_sep = sep
+ }
+
+ proc {
+ vpath.extend(self) unless vpath.empty?
+ }
+ 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(fn, 'w') {|f| f.puts(s)}
+ }
+ end
+
+ def self.def_options(opt)
+ opts = {
+ :"insns.def" => 'insns.def',
+ :"opope.def" => 'opt_operand.def',
+ :"unif.def" => '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("-V", "--[no-]verbose") {|v|
+ opts[:verbose] = v
+ }
+
+ vpath = VPATH.def_options(opt)
+
+ proc {
+ opts[:VPATH] = vpath.call
+ build opts
+ }
+ end
+
+ def self.build opts, vpath = ['./']
+ opts[:VPATH] = vpath.extend(VPATH) unless opts[:VPATH]
+ self.new InstructionsLoader.new(opts)
+ end
+ end
+end
+
diff --git a/tool/insns2vm.rb b/tool/insns2vm.rb
index 3eb2cc507e..82a5e7c88c 100644
--- a/tool/insns2vm.rb
+++ b/tool/insns2vm.rb
@@ -1,1304 +1,15 @@
#!/usr/bin/env ruby
-#
-#
-#
-# $verbose = true
-# $use_const = true
-Version = %w$Revision: 11626 $[1..-1]
-
-require 'pp'
-require 'erb'
require 'optparse'
-module VPATH
- def search(meth, base, *rest)
- begin
- meth.call(base, *rest)
- rescue Errno::ENOENT => error
- each do |dir|
- return meth.call(File.join(dir, base), *rest) rescue nil
- end
- raise error
- end
- end
-
- def process(*args, &block)
- search(File.method(__callee__), *args, &block)
- end
-
- alias stat process
- alias lstat process
-
- def open(*args)
- f = search(File.method(:open), *args)
- if block_given?
- begin
- yield f
- ensure
- f.close unless f.closed?
- end
- else
- f
- end
- end
-
- def read(*args)
- open(*args) {|f| f.read}
- end
-
- def foreach(file, *args, &block)
- open(file) {|f| f.each(*args, &block)}
- end
-
- def self.def_options(opt)
- vpath = []
- path_sep = ':'
-
- opt.on("-I", "--srcdir=DIR", "add a directory to search path") {|dir|
- vpath |= [dir]
- }
- opt.on("-L", "--vpath=PATH LIST", "add directories to search path") {|dirs|
- vpath |= dirs.split(path_sep)
- }
- opt.on("--path-separator=SEP", /\A\W\z/, "separator for vpath") {|sep|
- path_sep = sep
- }
-
- proc {
- vpath.extend(self) unless vpath.empty?
- }
- end
-end
-
-class InsnsDef
- class InsnInfo
-
- 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
- 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
-
- 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 definision
- 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'
- ret << " unsigned long #{v} = FIX2INT(opes[#{i}]);\n"
- end
- }
- @defopes.each_with_index{|((t, var), val), i|
- if t == 'rb_num_t' && val != '*'
- ret << " unsigned long #{var} = #{val};\n"
- end
- }
-
- ret << " #{@sp_inc};\n"
- ret << " return depth + inc;"
- ret
- else
- "return depth + #{rets.size - pops.size};"
- end
- end
-
- def inspect
- "#<InsnInfo:#{@name}>"
- end
- end
-
- def initialize file, optopfile, uniffile, opts = {}
- @insns = []
- @insn_map = {}
-
- @vpath = opts['VPATH'] || File
- @use_const = opts['use-const']
- @verbose = opts['verbose']
-
- (@vm_opts = load_vm_opts).each {|k, v|
- @vm_opts[k] = opts[k] if opts.key?(k)
- }
-
- load_insns_def file
-
- load_opt_operand_def optopfile
- load_insn_unification_def uniffile
- make_stackcaching_insns if vm_opt?('STACK_CACHING')
- end
-
- attr_reader :insns
- attr_reader :insn_map
-
- attr_reader :vpath
-
- %w[use_const verbose].each do |attr|
- attr_reader attr
- alias_method "#{attr}?", attr
- remove_method attr
- end
-
- 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 each
- @insns.each{|insn|
- yield insn
- }
- end
-
- 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 InsnInfo.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 = ''
-
- if /\/\/(.+)/ =~ rets_str
- sp_inc = $1
- else
- sp_inc = nil
- end
-
- raise unless /^\{$/ =~ f.gets.chomp
- 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}
- }
- 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 mismatcvh 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_optimiized(insn, ninsn, nopes, defv)
- end
-
- def make_insn_operand_optimiized orig_insn, name, opes, defopes
- comm = orig_insn.comm.dup
- comm[:c] = 'optimize'
- add_insn insn = InsnInfo.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]
- }
- }
- 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|
- "#define #{v} #{v}_#{i}"
- }.join("\n") + "\n" +
- insn.body +
- passed_vars.map{|rpvars|
- "#undef #{rpvars[0][1]}"
- }.join("\n") +
- "\n" +
- redef_vars.keys.map{|v|
- "#undef #{v}"
- }.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 = InsnInfo.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
-
- after = nil
- SPECIAL_INSN_FOR_SC_AFTER.any?{|k, v|
- if k =~ insn.name
- after = v
- break
- end
- }
-
- 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)
- }
- }
- # exit! 1
- end
-
- def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc
- comm = orig_insn.comm.dup
- comm[:c] = 'optimize(sc)'
-
- scinsn = InsnInfo.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 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_#{complement_name(ofrom)}_#{complement_name(from)}",
- pops, rets, pushs_before, pushs, from]
- #p ret
- ret
- end
-
- ###################################################################
- # vm.inc
- def make_header_prepare_stack insn
- ret = []
- push_ba = insn.pushsc
- raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0
-
- push_ba.each{|pushs|
- pushs.each{|r|
- ret << " PUSH(SCREG(#{r}));"
- }
- }
- ret.join("\n") + "\n"
- end
-
- def make_header_operands insn
- vars = insn.opes
- n = 0
- ops = []
-
- vars.each_with_index{|(type, var), i|
- if type == '...'
- break
- end
-
- ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});"
- n += 1
- }
- @opn = n
-
- # reverse or not?
- # ops.join
- ops.reverse.join("\n") + "\n"
- end
-
- def make_header_default_operands insn
- ret = []
- vars = insn.defopes
-
- vars.each{|e|
- next if e[1] == '*'
- if use_const?
- ret << " const #{e[0][0]} #{e[0][1]} = #{e[1]};"
- else
- ret << " #define #{e[0][1]} #{e[1]}"
- end
- }
- ret.join("\n") + "\n"
- end
-
- def make_footer_default_operands insn
- if use_const?
- "\n"
- else
- ret = []
- vars = insn.defopes
-
- vars.each{|e|
- next if e[1] == '*'
- ret << "#undef #{e[0][1]}\n"
- }
- ret.join("\n") + "\n"
- end
- end
-
- def make_header_stack_pops insn
- 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?
- pops.reverse.join("\n") + "\n"
- end
-
- def make_header_temporary_vars insn
- ret = []
- insn.tvars.each{|var|
- ret << " #{var[0]} #{var[1]};"
- }
- ret.join("\n") + "\n"
- end
-
- def make_header_stack_val insn
- ret = []
-
- 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] != '...'
- ret << " #{var[0]} #{var[1]};"
- end
- }
- ret.join("\n") + "\n"
- end
-
- def make_footer_stack_val insn
- ret = []
- insn.rets.reverse_each{|v|
- if v[1] == '...'
- break
- end
- if v[2]
- ret << " SCREG(#{v[2]}) = #{v[1]};"
- else
- ret << " PUSH(#{v[1]});"
- end
- }
- ret.join("\n") + "\n"
- end
-
- def make_header insn
- ret = "\nINSN_ENTRY(#{insn.name}){\n"
- ret += " /* prepare stack status */\n" if verbose?
- ret += make_header_prepare_stack insn
- ret += "{\n"
- ret += " /* declare stack push val */\n" if verbose?
- ret += make_header_stack_val insn
- ret += " /* declare and initialize default opes */\n" if verbose?
- ret += make_header_default_operands insn
- ret += " /* declare and get from iseq */\n" if verbose?
- ret += make_header_operands insn
- ret += " /* declare and pop from stack */\n" if verbose?
- ret += make_header_stack_pops insn
- ret += " /* declare temporary vars */\n" if verbose?
- ret += make_header_temporary_vars insn
-
- ret += " /* for debug */\n" if verbose?
- ret += " DEBUG_ENTER_INSN(\"#{insn.name}\");\n"
- ret += " /* management */\n" if verbose?
- ret += " ADD_PC(1+#{@opn});\n"
- ret += " PREFETCH(GET_PC());\n"
- ret += " POPN(#{@popn});\n" if @popn > 0
- ret += " #define CURRENT_INSN_#{insn.name} 1\n"
- ret += " #define INSN_IS_SC() #{insn.sc ? 0 : 1}\n"
- ret += " #define INSN_LABEL(lab) LABEL_#{insn.name}_##lab\n"
-
- ret += " #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}\n"
-
- ret += " USAGE_ANALYSIS_INSN(BIN(#{insn.name}));\n"
- insn.opes.each_with_index{|op, i|
- ret += " USAGE_ANALYSIS_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});\n"
- }
- ret += "{\n"
-
- end
-
- def make_footer insn
- ret = ''
- ret = " /* push stack val */\n" if verbose?
- ret += make_footer_stack_val insn
- # debug info
-
- # epilogue
- ret += make_footer_default_operands insn
- ret += "#undef CURRENT_INSN_#{insn.name}\n"
- ret += "#undef INSN_IS_SC\n"
- ret += "#undef INSN_LABEL\n"
- ret += "#undef LABEL_IS_SC\n"
- ret += " END_INSN(#{insn.name});\n}}\n"
- ret += "}\n"
- end
-
- def make_insn_def insn
- ret = make_header insn
- if line = insn.body.instance_variable_get(:@line_no)
- file = insn.body.instance_variable_get(:@file)
- ret << "#line #{line+1} \"#{file}\"" << "\n"
- ret << insn.body
- ret << '#line __CURRENT_LINE__ "vm.inc"' << "\n"
- else
- ret << insn.body
- end
- ret << make_footer(insn)
- end
-
- # vm.inc
- def vm_inc
- vm_body = ''
-
- @insns.each{|insn|
- vm_body << "\n"
- vm_body << make_insn_def(insn)
- }
- src = vpath.read('template/vm.inc.tmpl')
- ERB.new(src).result(binding)
- end
-
-
- ###################################################################
- # vmtc.inc
-
- def vmtc_inc
- insns_table = ''
- insns_end_table = ''
-
- @insns.each{|insn|
- insns_table << " LABEL_PTR(#{insn.name}),\n"
- }
- @insns.each{|insn|
- insns_end_table << " ELABEL_PTR(#{insn.name}),\n"
- }
-
- ERB.new(vpath.read('template/vmtc.inc.tmpl')).result(binding)
- end
-
-
- ###################################################################
- # insns_info.inc
-
- def op2typesig op
- case op
- when /^OFFSET/
- "TS_OFFSET"
- when /^rb_num_t/
- "TS_NUM"
- when /^lindex_t/
- "TS_LINDEX"
- when /^dindex_t/
- "TS_DINDEX"
- when /^VALUE/
- "TS_VALUE"
- when /^ID/
- "TS_ID"
- when /GENTRY/
- "TS_GENTRY"
- when /^IC/
- "TS_IC"
- when /^\.\.\./
- "TS_VARIABLE"
- when /^CDHASH/
- "TS_CDHASH"
- when /^ISEQ/
- "TS_ISEQ"
- else
- raise "unknown op type: #{op}"
- end
- end
-
- TYPE_CHARS = {
- 'TS_OFFSET' => 'O',
- 'TS_NUM' => 'N',
- 'TS_LINDEX' => 'L',
- 'TS_DINDEX' => 'D',
- 'TS_VALUE' => 'V',
- 'TS_ID' => 'I',
- 'TS_GENTRY' => 'G',
- 'TS_IC' => 'C',
- 'TS_CDHASH' => 'H',
- 'TS_ISEQ' => 'S',
- 'TS_VARIABLE' => '.',
- }
-
- # insns_info.inc
- def insns_info_inc
- # insn_type_chars
- insn_type_chars = TYPE_CHARS.map{|t, c|
- "#define #{t} '#{c}'"
- }.join("\n")
-
- # insn_names
- insn_names = ''
- @insns.each{|insn|
- insn_names << " \"#{insn.name}\",\n"
- }
-
- # operands info
- operands_info = ''
- operands_num_info = ''
- @insns.each{|insn|
- opes = insn.opes
- operands_info << ' '
- ot = opes.map{|type, var|
- TYPE_CHARS.fetch(op2typesig(type))
- }
- operands_info << "\"#{ot.join}\"" << ", \n"
-
- num = opes.size + 1
- operands_num_info << " #{num},\n"
- }
-
- # stack num
- stack_num_info = ''
- @insns.each{|insn|
- num = insn.rets.size
- stack_num_info << " #{num},\n"
- }
-
- # stack increase
- stack_increase = ''
- @insns.each{|insn|
- stack_increase << <<-EOS
- case BIN(#{insn.name}):{
- #{insn.sp_increase_c_expr}
- }
- EOS
- }
- ERB.new(vpath.read('template/insns_info.inc.tmpl')).result(binding)
- end
-
-
- ###################################################################
- # insns.inc
- def insns_inc
- insns = ''
- i=0
- @insns.each{|insn|
- insns << " %-30s = %d,\n" % ["BIN(#{insn.name})", i]
- i+=1
- }
- ERB.new(vpath.read('template/insns.inc.tmpl')).result(binding)
- end
-
-
- ###################################################################
- # minsns.inc
- def minsns_inc
- defs = ''
- i=0
- @insns.each{|insn|
- defs << " rb_define_const(mYarvInsns, %-30s, INT2FIX(%d));\n" %
- ["\"I#{insn.name}\"", i]
- i+=1
- }
- ERB.new(vpath.read('template/minsns.inc.tmpl')).result(binding)
- end
-
-
- ###################################################################
- # optinsn.inc
- def val_as_type op
- type = op[0][0]
- val = op[1]
-
- case type
- when /^long/, /^rb_num_t/, /^lindex_t/, /^dindex_t/
- "INT2FIX(#{val})"
- when /^VALUE/
- val
- when /^ID/
- "INT2FIX(#{val})"
- when /^ISEQ/
- val
- when /GENTRY/
- raise
- when /^\.\.\./
- raise
- else
- raise "type: #{type}"
- end
- end
-
- # optinsn.inc
- def optinsn_inc
- rule = ''
- opt_insns_map = Hash.new{|h, k| h[k] = []}
-
- @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{|originsn, optinsns|
- rule += "case BIN(#{originsn.name}):\n"
-
- optinsns.sort_by{|opti|
- opti.defopes.find_all{|e| e[1] == '*'}.size
- }.each{|opti|
- rule += " if(\n"
- i = 0
- rule += ' ' + opti.defopes.map{|opinfo|
- i += 1
- next if opinfo[1] == '*'
- "insnobj->operands[#{i-1}] == #{val_as_type(opinfo)}\n"
- }.compact.join('&& ')
- rule += " ){\n"
- idx = 0
- n = 0
- opti.defopes.each{|opinfo|
- if opinfo[1] == '*'
- if idx != n
- rule += " insnobj->operands[#{idx}] = insnobj->operands[#{n}];\n"
- end
- idx += 1
- else
- # skip
- end
- n += 1
- }
- rule += " insnobj->insn_id = BIN(#{opti.name});\n"
- rule += " insnobj->operand_size = #{idx};\n"
- rule += " break;\n }\n"
- }
- rule += " break;\n";
- }
- ERB.new(vpath.read('template/optinsn.inc.tmpl')).result(binding)
- end
-
-
- ###################################################################
- # optunifs.inc
- def optunifs_unc
- unif_insns_each = ''
- unif_insns = ''
- unif_insns_data = []
-
- insns = find_all{|insn| !insn.is_sc}
- insns.each{|insn|
- size = insn.unifs.size
- if size > 0
- insn.unifs.sort_by{|unif| -unif[1].size}.each_with_index{|unif, i|
-
- uni_insn, uni_insns = *unif
- uni_insns = uni_insns[1..-1]
- unif_insns_each << "static int UNIFIED_#{insn.name}_#{i}[] = {" +
- " BIN(#{uni_insn.name}), #{uni_insns.size + 2}, \n " +
- uni_insns.map{|e| "BIN(#{e.name})"}.join(", ") + "};\n"
- }
- else
-
- end
- if size > 0
- unif_insns << "static int *UNIFIED_#{insn.name}[] = {(int *)#{size+1}, \n"
- unif_insns << (0...size).map{|e| " UNIFIED_#{insn.name}_#{e}"}.join(",\n") + "};\n"
- unif_insns_data << " UNIFIED_#{insn.name}"
- else
- unif_insns_data << " 0"
- end
- }
- unif_insns_data = "static int **unified_insns_data[] = {\n" +
- unif_insns_data.join(",\n") + "};\n"
- ERB.new(vpath.read('template/optunifs.inc.tmpl')).result(binding)
- end
-
- ###################################################################
- # opt_sc.inc
- def opt_sc_inc
- 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_#{complement_name(insn.nextsc).upcase}" +
- (verbose? ? " /* #{insn.name} */" : '')
- }.join(",\n")
- ERB.new(vpath.read('template/opt_sc.inc.tmpl')).result(binding)
- end
-
- ###################################################################
- # yasmdata.rb
- def yasmdata_rb
- insn_id2no = ''
- @insns.each_with_index{|insn, i|
- insn_id2no << " :#{insn.name} => #{i},\n"
- }
- ERB.new(vpath.read('template/yasmdata.rb.tmpl')).result(binding)
- end
-
- ###################################################################
- # yarvarch.*
- 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 #{seq}\n" % i
- d << ":stack: #{before} => #{after}\n\n"
- i+=1
- }
- d
- end
-
- def desc_ja
- d = desc :j
- ERB.new(vpath.read('template/yarvarch.ja')).result(binding)
- end
-
- def desc_en
- d = desc :e
- ERB.new(vpath.read('template/yarvarch.en')).result(binding)
- end
-
- Files = { # codes
- 'vm.inc' => :vm_inc,
- 'vmtc.inc' => :vmtc_inc,
- 'insns.inc' => :insns_inc,
- 'insns_info.inc' => :insns_info_inc,
- # 'minsns.inc' => :minsns_inc,
- 'optinsn.inc' => :optinsn_inc,
- 'optunifs.inc' => :optunifs_unc,
- 'opt_sc.inc' => :opt_sc_inc,
- 'yasmdata.rb' => :yasmdata_rb,
- }
-
- def make_sources args = []
- args = Files.keys if args.empty?
-
- args.each{|fn|
- s = __send__(Files[fn])
- open(fn, 'w') {|f| f.puts(s)}
- }
- end
-
- def self.make_sources insns_def, opopt_def, unif_def, args = [], opts = {}
- insns = InsnsDef.new(insns_def, opopt_def, unif_def, opts)
- insns.make_sources(args)
- end
-
- def self.def_options(opt)
- opts = {}
- insns_def = 'insns.def'
- opope_def = 'opt_operand.def'
- unif_def = '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", /\A\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|
- insns_def = n
- }
- opt.on("-o", "--opt-operanddef=FILE", "--opt-operand-def",
- "vm option: operand definition file") {|n|
- opope_def = n
- }
- opt.on("-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def",
- "vm option: instruction unification file") {|n|
- unif_def = n
- }
- opt.on("-C", "--[no-]use-const",
- "use consts for default operands instead of macros") {|v|
- opts['use-const'] = v
- }
- opt.on("-V", "--[no-]verbose") {|v|
- opts['verbose'] = v
- }
-
- vpath = VPATH.def_options(opt)
-
- proc {
- opts['VPATH'] = vpath[]
- new insns_def, opope_def, unif_def, opts
- }
- end
-end
-
-
-
+Version = %w$Revision: 11626 $[1..-1]
-##############################################
-def insns_def_new(argv = ARGV)
- opt = OptionParser.new
- insn_maker = InsnsDef.def_options(opt)
- opt.parse(argv)
- insn_maker[]
-end
+require "#{File.join(File.dirname(__FILE__), '../lib/vm/instruction')}"
if $0 == __FILE__
opts = ARGV.options
- insn_maker = InsnsDef.def_options(opts)
+ maker = RubyVM::SourceCodeGenerator.def_options(opts)
files = opts.parse!
- insns = insn_maker[]
- insns.make_sources(files)
+ generator = maker.call
+ generator.generate(files)
end
-