diff options
author | yugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-08-25 15:02:05 +0000 |
---|---|---|
committer | yugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-08-25 15:02:05 +0000 |
commit | 0dc342de848a642ecce8db697b8fecd83a63e117 (patch) | |
tree | 2b7ed4724aff1f86073e4740134bda9c4aac1a39 /trunk/tool | |
parent | ef70cf7138ab8034b5b806f466e4b484b24f0f88 (diff) |
added tag v1_9_0_4
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_9_0_4@18845 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'trunk/tool')
-rw-r--r-- | trunk/tool/asm_parse.rb | 51 | ||||
-rwxr-xr-x | trunk/tool/build-transcode | 16 | ||||
-rw-r--r-- | trunk/tool/compile_prelude.rb | 96 | ||||
-rw-r--r-- | trunk/tool/eval.rb | 161 | ||||
-rwxr-xr-x | trunk/tool/ifchange | 17 | ||||
-rwxr-xr-x | trunk/tool/insns2vm.rb | 15 | ||||
-rw-r--r-- | trunk/tool/instruction.rb | 1385 | ||||
-rwxr-xr-x | trunk/tool/make-snapshot | 183 | ||||
-rwxr-xr-x | trunk/tool/node_name.rb | 4 | ||||
-rw-r--r-- | trunk/tool/parse.rb | 13 | ||||
-rw-r--r-- | trunk/tool/transcode-tblgen.rb | 637 | ||||
-rw-r--r-- | trunk/tool/vtlh.rb | 15 | ||||
-rwxr-xr-x | trunk/tool/ytab.sed | 30 |
13 files changed, 2623 insertions, 0 deletions
diff --git a/trunk/tool/asm_parse.rb b/trunk/tool/asm_parse.rb new file mode 100644 index 0000000000..f3d7f73c99 --- /dev/null +++ b/trunk/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/trunk/tool/build-transcode b/trunk/tool/build-transcode new file mode 100755 index 0000000000..fa71155530 --- /dev/null +++ b/trunk/tool/build-transcode @@ -0,0 +1,16 @@ +#!/bin/sh + +[ "$1" -a -d "$1" ] && { cd "$1" || exit $?; } && shift +[ "$#" = 0 ] && set enc/trans/*.trans +for src; do + case "$src" in + *.trans) + c="`dirname $src`/`basename $src .trans`.c" + ${BASERUBY-ruby} tool/transcode-tblgen.rb -vo "$c" "$src" + ;; + *) + echo "$0: don't know how to deal with $src" + continue + ;; + esac +done diff --git a/trunk/tool/compile_prelude.rb b/trunk/tool/compile_prelude.rb new file mode 100644 index 0000000000..70197aebf3 --- /dev/null +++ b/trunk/tool/compile_prelude.rb @@ -0,0 +1,96 @@ +# This file is interpreted by $(BASERUBY) and miniruby. +# $(BASERUBY) is used for miniprelude.c. +# miniruby is used for prelude.c. +# Since $(BASERUBY) may be older than Ruby 1.9, +# Ruby 1.9 feature should not be used. + +$:.unshift(File.expand_path("../..", __FILE__)) + +preludes = ARGV.dup +outfile = preludes.pop +init_name = outfile[/\w+(?=_prelude.c\b)/] || 'prelude' + +C_ESC = { + "\\" => "\\\\", + '"' => '\"', + "\n" => '\n', +} + +0x00.upto(0x1f) {|ch| C_ESC[[ch].pack("C")] ||= "\\%03o" % ch } +0x7f.upto(0xff) {|ch| C_ESC[[ch].pack("C")] = "\\%03o" % ch } +C_ESC_PAT = Regexp.union(*C_ESC.keys) + +def c_esc(str) + '"' + str.gsub(C_ESC_PAT) { C_ESC[$&] } + '"' +end + +mkconf = nil +setup_ruby_prefix = nil +teardown_ruby_prefix = nil +lines_list = preludes.map {|filename| + lines = [] + need_ruby_prefix = false + File.readlines(filename).each {|line| + line.gsub!(/RbConfig::CONFIG\["(\w+)"\]/) { + key = $1 + unless mkconf + require 'rbconfig' + mkconf = RbConfig::MAKEFILE_CONFIG.merge('prefix'=>'#{TMP_RUBY_PREFIX}') + setup_ruby_prefix = "TMP_RUBY_PREFIX = $:.reverse.find{|e|e!=\".\"}.sub(%r{(.*)/lib/.*}m, \"\\\\1\")\n" + teardown_ruby_prefix = 'Object.class_eval { remove_const "TMP_RUBY_PREFIX" }' + end + if RbConfig::MAKEFILE_CONFIG.has_key? key + val = RbConfig.expand("$(#{key})", mkconf) + need_ruby_prefix = true if /\A\#\{TMP_RUBY_PREFIX\}/ =~ val + c_esc(val) + else + "nil" + end + } + lines << c_esc(line) + } + setup_lines = [] + if need_ruby_prefix + setup_lines << c_esc(setup_ruby_prefix) + lines << c_esc(teardown_ruby_prefix) + end + [setup_lines, lines] +} + +require 'erb' + +tmp = ERB.new(<<'EOS', nil, '%').result(binding) +#include "ruby/ruby.h" +#include "vm_core.h" + +% preludes.zip(lines_list).each_with_index {|(prelude, (setup_lines, lines)), i| +static const char prelude_name<%=i%>[] = <%=c_esc(File.basename(prelude))%>; +static const char prelude_code<%=i%>[] = +% (setup_lines+lines).each {|line| +<%=line%> +% } +; +% } + +void +Init_<%=init_name%>(void) +{ +% lines_list.each_with_index {|(setup_lines, lines), i| + rb_iseq_eval(rb_iseq_compile( + rb_str_new(prelude_code<%=i%>, sizeof(prelude_code<%=i%>) - 1), + rb_str_new(prelude_name<%=i%>, sizeof(prelude_name<%=i%>) - 1), + INT2FIX(<%=1-setup_lines.length%>))); + +% } +#if 0 +% preludes.length.times {|i| + puts(prelude_code<%=i%>); +% } +#endif +} +EOS + +open(outfile, 'w'){|f| + f << tmp +} + diff --git a/trunk/tool/eval.rb b/trunk/tool/eval.rb new file mode 100644 index 0000000000..906ba9c23c --- /dev/null +++ b/trunk/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/trunk/tool/ifchange b/trunk/tool/ifchange new file mode 100755 index 0000000000..544513ad15 --- /dev/null +++ b/trunk/tool/ifchange @@ -0,0 +1,17 @@ +#!/bin/sh +# usage: ifchange target temporary + +target="$1" +temp="$2" +if [ "$temp" = - ]; then + temp="tmpdata$$.tmp~" + cat > "$temp" || exit $? + trap 'rm -f "$temp"' 0 +fi +if cmp "$target" "$temp" >/dev/null 2>&1; then + echo "$target unchanged" + rm -f "$temp" +else + echo "$target updated" + mv -f "$temp" "$target" +fi diff --git a/trunk/tool/insns2vm.rb b/trunk/tool/insns2vm.rb new file mode 100755 index 0000000000..47aba50c1e --- /dev/null +++ b/trunk/tool/insns2vm.rb @@ -0,0 +1,15 @@ +#!ruby -Kn + +require 'optparse' + +Version = %w$Revision: 11626 $[1..-1] + +require "#{File.join(File.dirname(__FILE__), 'instruction')}" + +if $0 == __FILE__ + opts = ARGV.options + maker = RubyVM::SourceCodeGenerator.def_options(opts) + files = opts.parse! + generator = maker.call + generator.generate(files) +end diff --git a/trunk/tool/instruction.rb b/trunk/tool/instruction.rb new file mode 100644 index 0000000000..5e5eb0b78a --- /dev/null +++ b/trunk/tool/instruction.rb @@ -0,0 +1,1385 @@ +# +# +# + +require 'erb' + +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 + 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' && ((re = /\b#{v}\b/n) =~ @sp_inc || + @defopes.any?{|t, val| re =~ val}) + ret << " #{t} #{v} = FIX2INT(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"] || '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 + 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 = '' + + sp_inc = rets_str[%r"//\s*(.+)", 1] + + 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 + + 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 + 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 + comment " /* prepare stack status */" + + push_ba = insn.pushsc + raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 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 + + re = /\b#{var}\b/n + if re =~ insn.body or re =~ insn.sp_inc or insn.rets.any?{|t, v| re =~ v} + 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 " USAGE_ANALYSIS_INSN(BIN(#{insn.name}));" + insn.opes.each_with_index{|op, i| + commit " USAGE_ANALYSIS_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_hader_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 make_footer_stack_val insn + comment " /* push stack val */" + + insn.rets.reverse_each{|v| + if v[1] == '...' + break + end + 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 + commit "INSN_ENTRY(#{insn.name}){" + make_header_prepare_stack insn + commit "{" + 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_hader_debug insn + make_header_pc insn + make_header_popn insn + make_header_defines insn + make_header_analysis insn + commit "{" + end + + def make_footer insn + make_footer_stack_val insn + make_footer_default_operands insn + make_footer_undefs insn + 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 + insn.body + end + make_footer(insn) + end + end + end + + ################################################################### + # vmtc.inc + class VmTCIncGenerator < SourceCodeGenerator + def generate + + insns_table = build_string do + @insns.each{|insn| + commit " LABEL_PTR(#{insn.name})," + } + end + + insn_end_table = build_string do + @insns.each{|insn| + commit " ELABEL_PTR(#{insn.name}),\n" + } + end + + 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" + 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_DINDEX' => 'D', + 'TS_VALUE' => 'V', + 'TS_ID' => 'I', + 'TS_GENTRY' => 'G', + 'TS_IC' => 'C', + 'TS_CDHASH' => 'H', + 'TS_ISEQ' => 'S', + 'TS_VARIABLE' => '.', + 'TS_FUNCPTR' => 'F', + } + + # 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 + i=0 + insns = build_string do + @insns.each{|insn| + commit " %-30s = %d,\n" % ["BIN(#{insn.name})", i] + i+=1 + } + end + + ERB.new(vpath.read('template/insns.inc.tmpl')).result(binding) + end + end + + ################################################################### + # minsns.inc + class MInsnsIncGenerator < SourceCodeGenerator + def generate + i=0 + defs = build_string do + @insns.each{|insn| + commit " rb_define_const(mYarvInsns, %-30s, INT2FIX(%d));\n" % + ["\"I#{insn.name}\"", i] + i+=1 + } + end + 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/, /^rb_insn_func_t/ + 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 + } + + rule = build_string do + opt_insns_map.each{|originsn, optinsns| + commit "case BIN(#{originsn.name}):" + + optinsns.sort_by{|opti| + opti.defopes.find_all{|e| e[1] == '*'}.size + }.each{|opti| + commit " if(" + i = 0 + commit " " + opti.defopes.map{|opinfo| + i += 1 + next if opinfo[1] == '*' + "insnobj->operands[#{i-1}] == #{val_as_type(opinfo)}" + }.compact.join('&& ') + commit " ){" + idx = 0 + n = 0 + opti.defopes.each{|opinfo| + if opinfo[1] == '*' + if idx != n + commit " insnobj->operands[#{idx}] = insnobj->operands[#{n}];" + end + idx += 1 + else + # skip + end + n += 1 + } + commit " insnobj->insn_id = BIN(#{opti.name});" + commit " insnobj->operand_size = #{idx};" + commit " break;\n }\n" + } + commit " break;"; + } + end + + 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 const 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 const int *const 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 const int *const *const 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(output_path(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("-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.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/trunk/tool/make-snapshot b/trunk/tool/make-snapshot new file mode 100755 index 0000000000..65f5bd024d --- /dev/null +++ b/trunk/tool/make-snapshot @@ -0,0 +1,183 @@ +#!/usr/bin/ruby -s +require 'uri' +require 'digest/md5' +require 'digest/sha2' +require 'fileutils' +require 'tmpdir' +STDOUT.sync = true + +ENV["LC_ALL"] = ENV["LANG"] = "C" +SVNURL = URI.parse("http://svn.ruby-lang.org/repos/ruby/") +RUBY_VERSION_PATTERN = /^\#define\s+RUBY_VERSION\s+"([\d.]+)"/ + +ENV["VPATH"] ||= "include/ruby" +YACC = ENV["YACC"] ||= "bison" +ENV["BASERUBY"] ||= "ruby" +ENV["RUBY"] ||= "ruby" +ENV["MV"] ||= "mv" +ENV["MINIRUBY"] ||= "ruby" + +$patch_file &&= File.expand_path($patch_file) +path = ENV["PATH"].split(File::PATH_SEPARATOR) +%w[YACC BASERUBY RUBY MV MINIRUBY].each do |var| + cmd = ENV[var] + unless path.any? {|dir| + file = File.join(dir, cmd) + File.file?(file) and File.executable?(file) + } + abort "#{File.basename $0}: #{var} command not found - #{cmd}" + end +end + +unless destdir = ARGV.shift + abort "usage: #{File.basename $0} new-directory-to-save [version ...]" +end +revisions = ARGV.empty? ? ["trunk"] : ARGV +unless tmp = $exported + FileUtils.mkpath(destdir) + destdir = File.expand_path(destdir) + tmp = Dir.mktmpdir("ruby-snapshot") + FileUtils.mkpath(tmp) + at_exit { + Dir.chdir "/" + FileUtils.rm_rf(tmp) + } unless $keep_temp +end +Dir.chdir tmp + +def package(rev, destdir) + patchlevel = false + case rev + when /\Atrunk\z/, /\Abranches\//, /\Atags\// + url = SVNURL + rev + when /\Astable\z/ + url = SVNURL + "branches/" + url = url + `svn ls #{url}`[/.*^(ruby_\d+_\d+)\//m, 1] + when /\A\(.*\..*\..*\)-/ + patchlevel = true + url = SVNURL + "tags/v#{rev.sub(/-p?/, '_').tr('.', '_')}" + when /\./ + url = SVNURL + "branches/ruby_#{rev.tr('.', '_')}" + else + warn "#{$0}: unknown version - #{rev}" + return + end + revision = `svn info #{url} 2>&1`[/Last Changed Rev: (\d+)/, 1] + version = nil + unless revision + url = SVNURL + "trunk" + version = `svn cat #{url + "version.h"}`[RUBY_VERSION_PATTERN, 1] + unless rev == version + warn "#{$0}: #{rev} not found" + return + end + revision = `svn info #{url}`[/Last Changed Rev: (\d+)/, 1] + end + unless $exported + puts "Exporting #{rev}@#{revision}" + IO.popen("svn export #{url} ruby") do |pipe| + pipe.each {|line| /^A/ =~ line or print line} + end + unless $?.success? + warn("Export failed") + return + end + end + + if !File.directory?(v = "ruby") + v = Dir.glob("ruby-*").select(&File.method(:directory?)) + v.size == 1 or abort "not exported" + v = v[0] + end + open("#{v}/revision.h", "wb") {|f| f.puts "#define RUBY_REVISION #{revision}"} + version ||= (versionhdr = IO.read("#{v}/version.h"))[RUBY_VERSION_PATTERN, 1] + version or return + if patchlevel + versionhdr ||= IO.read("#{v}/version.h") + patchlevel = versionhdr[/^\#define\s+RUBY_PATCHLEVEL\s+(\d+)/, 1] + tag = (patchlevel ? "p#{patchlevel}" : "r#{revision}") + else + tag = "r#{revision}" + end + v = "ruby-#{version}-#{tag}" + File.directory?(v) or File.rename "ruby", v + system("patch -d #{v} -p0 -i #{$patch_file}") if $patch_file + def (clean = []).add(n) push(n); n end + Dir.chdir(v) do + File.open(clean.add("cross.rb"), "w") {|f| f.puts "CROSS_COMPILING=true"} + unless File.exist?("configure") + print "creating configure..." + unless system("autoconf") + puts " failed" + return + end + puts " done" + end + clean.add("autom4te.cache") + print "creating prerequisites..." + if File.file?("common.mk") && /^prereq/ =~ commonmk = IO.read("common.mk") + puts + extout = clean.add('tmp') + File.open(clean.add("config.status"), "w") {|f| + f.puts "s,@configure_args@,|#_!!_#|,g" + f.puts "s,@EXTOUT@,|#_!!_#|#{extout},g" + f.puts "s,@bindir@,|#_!!_#|,g" + f.puts "s,@ruby_install_name@,|#_!!_#|,g" + f.puts "s,@ARCH_FLAG@,|#_!!_#|,g" + f.puts "s,@CFLAGS@,|#_!!_#|,g" + f.puts "s,@CPPFLAGS@,|#_!!_#|,g" + f.puts "s,@LDFLAGS@,|#_!!_#|,g" + f.puts "s,@DLDFLAGS@,|#_!!_#|,g" + f.puts "s,@LIBEXT@,|#_!!_#|a,g" + f.puts "s,@OBJEXT@,|#_!!_#|o,g" + f.puts "s,@LIBRUBY@,|#_!!_#|liburyb.a,g" + f.puts "s,@LIBRUBY_A@,|#_!!_#|liburyb.a,g" + } + FileUtils.mkpath(hdrdir = "#{extout}/include/ruby") + File.open("#{hdrdir}/config.h", "w") {} + miniruby = ENV['MINIRUBY'] + " -rcross" + IO.popen("make -f - prereq srcdir=. IFCHANGE=tool/ifchange 'MINIRUBY=#{miniruby}'", "w") do |f| + f.puts(IO.read("Makefile.in")[/^lex\.c.*?^$/m]) + f.puts(commonmk.gsub(/\{[^{}]*\}/, "")) + end + clean.push("rbconfig.rb", ".rbconfig.time") + print "prerequisites" + else + system("#{YACC} -o parse.c parse.y") + end + FileUtils.rm_rf(clean) + unless $?.success? + puts " failed" + return + end + puts " done" + end + + return [["bzip tarball", ".tar.bz2", %w"tar cjf"], + ["gzip tarball", ".tar.gz", %w"tar czf"], + ["zip archive", ".zip", %w"zip -qr"] + ].collect do |mesg, ext, cmd| + file = "#{destdir}/#{v}#{ext}" + print "creating #{mesg}... #{file}" + if system(*(cmd + [file, v])) + puts " done" + file + else + puts " failed" + nil + end + end.compact +ensure + FileUtils.rm_rf(v) if v and !$exported and !$keep_temp +end + +revisions.collect {|rev| package(rev, destdir)}.flatten.each do |name| + name or next + str = open(name, "rb") {|f| f.read} + md5 = Digest::MD5.hexdigest str + sha = Digest::SHA256.hexdigest str + puts "MD5(#{name})= #{md5}" + puts "SHA256(#{name})= #{sha}" + puts "SIZE(name)= #{str.size}" + puts +end diff --git a/trunk/tool/node_name.rb b/trunk/tool/node_name.rb new file mode 100755 index 0000000000..5d39e9f5cc --- /dev/null +++ b/trunk/tool/node_name.rb @@ -0,0 +1,4 @@ +#! ./miniruby -n +if ~/enum node_type \{/..~/^\};/ + ~/(NODE_.+),/ and puts(" case #{$1}:\n\treturn \"#{$1}\";") +end diff --git a/trunk/tool/parse.rb b/trunk/tool/parse.rb new file mode 100644 index 0000000000..6243d7aa8e --- /dev/null +++ b/trunk/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 = RubyVM::InstructionSequence.compile_file($file) +puts "# disasm result: " +puts '# ' + '-' * 70 +puts $parsed.disasm +puts '# ' + '-' * 70 diff --git a/trunk/tool/transcode-tblgen.rb b/trunk/tool/transcode-tblgen.rb new file mode 100644 index 0000000000..f79fc551ec --- /dev/null +++ b/trunk/tool/transcode-tblgen.rb @@ -0,0 +1,637 @@ +require 'optparse' +require 'erb' +require 'fileutils' + +C_ESC = { + "\\" => "\\\\", + '"' => '\"', + "\n" => '\n', +} + +0x00.upto(0x1f) {|ch| C_ESC[[ch].pack("C")] ||= "\\%03o" % ch } +0x7f.upto(0xff) {|ch| C_ESC[[ch].pack("C")] = "\\%03o" % ch } +C_ESC_PAT = Regexp.union(*C_ESC.keys) + +def c_esc(str) + '"' + str.gsub(C_ESC_PAT) { C_ESC[$&] } + '"' +end + +class StrSet + def self.parse(pattern) + if /\A\s*(([0-9a-f][0-9a-f]|\{([0-9a-f][0-9a-f]|[0-9a-f][0-9a-f]-[0-9a-f][0-9a-f])(,([0-9a-f][0-9a-f]|[0-9a-f][0-9a-f]-[0-9a-f][0-9a-f]))*\})+(\s+|\z))*\z/i !~ pattern + raise ArgumentError, "invalid pattern: #{pattern.inspect}" + end + result = [] + pattern.scan(/\S+/) {|seq| + seq_result = [] + while !seq.empty? + if /\A([0-9a-f][0-9a-f])/i =~ seq + byte = $1.to_i(16) + seq_result << [byte..byte] + seq = $' + elsif /\A\{([^\}]+)\}/ =~ seq + set = $1 + seq = $' + set_result = [] + set.scan(/[^,]+/) {|range| + if /\A([0-9a-f][0-9a-f])-([0-9a-f][0-9a-f])\z/ =~ range + b = $1.to_i(16) + e = $2.to_i(16) + set_result << (b..e) + elsif /\A([0-9a-f][0-9a-f])\z/ =~ range + byte = $1.to_i(16) + set_result << (byte..byte) + else + raise "invalid range: #{range.inspect}" + end + } + seq_result << set_result + else + raise "invalid sequence: #{seq.inspect}" + end + end + result << seq_result + } + self.new(result) + end + + def initialize(pat) + @pat = pat + end + + def hash + @pat.hash + end + + def eql?(other) + self.class == other.class && + @pat == other.instance_eval { @pat } + end + + alias == eql? + + def to_s + if @pat.empty? + "(empset)" + else + @pat.map {|seq| + if seq.empty? + "(empstr)" + else + seq.map {|byteset| + if byteset.length == 1 && byteset[0].begin == byteset[0].end + "%02x" % byteset[0].begin + else + "{" + + byteset.map {|range| + if range.begin == range.end + "%02x" % range.begin + else + "%02x-%02x" % [range.begin, range.end] + end + }.join(',') + + "}" + end + }.join('') + end + }.join(' ') + end + end + + def inspect + "\#<#{self.class}: #{self.to_s}>" + end + + def min_length + if @pat.empty? + nil + else + @pat.map {|seq| seq.length }.min + end + end + + def max_length + if @pat.empty? + nil + else + @pat.map {|seq| seq.length }.max + end + end + + def emptyable? + @pat.any? {|seq| + seq.empty? + } + end + + def first_bytes + result = {} + @pat.each {|seq| + next if seq.empty? + seq.first.each {|range| + range.each {|byte| + result[byte] = true + } + } + } + result.keys.sort + end + + def each_firstbyte + h = {} + @pat.each {|seq| + next if seq.empty? + seq.first.each {|range| + range.each {|byte| + (h[byte] ||= []) << seq[1..-1] + } + } + } + h.keys.sort.each {|byte| + yield byte, StrSet.new(h[byte]) + } + end +end + +class ActionMap + def self.parse(hash) + h = {} + hash.each {|pat, action| + h[StrSet.parse(pat)] = action + } + self.new(h) + end + + def initialize(h) + @map = h + end + + def hash + hash = 0 + @map.each {|k,v| + hash ^= k.hash ^ v.hash + } + hash + end + + def eql?(other) + self.class == other.class && + @map == other.instance_eval { @map } + end + + alias == eql? + + def inspect + "\#<#{self.class}:" + + @map.map {|k, v| " [" + k.to_s + "]=>" + v.inspect }.join('') + + ">" + end + + def max_input_length + @map.keys.map {|k| k.max_length }.max + end + + def empty_action + @map.each {|ss, action| + return action if ss.emptyable? + } + nil + end + + def each_firstbyte(valid_encoding=nil) + h = {} + @map.each {|ss, action| + if ss.emptyable? + raise "emptyable pattern" + else + ss.each_firstbyte {|byte, rest| + h[byte] ||= {} + if h[byte][rest] + raise "ambiguous" + end + h[byte][rest] = action + } + end + } + if valid_encoding + valid_encoding.each_firstbyte {|byte, rest| + if h[byte] + am = ActionMap.new(h[byte]) + yield byte, am, rest + else + am = ActionMap.new(rest => :undef) + yield byte, am, nil + end + } + else + h.keys.sort.each {|byte| + am = ActionMap.new(h[byte]) + yield byte, am, nil + } + end + end + + OffsetsMemo = {} + InfosMemo = {} + + def format_offsets(min, max, offsets) + offsets = offsets[min..max] + code = "{ %d, %d,\n" % [min, max] + 0.step(offsets.length-1,16) {|i| + code << " " + code << offsets[i,8].map {|off| "%3d," % off.to_s }.join('') + if i+8 < offsets.length + code << " " + code << offsets[i+8,8].map {|off| "%3d," % off.to_s }.join('') + end + code << "\n" + } + code << '}' + code + end + + def generate_info(info) + case info + when :nomap + "NOMAP" + when :undef + "UNDEF" + when :invalid + "INVALID" + when :func_ii + "FUNii" + when :func_si + "FUNsi" + when :func_io + "FUNio" + when :func_so + "FUNso" + when /\A([0-9a-f][0-9a-f])\z/i + "o1(0x#$1)" + when /\A([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])\z/i + "o2(0x#$1,0x#$2)" + when /\A([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])\z/i + "o3(0x#$1,0x#$2,0x#$3)" + when /\A([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])\z/i + "o4(0x#$1,0x#$2,0x#$3,0x#$4)" + when /\A&/ # pointer to BYTE_LOOKUP structure + info.to_s + else + raise "unexpected action: #{info.inspect}" + end + end + + def format_infos(infos) + infos = infos.map {|info| generate_info(info) } + maxlen = infos.map {|info| info.length }.max + columns = maxlen <= 16 ? 4 : 2 + code = "{\n" + 0.step(infos.length-1, columns) {|i| + code << " " + is = infos[i,columns] + is.each {|info| + code << sprintf(" %#{maxlen}s,", info) + } + code << "\n" + } + code << "}" + code + end + + def generate_lookup_node(name, table) + offsets = [] + infos = [] + infomap = {} + min = max = nil + table.each_with_index {|action, byte| + action ||= :invalid + if action != :invalid + min = byte if !min + max = byte + end + unless o = infomap[action] + infomap[action] = o = infos.length + infos[o] = action + end + offsets[byte] = o + } + if !min + min = max = 0 + end + + offsets_key = [min, max, offsets[min..max]] + if n = OffsetsMemo[offsets_key] + offsets_name = n + offsets_code = '' + else + offsets_name = "#{name}_offsets" + offsets_code = <<"End" +static const unsigned char +#{offsets_name}[#{2+max-min+1}] = #{format_offsets(min,max,offsets)}; +End + OffsetsMemo[offsets_key] = offsets_name + end + + if n = InfosMemo[infos] + infos_name = n + infos_code = '' + else + infos_name = "#{name}_infos" + infos_code = <<"End" +static const struct byte_lookup* const +#{infos_name}[#{infos.length}] = #{format_infos(infos)}; +End + InfosMemo[infos] = infos_name + end + + r = offsets_code + infos_code + <<"End" +static const BYTE_LOOKUP +#{name} = { + #{offsets_name}, + #{infos_name} +}; + +End + r + end + + PreMemo = {} + PostMemo = {} + NextName = "a" + + def generate_node(code, name_hint=nil, valid_encoding=nil) + if n = PreMemo[[self,valid_encoding]] + return n + end + + table = Array.new(0x100, :invalid) + each_firstbyte(valid_encoding) {|byte, rest, rest_valid_encoding| + if a = rest.empty_action + table[byte] = a + else + name_hint2 = nil + name_hint2 = "#{name_hint}_#{'%02X' % byte}" if name_hint + table[byte] = "&" + rest.generate_node(code, name_hint2, rest_valid_encoding) + end + } + + if n = PostMemo[table] + return n + end + + if !name_hint + name_hint = "fun_" + NextName.dup + NextName.succ! + end + + PreMemo[[self,valid_encoding]] = PostMemo[table] = name_hint + + code << generate_lookup_node(name_hint, table) + name_hint + end +end + +def encode_utf8(map) + r = [] + map.each {|k, v| + # integer means UTF-8 encoded sequence. + k = [k].pack("U").unpack("H*")[0].upcase if Integer === k + v = [v].pack("U").unpack("H*")[0].upcase if Integer === v + r << [k,v] + } + r +end + +def transcode_compile_tree(name, from, map) + map = encode_utf8(map) + h = {} + map.each {|k, v| + h[k] = v + } + am = ActionMap.parse(h) + + max_input = am.max_input_length + + if ValidEncoding[from] + valid_encoding = StrSet.parse(ValidEncoding[from]) + else + valid_encoding = nil + end + + code = '' + defined_name = am.generate_node(code, name, valid_encoding) + return defined_name, code, max_input +end + +TRANSCODERS = [] + +def transcode_tblgen(from, to, map) + STDERR.puts "converter from #{from} to #{to}" if VERBOSE_MODE + id_from = from.tr('^0-9A-Za-z', '_') + id_to = to.tr('^0-9A-Za-z', '_') + if from == "UTF-8" + tree_name = "to_#{id_to}" + elsif to == "UTF-8" + tree_name = "from_#{id_from}" + else + tree_name = "from_#{id_from}_to_#{id_to}" + end + map = encode_utf8(map) + real_tree_name, tree_code, max_input = transcode_compile_tree(tree_name, from, map) + transcoder_name = "rb_#{tree_name}" + TRANSCODERS << transcoder_name + input_unit_length = UnitLength[from] + max_output = map.map {|k,v| String === v ? v.length/2 : 1 }.max + transcoder_code = <<"End" +static const rb_transcoder +#{transcoder_name} = { + #{c_esc from}, #{c_esc to}, &#{real_tree_name}, + #{input_unit_length}, /* input_unit_length */ + #{max_input}, /* max_input */ + #{max_output}, /* max_output */ + stateless_converter, /* stateful_type */ + NULL, NULL, NULL, NULL, + NULL, NULL, NULL +}; +End + tree_code + "\n" + transcoder_code +end + +def transcode_generate_node(am, name_hint=nil) + STDERR.puts "converter for #{name_hint}" if VERBOSE_MODE + code = '' + am.generate_node(code, name_hint) + code +end + +def transcode_register_code + code = '' + TRANSCODERS.each {|transcoder_name| + code << " rb_register_transcoder(&#{transcoder_name});\n" + } + code +end + +UnitLength = { + 'UTF-16BE' => 2, + 'UTF-16LE' => 2, + 'UTF-32BE' => 4, + 'UTF-32LE' => 4, +} +UnitLength.default = 1 + +ValidEncoding = { + '1byte' => '{00-ff}', + '2byte' => '{00-ff}{00-ff}', + '4byte' => '{00-ff}{00-ff}{00-ff}{00-ff}', + 'US-ASCII' => '{00-7f}', + 'UTF-8' => '{00-7f} + {c2-df}{80-bf} + e0{a0-bf}{80-bf} + {e1-ec}{80-bf}{80-bf} + ed{80-9f}{80-bf} + {ee-ef}{80-bf}{80-bf} + f0{90-bf}{80-bf}{80-bf} + {f1-f3}{80-bf}{80-bf}{80-bf} + f4{80-8f}{80-bf}{80-bf}', + 'UTF-16BE' => '{00-d7,e0-ff}{00-ff} + {d8-db}{00-ff}{dc-df}{00-ff}', + 'UTF-16LE' => '{00-ff}{00-d7,e0-ff} + {00-ff}{d8-db}{00-ff}{dc-df}', + 'UTF-32BE' => '0000{00-d7,e0-ff}{00-ff} + 00{01-10}{00-ff}{00-ff}', + 'UTF-32LE' => '{00-ff}{00-d7,e0-ff}0000 + {00-ff}{00-ff}{01-10}00', + 'EUC-JP' => '{00-7f} + {a1-fe}{a1-fe} + 8e{a1-fe} + 8f{a1-fe}{a1-fe}', + 'CP51932' => '{00-7f} + {a1-fe}{a1-fe} + 8e{a1-fe}', + 'Shift_JIS' => '{00-7f} + {81-9f,e0-fc}{40-7e,80-fc} + {a1-df}', + 'EUC-KR' => '{00-7f} + {a1-fe}{a1-fe}', + 'CP949' => '{00-7f} + {81-fe}{41-5a,61-7a,81-fe}', + 'Big5' => '{00-7f} + {81-fe}{40-7e,a1-fe}', + 'EUC-TW' => '{00-7f} + {a1-fe}{a1-fe} + 8e{a1-b0}{a1-fe}{a1-fe}', + 'GBK' => '{00-80} + {81-fe}{40-7e,80-fe}', + 'GB18030' => '{00-7f} + {81-fe}{40-7e,80-fe} + {81-fe}{30-39}{81-fe}{30-39}', +} + +{ + 'ASCII-8BIT' => '1byte', + 'ISO-8859-1' => '1byte', + 'ISO-8859-2' => '1byte', + 'ISO-8859-3' => '1byte', + 'ISO-8859-4' => '1byte', + 'ISO-8859-5' => '1byte', + 'ISO-8859-6' => '1byte', + 'ISO-8859-7' => '1byte', + 'ISO-8859-8' => '1byte', + 'ISO-8859-9' => '1byte', + 'ISO-8859-10' => '1byte', + 'ISO-8859-11' => '1byte', + 'ISO-8859-13' => '1byte', + 'ISO-8859-14' => '1byte', + 'ISO-8859-15' => '1byte', + 'Windows-31J' => 'Shift_JIS', +}.each {|k, v| + ValidEncoding[k] = ValidEncoding.fetch(v) +} + +def make_signature(filename, src) + "src=#{filename.dump}, len=#{src.length}, checksum=#{src.sum}" +end + +output_filename = nil +verbose_mode = false +force_mode = false + +op = OptionParser.new +op.def_option("--help", "show help message") { puts op; exit 0 } +op.def_option("--verbose", "verbose mode") { verbose_mode = true } +op.def_option("--force", "force table generation") { force_mode = true } +op.def_option("--output=FILE", "specify output file") {|arg| output_filename = arg } +op.parse! + +VERBOSE_MODE = verbose_mode + +arg = ARGV.shift +dir = File.dirname(arg) +$:.unshift dir unless $:.include? dir +src = File.read(arg) +src.force_encoding("ascii-8bit") if src.respond_to? :force_encoding +this_script = File.read(__FILE__) +this_script.force_encoding("ascii-8bit") if this_script.respond_to? :force_encoding + +base_signature = "/* autogenerated. */\n" +base_signature << "/* #{make_signature(File.basename(__FILE__), this_script)} */\n" +base_signature << "/* #{make_signature(File.basename(arg), src)} */\n" + +if !force_mode && output_filename && File.readable?(output_filename) + old_signature = File.open(output_filename) {|f| f.gets("").chomp } + chk_signature = base_signature.dup + old_signature.each_line {|line| + if %r{/\* src="([0-9a-z_.-]+)",} =~ line + name = $1 + next if name == File.basename(arg) || name == File.basename(__FILE__) + path = File.join(dir, name) + if File.readable? path + chk_signature << "/* #{make_signature(name, File.read(path))} */\n" + end + end + } + if old_signature == chk_signature + now = Time.now + File.utime(now, now, output_filename) + STDERR.puts "already up-to-date: #{output_filename}" if VERBOSE_MODE + exit + end +end + +if VERBOSE_MODE + if output_filename + STDERR.puts "generating #{output_filename} ..." + end +end + +libs1 = $".dup +erb_result = ERB.new(src, nil, '%').result(binding) +libs2 = $".dup + +libs = libs2 - libs1 +lib_sigs = '' +libs.each {|lib| + lib = File.basename(lib) + path = File.join(dir, lib) + if File.readable? path + lib_sigs << "/* #{make_signature(lib, File.read(path))} */\n" + end +} + +result = '' +result << base_signature +result << lib_sigs +result << "\n" +result << erb_result +result << "\n" + +if output_filename + new_filename = output_filename + ".new" + FileUtils.mkdir_p(File.dirname(output_filename)) + File.open(new_filename, "wb") {|f| f << result } + File.rename(new_filename, output_filename) + STDERR.puts "done." if VERBOSE_MODE +else + print result +end diff --git a/trunk/tool/vtlh.rb b/trunk/tool/vtlh.rb new file mode 100644 index 0000000000..fcd3630821 --- /dev/null +++ b/trunk/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 +} + diff --git a/trunk/tool/ytab.sed b/trunk/tool/ytab.sed new file mode 100755 index 0000000000..17a57fe494 --- /dev/null +++ b/trunk/tool/ytab.sed @@ -0,0 +1,30 @@ +#!/bin/sed -f +/^int yydebug;/{ +i\ +#ifndef yydebug +a\ +#endif +} +/^yydestruct.*yymsg/,/#endif/{ + /^yydestruct/{ + /parser/!{ + h + s/^/ruby_parser_&/ + s/)$/, parser)/ + /\*/s/parser)$/struct parser_params *&/ + } + } + /^#endif/{ + x + /^./{ + i\ + struct parser_params *parser; + a\ +#define yydestruct(m, t, v) ruby_parser_yydestruct(m, t, v, parser) + } + x + } +} +s/^\([ ]*\)\(yyerror[ ]*([ ]*parser,\)/\1parser_\2/ +s!^ *extern char \*getenv();!/* & */! +s/^\(#.*\)".*\.tab\.c"/\1"parse.c"/ |