summaryrefslogtreecommitdiff
path: root/tool
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2006-12-31 15:02:22 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2006-12-31 15:02:22 +0000
commita3e1b1ce7ed7e7ffac23015fc2fde56511b30681 (patch)
tree7b725552a9a4ded93849ca2faab1b257f7761790 /tool
parent3e7566d8fb5138bb9cd647e5fdefc54fc9803509 (diff)
* Merge YARV
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11439 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'tool')
-rw-r--r--tool/asm_parse.rb51
-rw-r--r--tool/compile.rb67
-rw-r--r--tool/eval.rb161
-rw-r--r--tool/getrev.rb13
-rw-r--r--tool/insns2vm.rb1220
-rw-r--r--tool/makedocs.rb62
-rw-r--r--tool/parse.rb13
-rw-r--r--tool/runruby.rb4
-rw-r--r--tool/vtlh.rb15
9 files changed, 1606 insertions, 0 deletions
diff --git a/tool/asm_parse.rb b/tool/asm_parse.rb
new file mode 100644
index 0000000..f3d7f73
--- /dev/null
+++ b/tool/asm_parse.rb
@@ -0,0 +1,51 @@
+stat = {}
+
+while line = ARGF.gets
+ if /\[start\] (\w+)/ =~ line
+ name = $1
+ puts '--------------------------------------------------------------'
+ puts line
+ size = 0
+ len = 0
+
+ while line = ARGF.gets
+ if /\[start\] (\w+)/ =~ line
+ puts "\t; # length: #{len}, size: #{size}"
+ puts "\t; # !!"
+ stat[name] = [len, size]
+ #
+ name = $1
+ puts '--------------------------------------------------------------'
+ puts line
+ size = 0
+ len = 0
+ next
+ end
+
+ unless /(\ALM)|(\ALB)|(\A\.)|(\A\/)/ =~ line
+ puts line
+ if /\[length = (\d+)\]/ =~ line
+ len += $1.to_i
+ size += 1
+ end
+ end
+
+
+ if /__NEXT_INSN__/ !~ line && /\[end \] (\w+)/ =~ line
+ ename = $1
+ if name != ename
+ puts "!! start with #{name}, but end with #{ename}"
+ end
+ stat[ename] = [len, size]
+ puts "\t; # length: #{len}, size: #{size}"
+ break
+ end
+ end
+ end
+end
+
+stat.sort_by{|a, b| -b[0] * 1000 - a[0]}.each{|a, b|
+ puts "#{a}\t#{b.join("\t")}"
+}
+puts "total length :\t#{stat.inject(0){|r, e| r+e[1][0]}}"
+puts "total size :\t#{stat.inject(0){|r, e| r+e[1][1]}}"
diff --git a/tool/compile.rb b/tool/compile.rb
new file mode 100644
index 0000000..5798b81
--- /dev/null
+++ b/tool/compile.rb
@@ -0,0 +1,67 @@
+require 'optparse'
+require 'pp'
+
+OutputCompileOption = {
+ # enable
+ :peephole_optimization =>true,
+ :inline_const_cache =>true,
+
+ # disable
+ :specialized_instruction =>false,
+ :operands_unification =>false,
+ :instructions_unification =>false,
+ :stack_caching =>false,
+}
+
+def compile_to_rb infile, outfile
+ iseq = YARVCore::InstructionSequence.compile_file(infile, OutputCompileOption)
+
+ open(outfile, 'w'){|f|
+ f.puts "YARVCore::InstructionSequence.load(" +
+ "Marshal.load(<<EOS____.unpack('m*')[0])).eval"
+ f.puts [Marshal.dump(iseq.to_a)].pack('m*')
+ f.puts "EOS____"
+ }
+end
+
+def compile_to_rbc infile, outfile, type
+ iseq = YARVCore::InstructionSequence.compile_file(infile, OutputCompileOption)
+
+ case type
+ when 'm'
+ open(outfile, 'wb'){|f|
+ f.print "RBCM"
+ f.puts Marshal.dump(iseq.to_a, f)
+ }
+ else
+ raise "Unsupported compile type: #{type}"
+ end
+end
+
+## main
+
+outfile = 'a.rb'
+type = 'm'
+opt = OptionParser.new{|opt|
+ opt.on('-o file'){|o|
+ outfile = o
+ }
+ opt.on('-t type', '--type type'){|o|
+ type = o
+ }
+ opt.version = '0.0.1'
+}
+
+opt.parse!(ARGV)
+
+ARGV.each{|file|
+ case outfile
+ when /\.rb\Z/
+ compile_to_rb file, outfile
+ when /\.rbc\Z/
+ compile_to_rbc file, outfile, type
+ else
+ raise
+ end
+}
+
diff --git a/tool/eval.rb b/tool/eval.rb
new file mode 100644
index 0000000..906ba9c
--- /dev/null
+++ b/tool/eval.rb
@@ -0,0 +1,161 @@
+
+require 'rbconfig'
+require 'fileutils'
+require 'pp'
+
+Ruby = ENV['RUBY'] ||
+ File.join(Config::CONFIG["bindir"],
+ Config::CONFIG["ruby_install_name"] + Config::CONFIG["EXEEXT"])
+#
+
+OPTIONS = %w{
+ opt-direct-threaded-code
+ opt-basic-operations
+ opt-operands-unification
+ opt-instructions-unification
+ opt-inline-method-cache
+ opt-stack-caching
+}.map{|opt|
+ '--disable-' + opt
+}
+
+opts = OPTIONS.dup
+Configs = OPTIONS.map{|opt|
+ o = opts.dup
+ opts.delete(opt)
+ o
+} + [[]]
+
+pp Configs if $DEBUG
+
+
+def exec_cmd(cmd)
+ puts cmd
+ unless system(cmd)
+ p cmd
+ raise "error"
+ end
+end
+
+def dirname idx
+ "ev-#{idx}"
+end
+
+def build
+ Configs.each_with_index{|config, idx|
+ dir = dirname(idx)
+ FileUtils.rm_rf(dir) if FileTest.exist?(dir)
+ Dir.mkdir(dir)
+ FileUtils.cd(dir){
+ exec_cmd("#{Ruby} ../extconf.rb " + config.join(" "))
+ exec_cmd("make clean test-all")
+ }
+ }
+end
+
+def check
+ Configs.each_with_index{|c, idx|
+ puts "= #{idx}"
+ system("#{Ruby} -r ev-#{idx}/yarvcore -e 'puts YARVCore::OPTS'")
+ }
+end
+
+def bench_each idx
+ puts "= #{idx}"
+ 5.times{|count|
+ print count
+ FileUtils.cd(dirname(idx)){
+ exec_cmd("make benchmark OPT=-y ITEMS=#{ENV['ITEMS']} > ../b#{idx}-#{count}")
+ }
+ }
+ puts
+end
+
+def bench
+ # return bench_each(6)
+ Configs.each_with_index{|c, idx|
+ bench_each idx
+ }
+end
+
+def parse_result data
+ flag = false
+ stat = []
+ data.each{|line|
+ if flag
+ if /(\w+)\t([\d\.]+)/ =~ line
+ stat << [$1, $2.to_f]
+ else
+ raise "not a data"
+ end
+
+ end
+ if /benchmark summary/ =~ line
+ flag = true
+ end
+ }
+ stat
+end
+
+def calc_each data
+ data.sort!
+ data.pop # remove max
+ data.shift # remove min
+
+ data.inject(0.0){|res, e|
+ res += e
+ } / data.size
+end
+
+def calc_stat stats
+ stat = []
+ stats[0].each_with_index{|e, idx|
+ bm = e[0]
+ vals = stats.map{|st|
+ st[idx][1]
+ }
+ [bm, calc_each(vals)]
+ }
+end
+
+def stat
+ total = []
+ Configs.each_with_index{|c, idx|
+ stats = []
+ 5.times{|count|
+ file = "b#{idx}-#{count}"
+ # p file
+ open(file){|f|
+ stats << parse_result(f.read)
+ }
+ }
+ # merge stats
+ total << calc_stat(stats)
+ total
+ }
+ # pp total
+ total[0].each_with_index{|e, idx|
+ bm = e[0]
+ # print "#{bm}\t"
+ total.each{|st|
+ print st[idx][1], "\t"
+ }
+ puts
+ }
+end
+
+ARGV.each{|cmd|
+ case cmd
+ when 'build'
+ build
+ when 'check'
+ check
+ when 'bench'
+ bench
+ when 'stat'
+ stat
+ else
+ raise
+ end
+}
+
diff --git a/tool/getrev.rb b/tool/getrev.rb
new file mode 100644
index 0000000..1d24a17
--- /dev/null
+++ b/tool/getrev.rb
@@ -0,0 +1,13 @@
+str = ARGF.gets
+if /ChangeLog (\d+)/ =~ str
+ puts %Q{char *rev = "#{$1}";}
+else
+ raise
+end
+
+if /ChangeLog \d+ ([\d-]+)/ =~ str
+ puts %Q{char *date = "#{$1}";}
+else
+ raise
+end
+
diff --git a/tool/insns2vm.rb b/tool/insns2vm.rb
new file mode 100644
index 0000000..6270f51
--- /dev/null
+++ b/tool/insns2vm.rb
@@ -0,0 +1,1220 @@
+#!/usr/bin/env ruby
+#
+#
+#
+# $verbose = true
+# $use_const = true
+
+require 'pp'
+require 'erb'
+
+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 == 'num_t'
+ ret << " unsigned long #{v} = FIX2INT(opes[#{i}]);\n"
+ end
+ }
+ @defopes.each_with_index{|((t, var), val), i|
+ if t == '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
+ @insns = []
+ @insn_map = {}
+
+ load_insns_def file
+
+ load_opt_operand_def optopfile
+ load_insn_unification_def uniffile
+ make_stackcaching_insns if $opts['OPT_STACK_CACHING']
+ end
+
+ attr_reader :insns
+ attr_reader :insn_map
+
+ 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 = ''
+
+ 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)
+ 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
+ open(file){|f| f.each{|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
+ open(file){|f| f.each{|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 $opts['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)
+ ret << "#line #{line+1} \"#{$insns_def}\"" << "\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 = File.read(File.join($srcdir, '/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(File.read($srcdir + '/template/vmtc.inc.tmpl')).result(binding)
+ end
+
+
+ ###################################################################
+ # insns_info.inc
+
+ def op2typesig op
+ case op
+ when /^OFFSET/
+ "TS_OFFSET"
+ when /^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(File.read($srcdir + '/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(File.read($srcdir + '/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(File.read($srcdir + '/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/, /^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(File.read($srcdir + '/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
+ require 'pp'
+
+ 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(File.read($srcdir + '/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(File.read($srcdir + '/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(File.read($srcdir + '/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(File.read($srcdir + '/template/yarvarch.ja')).result(binding)
+ end
+
+ def desc_en
+ d = desc :e
+ ERB.new(File.read($srcdir + '/template/yarvarch.en')).result(binding)
+ end
+
+ def vm_macro_inc
+ ret = ''
+ flag = false
+ File.read($srcdir + '/vm_macro.def').each_line{|line|
+ line.rstrip!
+ if /^MACRO\s/ =~ line
+ line.sub!(/^MACRO/, '#define')
+ flag = true
+ elsif /^\}/ =~ line
+ flag = false
+ end
+
+ ret << line + (flag ? " \\" : '') + "\n"
+ }
+ ret
+ 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,
+ 'vm_macro.inc' => :vm_macro_inc,
+ }
+
+ def self.make_sources insns_def, opopt_def, unif_def, args = []
+ insns = InsnsDef.new(insns_def, opopt_def, unif_def)
+
+ args = Files.keys if args.empty?
+
+ args.each{|fn|
+ s = Files[fn]
+
+ open(fn, 'w'){|f|
+ f.puts(insns.__send__(s))
+ }
+ }
+ end
+end
+
+
+
+
+##############################################
+files = []
+$opts = {}
+$srcdir = '.'
+insns_def = 'insns.def'
+opope_def = 'opt_operand.def'
+unif_def = 'opt_insn_unif.def'
+
+ARGV.each{|e|
+ case e
+ when /\A\-D(\w+)/
+ $opts[$1] = true
+ when /\A--srcdir=(.+)/
+ $srcdir = $1
+ when /\A--insnsdef=(.+)/
+ insns_def = $1
+ when /\A--opt-operanddef=(.+)/
+ opope_def = $1
+ when /\A--opt-insnunifdef=(.+)/
+ unif_def = $1
+ when /\A-/
+ # ignore
+ else
+ files << e
+ end
+}
+
+optfile = File.join(Dir.pwd, 'vm_opts.h')
+basefile = File.join($srcdir, 'vm_opts.h.base')
+if !FileTest.exist?(optfile) || File.mtime(optfile) < File.mtime(basefile)
+ require 'fileutils'
+ FileUtils.cp(File.join($srcdir, 'vm_opts.h.base'), optfile)
+end
+
+if optfile
+ open(optfile){|f|
+ f.each_line{|line|
+ if /^\#define\s+(OPT_[A-Z_]+)\s+1/ =~ line
+ $opts[$1] = true
+ end
+ }
+ }
+end
+
+$insns_def = File.join($srcdir, insns_def)
+$opope_def = File.join($srcdir, opope_def)
+$unif_def = File.join($srcdir, unif_def)
+
+def insns_def_new
+ InsnsDef.new $insns_def, $opope_def, $unif_def
+end
+
+if $0 == __FILE__
+ InsnsDef.make_sources $insns_def, $opope_def, $unif_def, files
+end
+
diff --git a/tool/makedocs.rb b/tool/makedocs.rb
new file mode 100644
index 0000000..56cb215
--- /dev/null
+++ b/tool/makedocs.rb
@@ -0,0 +1,62 @@
+#!/usr/bin/env ruby
+#
+#
+
+require 'rb/insns2vm.rb'
+insns = insns_def_new
+
+{ # docs
+ '/doc/yarvarch.ja' => :desc_ja,
+ '/doc/yarvarch.en' => :desc_en,
+}.each{|fn, s|
+ fn = $srcdir + fn
+ p fn
+ open(fn, 'w'){|f|
+ f.puts(insns.__send__(s))
+ }
+}
+
+def chg ary
+ if ary.empty?
+ return '&nbsp;'
+ end
+
+ ary.map{|e|
+ if e[0] == '...'
+ '...'
+ else
+ e.join(' ')
+ end
+ e[1]
+ }.join(', ')
+end
+
+open($srcdir + '/doc/insnstbl.html', 'w'){|f|
+ tbl = ''
+ type = nil
+ insns.each_with_index{|insn, i|
+ c = insn.comm[:c]
+ if type != c
+ stype = c
+ type = c
+ end
+
+ tbl << "<tr>\n"
+ tbl << "<td>#{stype}</td>"
+ tbl << "<td>#{i}</td>"
+ tbl << "<td>#{insn.name}</td>"
+ tbl << "<td>#{chg insn.opes}</td>"
+ tbl << "<td>#{chg insn.pops.reverse}</td>"
+ tbl << "<td> =&gt; </td>"
+ tbl << "<td>#{chg insn.rets.reverse}</td>"
+ tbl << "</tr>\n"
+ }
+ f.puts ERB.new(File.read($srcdir + '/template/insnstbl.html')).result(binding)
+}
+
+begin
+ system('t2n.bat --tmpl doc.tmpl ../doc/yarvarch.ja > ../doc/yarvarch.ja.html')
+ system('t2n.bat --tmpl doc.tmpl ../doc/yarvarch.en > ../doc/yarvarch.en.html')
+rescue
+end
+
diff --git a/tool/parse.rb b/tool/parse.rb
new file mode 100644
index 0000000..15e7f77
--- /dev/null
+++ b/tool/parse.rb
@@ -0,0 +1,13 @@
+$file = ARGV[0]
+$str = ARGF.read.sub(/^__END__.*\z/m, '')
+puts '# ' + '-' * 70
+puts "# target program: "
+puts '# ' + '-' * 70
+puts $str
+puts '# ' + '-' * 70
+
+$parsed = YARVCore::InstructionSequence.compile_file($file)
+puts "# disasm result: "
+puts '# ' + '-' * 70
+puts $parsed.disasm
+puts '# ' + '-' * 70
diff --git a/tool/runruby.rb b/tool/runruby.rb
new file mode 100644
index 0000000..9de75cd
--- /dev/null
+++ b/tool/runruby.rb
@@ -0,0 +1,4 @@
+require 'rbconfig'
+$:.unshift File.join('.ext', Config::CONFIG['arch'])
+$:.unshift '.ext'
+load ARGV[0]
diff --git a/tool/vtlh.rb b/tool/vtlh.rb
new file mode 100644
index 0000000..fcd3630
--- /dev/null
+++ b/tool/vtlh.rb
@@ -0,0 +1,15 @@
+# ARGF = open('ha')
+cd = `pwd`.chomp + '/'
+ARGF.each{|line|
+ if /^0x([a-z0-9]+),/ =~ line
+ stat = line.split(',')
+ addr = stat[0].hex + 0x00400000
+ retired = stat[2].to_i
+ ticks = stat[3].to_i
+
+ src = `addr2line -e miniruby.exe #{addr.to_s(16)}`.chomp
+ src.sub!(cd, '')
+ puts '%-40s 0x%08x %8d %8d' % [src, addr, retired, ticks]
+ end
+}
+