mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* vm_exec.h: declare two macros * START_OF_ORIGINAL_INSN() * DISPATCH_ORIGINAL_INSN() instead of inserting label and goto lines. For OPT_CALL_THREADED_CODE, first macro is empty and second macro is simply call the original insn function. * tool/instruction.rb: use above macros. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61120 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			1249 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			1249 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable file
		
	
	
	
	
#!./miniruby
 | 
						|
# -*- coding: us-ascii -*-
 | 
						|
#
 | 
						|
# This library is used by insns2vm.rb as part of autogenerating
 | 
						|
# instruction files with .inc extensions like insns.inc and vm.inc.
 | 
						|
 | 
						|
require 'erb'
 | 
						|
$:.unshift(File.dirname(__FILE__))
 | 
						|
require 'vpath'
 | 
						|
 | 
						|
class RubyVM
 | 
						|
  class Instruction
 | 
						|
    def initialize name, opes, pops, rets, comm, body, tvars, sp_inc,
 | 
						|
                   orig = self, defopes = [], type = nil,
 | 
						|
                   nsc = [], psc = [[], []]
 | 
						|
 | 
						|
      @name = name
 | 
						|
      @opes = opes # [[type, name], ...]
 | 
						|
      @pops = pops # [[type, name], ...]
 | 
						|
      @rets = rets # [[type, name], ...]
 | 
						|
      @comm = comm # {:c => category, :e => en desc, :j => ja desc}
 | 
						|
      @body = body # '...'
 | 
						|
 | 
						|
      @orig    = orig
 | 
						|
      @defopes = defopes
 | 
						|
      @type    = type
 | 
						|
      @tvars   = tvars
 | 
						|
 | 
						|
      @nextsc = nsc
 | 
						|
      @pushsc = psc
 | 
						|
      @sc     = []
 | 
						|
      @unifs  = []
 | 
						|
      @optimized = []
 | 
						|
      @is_sc  = false
 | 
						|
      @sp_inc = sp_inc
 | 
						|
      @trace  = trace
 | 
						|
    end
 | 
						|
 | 
						|
    def add_sc sci
 | 
						|
      @sc << sci
 | 
						|
      sci.set_sc
 | 
						|
    end
 | 
						|
 | 
						|
    attr_reader :name, :opes, :pops, :rets
 | 
						|
    attr_reader :body, :comm
 | 
						|
    attr_reader :nextsc, :pushsc
 | 
						|
    attr_reader :orig, :defopes, :type
 | 
						|
    attr_reader :sc
 | 
						|
    attr_reader :unifs, :optimized
 | 
						|
    attr_reader :is_sc
 | 
						|
    attr_reader :tvars
 | 
						|
    attr_reader :sp_inc
 | 
						|
    attr_accessor :trace
 | 
						|
 | 
						|
    def set_sc
 | 
						|
      @is_sc = true
 | 
						|
    end
 | 
						|
 | 
						|
    def add_unif insns
 | 
						|
      @unifs << insns
 | 
						|
    end
 | 
						|
 | 
						|
    def add_optimized insn
 | 
						|
      @optimized << insn
 | 
						|
    end
 | 
						|
 | 
						|
    def sp_increase_c_expr
 | 
						|
      if(pops.any?{|t, v| v == '...'} ||
 | 
						|
         rets.any?{|t, v| v == '...'})
 | 
						|
        # user definition
 | 
						|
        raise "no sp increase definition" if @sp_inc.nil?
 | 
						|
        ret = "int inc = 0;\n"
 | 
						|
 | 
						|
        @opes.each_with_index{|(t, v), i|
 | 
						|
          if (t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc)) ||
 | 
						|
             (@defopes.any?{|t, val| re =~ val})
 | 
						|
            ret << "        int #{v} = FIX2INT(opes[#{i}]);\n"
 | 
						|
          elsif (t == 'CALL_INFO' && ((re = /\b#{v}\b/n) =~ @sp_inc))
 | 
						|
            ret << "        CALL_INFO #{v} = (CALL_INFO)(opes[#{i}]);\n"
 | 
						|
          end
 | 
						|
        }
 | 
						|
 | 
						|
        @defopes.each_with_index{|((t, var), val), i|
 | 
						|
          if t == 'rb_num_t' && val != '*' && /\b#{var}\b/ =~ @sp_inc
 | 
						|
            ret << "        #{t} #{var} = #{val};\n"
 | 
						|
          end
 | 
						|
        }
 | 
						|
 | 
						|
        ret << "        #{@sp_inc};\n"
 | 
						|
        ret << "        return depth + inc;"
 | 
						|
        ret
 | 
						|
      else
 | 
						|
        "return depth + #{rets.size - pops.size};"
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def inspect
 | 
						|
      "#<Instruction:#{@name}>"
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  class InstructionsLoader
 | 
						|
    def initialize opts = {}
 | 
						|
      @insns    = []
 | 
						|
      @insn_map = {}
 | 
						|
 | 
						|
      @vpath = opts[:VPATH] || File
 | 
						|
      @use_const = opts[:use_const]
 | 
						|
      @verbose   = opts[:verbose]
 | 
						|
      @destdir   = opts[:destdir]
 | 
						|
 | 
						|
      (@vm_opts = load_vm_opts).each {|k, v|
 | 
						|
        @vm_opts[k] = opts[k] if opts.key?(k)
 | 
						|
      }
 | 
						|
 | 
						|
      load_insns_def opts[:"insns.def"] || 'insns.def'
 | 
						|
 | 
						|
      load_opt_operand_def opts[:"opope.def"] || 'defs/opt_operand.def'
 | 
						|
      load_insn_unification_def opts[:"unif.def"] || 'defs/opt_insn_unif.def'
 | 
						|
      make_stackcaching_insns if vm_opt?('STACK_CACHING')
 | 
						|
      make_trace_insns
 | 
						|
    end
 | 
						|
 | 
						|
    attr_reader :vpath
 | 
						|
    attr_reader :destdir
 | 
						|
 | 
						|
    %w[use_const verbose].each do |attr|
 | 
						|
      attr_reader attr
 | 
						|
      alias_method "#{attr}?", attr
 | 
						|
      remove_method attr
 | 
						|
    end
 | 
						|
 | 
						|
    def [](s)
 | 
						|
      @insn_map[s.to_s]
 | 
						|
    end
 | 
						|
 | 
						|
    def each
 | 
						|
      @insns.each{|insn|
 | 
						|
        yield insn
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def size
 | 
						|
      @insns.size
 | 
						|
    end
 | 
						|
 | 
						|
    ###
 | 
						|
    private
 | 
						|
 | 
						|
    def vm_opt? name
 | 
						|
      @vm_opts[name]
 | 
						|
    end
 | 
						|
 | 
						|
    def load_vm_opts file = nil
 | 
						|
      file ||= 'vm_opts.h'
 | 
						|
      opts = {}
 | 
						|
      vpath.open(file) do |f|
 | 
						|
        f.grep(/^\#define\s+OPT_([A-Z_]+)\s+(\d+)/) do
 | 
						|
          opts[$1] = !$2.to_i.zero?
 | 
						|
        end
 | 
						|
      end
 | 
						|
      opts
 | 
						|
    end
 | 
						|
 | 
						|
    SKIP_COMMENT_PATTERN = Regexp.compile(Regexp.escape('/** ##skip'))
 | 
						|
 | 
						|
    include Enumerable
 | 
						|
 | 
						|
    def add_insn insn
 | 
						|
      @insns << insn
 | 
						|
      @insn_map[insn.name] = insn
 | 
						|
    end
 | 
						|
 | 
						|
    def make_insn name, opes, pops, rets, comm, body, sp_inc
 | 
						|
      add_insn Instruction.new(name, opes, pops, rets, comm, body, [], sp_inc)
 | 
						|
    end
 | 
						|
 | 
						|
    # str -> [[type, var], ...]
 | 
						|
    def parse_vars line
 | 
						|
      raise unless /\((.*?)\)/ =~ line
 | 
						|
      vars = $1.split(',')
 | 
						|
      vars.map!{|v|
 | 
						|
        if /\s*(\S+)\s+(\S+)\s*/ =~ v
 | 
						|
          type = $1
 | 
						|
          var  = $2
 | 
						|
        elsif /\s*\.\.\.\s*/ =~ v
 | 
						|
          type = var  = '...'
 | 
						|
        else
 | 
						|
          raise
 | 
						|
        end
 | 
						|
        [type, var]
 | 
						|
      }
 | 
						|
      vars
 | 
						|
    end
 | 
						|
 | 
						|
    def parse_comment comm
 | 
						|
      c = 'others'
 | 
						|
      j = ''
 | 
						|
      e = ''
 | 
						|
      comm.each_line{|line|
 | 
						|
        case line
 | 
						|
        when /@c (.+)/
 | 
						|
          c = $1
 | 
						|
        when /@e (.+)/
 | 
						|
          e = $1
 | 
						|
        when /@e\s*$/
 | 
						|
          e = ''
 | 
						|
        when /@j (.+)$/
 | 
						|
          j = $1
 | 
						|
        when /@j\s*$/
 | 
						|
          j = ''
 | 
						|
        end
 | 
						|
      }
 | 
						|
      { :c => c,
 | 
						|
        :e => e,
 | 
						|
        :j => j,
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def load_insns_def file
 | 
						|
      body = insn = opes = pops = rets = nil
 | 
						|
      comment = ''
 | 
						|
 | 
						|
      vpath.open(file) {|f|
 | 
						|
        f.instance_variable_set(:@line_no, 0)
 | 
						|
        class << f
 | 
						|
          def line_no
 | 
						|
            @line_no
 | 
						|
          end
 | 
						|
          def gets
 | 
						|
            @line_no += 1
 | 
						|
            super
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
        while line = f.gets
 | 
						|
          line.chomp!
 | 
						|
          case line
 | 
						|
 | 
						|
          when SKIP_COMMENT_PATTERN
 | 
						|
            while line = f.gets.chomp
 | 
						|
              if /\s+\*\/$/ =~ line
 | 
						|
                break
 | 
						|
              end
 | 
						|
            end
 | 
						|
 | 
						|
            # collect instruction comment
 | 
						|
          when /^\/\*\*$/
 | 
						|
            while line = f.gets
 | 
						|
              if /\s+\*\/\s*$/ =~ line
 | 
						|
                break
 | 
						|
              else
 | 
						|
                comment << line
 | 
						|
              end
 | 
						|
            end
 | 
						|
 | 
						|
            # start instruction body
 | 
						|
          when /^DEFINE_INSN$/
 | 
						|
            insn = f.gets.chomp
 | 
						|
            opes = parse_vars(f.gets.chomp)
 | 
						|
            pops = parse_vars(f.gets.chomp).reverse
 | 
						|
            rets_str = f.gets.chomp
 | 
						|
            rets = parse_vars(rets_str).reverse
 | 
						|
            comment = parse_comment(comment)
 | 
						|
            insn_in = true
 | 
						|
            body    = ''
 | 
						|
 | 
						|
            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 mismatch for #{insn.name} (opes: #{opes.size}, opts: #{opts.size})"
 | 
						|
      end
 | 
						|
 | 
						|
      ninsn = insn.name + '_OP_' + opts.map{|e| label_escape(e)}.join('_')
 | 
						|
      nopes = []
 | 
						|
      defv  = []
 | 
						|
 | 
						|
      opts.each_with_index{|e, i|
 | 
						|
        if e == '*'
 | 
						|
          nopes << opes[i]
 | 
						|
        end
 | 
						|
        defv  << [opes[i], e]
 | 
						|
      }
 | 
						|
 | 
						|
      make_insn_operand_optimized(insn, ninsn, nopes, defv)
 | 
						|
    end
 | 
						|
 | 
						|
    def make_insn_operand_optimized orig_insn, name, opes, defopes
 | 
						|
      comm = orig_insn.comm.dup
 | 
						|
      comm[:c] = 'optimize'
 | 
						|
      add_insn insn = Instruction.new(
 | 
						|
        name, opes, orig_insn.pops, orig_insn.rets, comm,
 | 
						|
        orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
 | 
						|
        orig_insn, defopes)
 | 
						|
      orig_insn.add_optimized insn
 | 
						|
    end
 | 
						|
 | 
						|
    ## insn unif
 | 
						|
    def load_insn_unification_def file
 | 
						|
      vpath.foreach(file) {|line|
 | 
						|
        line = line.gsub(/\#.*/, '').strip
 | 
						|
        next  if line.length == 0
 | 
						|
        break if /__END__/ =~ line
 | 
						|
        make_unified_insns line.split.map{|e|
 | 
						|
          raise "unknown insn: #{e}" unless @insn_map[e]
 | 
						|
          @insn_map[e]
 | 
						|
        }
 | 
						|
      } if file
 | 
						|
    end
 | 
						|
 | 
						|
    def all_combination sets
 | 
						|
      ret = sets.shift.map{|e| [e]}
 | 
						|
 | 
						|
      sets.each{|set|
 | 
						|
        prev = ret
 | 
						|
        ret  = []
 | 
						|
        prev.each{|ary|
 | 
						|
          set.each{|e|
 | 
						|
            eary = ary.dup
 | 
						|
            eary << e
 | 
						|
            ret  << eary
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      ret
 | 
						|
    end
 | 
						|
 | 
						|
    def make_unified_insns insns
 | 
						|
      if vm_opt?('UNIFY_ALL_COMBINATION')
 | 
						|
        insn_sets = insns.map{|insn|
 | 
						|
          [insn] + insn.optimized
 | 
						|
        }
 | 
						|
 | 
						|
        all_combination(insn_sets).each{|insns_set|
 | 
						|
          make_unified_insn_each insns_set
 | 
						|
        }
 | 
						|
      else
 | 
						|
        make_unified_insn_each insns
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def mk_private_val vals, i, redef
 | 
						|
      vals.dup.map{|v|
 | 
						|
        # v[0] : type
 | 
						|
        # v[1] : var name
 | 
						|
 | 
						|
        v = v.dup
 | 
						|
        if v[0] != '...'
 | 
						|
          redef[v[1]] = v[0]
 | 
						|
          v[1] = "#{v[1]}_#{i}"
 | 
						|
        end
 | 
						|
        v
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def mk_private_val2 vals, i, redef
 | 
						|
      vals.dup.map{|v|
 | 
						|
        # v[0][0] : type
 | 
						|
        # v[0][1] : var name
 | 
						|
        # v[1] : default val
 | 
						|
 | 
						|
        pv = v.dup
 | 
						|
        v = pv[0] = pv[0].dup
 | 
						|
        if v[0] != '...'
 | 
						|
          redef[v[1]] = v[0]
 | 
						|
          v[1] = "#{v[1]}_#{i}"
 | 
						|
        end
 | 
						|
        pv
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_unified_insn_each insns
 | 
						|
      names = []
 | 
						|
      opes = []
 | 
						|
      pops = []
 | 
						|
      rets = []
 | 
						|
      comm = {
 | 
						|
        :c => 'optimize',
 | 
						|
        :e => 'unified insn',
 | 
						|
        :j => 'unified insn',
 | 
						|
      }
 | 
						|
      body = ''
 | 
						|
      passed = []
 | 
						|
      tvars = []
 | 
						|
      defopes = []
 | 
						|
      sp_inc = ''
 | 
						|
 | 
						|
      insns.each_with_index{|insn, i|
 | 
						|
        names << insn.name
 | 
						|
        redef_vars = {}
 | 
						|
 | 
						|
        e_opes = mk_private_val(insn.opes, i, redef_vars)
 | 
						|
        e_pops = mk_private_val(insn.pops, i, redef_vars)
 | 
						|
        e_rets = mk_private_val(insn.rets, i, redef_vars)
 | 
						|
        # ToDo: fix it
 | 
						|
        e_defs = mk_private_val2(insn.defopes, i, redef_vars)
 | 
						|
 | 
						|
        passed_vars = []
 | 
						|
        while pvar = e_pops.pop
 | 
						|
          rvar = rets.pop
 | 
						|
          if rvar
 | 
						|
            raise "unsupported unif insn: #{insns.inspect}" if rvar[0] == '...'
 | 
						|
            passed_vars << [pvar, rvar]
 | 
						|
            tvars << rvar
 | 
						|
          else
 | 
						|
            e_pops.push pvar
 | 
						|
            break
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
        opes.concat e_opes
 | 
						|
        pops.concat e_pops
 | 
						|
        rets.concat e_rets
 | 
						|
        defopes.concat e_defs
 | 
						|
        sp_inc << "#{insn.sp_inc}"
 | 
						|
 | 
						|
        body << "{ /* unif: #{i} */\n" +
 | 
						|
                passed_vars.map{|rpvars|
 | 
						|
                  pv = rpvars[0]
 | 
						|
                  rv = rpvars[1]
 | 
						|
                  "#define #{pv[1]} #{rv[1]}"
 | 
						|
                }.join("\n") +
 | 
						|
                "\n" +
 | 
						|
                redef_vars.map{|v, type|
 | 
						|
                  "#{type} #{v} = #{v}_#{i};"
 | 
						|
                }.join("\n") + "\n"
 | 
						|
        if line = insn.body.instance_variable_get(:@line_no)
 | 
						|
          file = insn.body.instance_variable_get(:@file)
 | 
						|
          body << "#line #{line+1} \"#{file}\"\n"
 | 
						|
          body << insn.body
 | 
						|
          body << "\n#line __CURRENT_LINE__ \"__CURRENT_FILE__\"\n"
 | 
						|
        else
 | 
						|
          body << insn.body
 | 
						|
        end
 | 
						|
        body << redef_vars.keys.map{|v|
 | 
						|
                  "#{v}_#{i} = #{v};"
 | 
						|
                }.join("\n") +
 | 
						|
                "\n" +
 | 
						|
                passed_vars.map{|rpvars|
 | 
						|
                  "#undef #{rpvars[0][1]}"
 | 
						|
                }.join("\n") +
 | 
						|
                "\n}\n"
 | 
						|
      }
 | 
						|
 | 
						|
      tvars_ary = []
 | 
						|
      tvars.each{|tvar|
 | 
						|
        unless opes.any?{|var|
 | 
						|
          var[1] == tvar[1]
 | 
						|
        } || defopes.any?{|pvar|
 | 
						|
          pvar[0][1] == tvar[1]
 | 
						|
        }
 | 
						|
          tvars_ary << tvar
 | 
						|
        end
 | 
						|
      }
 | 
						|
      add_insn insn = Instruction.new("UNIFIED_" + names.join('_'),
 | 
						|
                                   opes, pops, rets.reverse, comm, body,
 | 
						|
                                   tvars_ary, sp_inc)
 | 
						|
      insn.defopes.replace defopes
 | 
						|
      insns[0].add_unif [insn, insns]
 | 
						|
    end
 | 
						|
 | 
						|
    ## sc
 | 
						|
    SPECIAL_INSN_FOR_SC_AFTER = {
 | 
						|
      /\Asend/      => [:a],
 | 
						|
      /\Aend/       => [:a],
 | 
						|
      /\Ayield/     => [:a],
 | 
						|
      /\Aclassdef/  => [:a],
 | 
						|
      /\Amoduledef/ => [:a],
 | 
						|
    }
 | 
						|
    FROM_SC = [[], [:a], [:b], [:a, :b], [:b, :a]]
 | 
						|
 | 
						|
    def make_stackcaching_insns
 | 
						|
      pops = rets = nil
 | 
						|
 | 
						|
      @insns.dup.each{|insn|
 | 
						|
        opops = insn.pops
 | 
						|
        orets = insn.rets
 | 
						|
        oopes = insn.opes
 | 
						|
        ocomm = insn.comm
 | 
						|
        oname = insn.name
 | 
						|
 | 
						|
        after = SPECIAL_INSN_FOR_SC_AFTER.find {|k, v| k =~ oname}
 | 
						|
 | 
						|
        insns = []
 | 
						|
        FROM_SC.each{|from|
 | 
						|
          name, pops, rets, pushs1, pushs2, nextsc =
 | 
						|
          *calc_stack(insn, from, after, opops, orets)
 | 
						|
 | 
						|
          make_insn_sc(insn, name, oopes, pops, rets, [pushs1, pushs2], nextsc)
 | 
						|
        }
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_trace_insns
 | 
						|
      @insns.dup.each{|insn|
 | 
						|
        body = <<-EOS
 | 
						|
        vm_trace(ec, GET_CFP(), GET_PC());
 | 
						|
        DISPATCH_ORIGINAL_INSN(#{insn.name});
 | 
						|
        EOS
 | 
						|
 | 
						|
        trace_insn = Instruction.new(name = "trace_#{insn.name}",
 | 
						|
                                     insn.opes, insn.pops, insn.rets, insn.comm,
 | 
						|
                                     body, insn.tvars, insn.sp_inc)
 | 
						|
        trace_insn.trace = true
 | 
						|
        add_insn trace_insn
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc
 | 
						|
      comm = orig_insn.comm.dup
 | 
						|
      comm[:c] = 'optimize(sc)'
 | 
						|
 | 
						|
      scinsn = Instruction.new(
 | 
						|
        name, opes, pops, rets, comm,
 | 
						|
        orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
 | 
						|
        orig_insn, orig_insn.defopes, :sc, nextsc, pushs)
 | 
						|
 | 
						|
      add_insn scinsn
 | 
						|
      orig_insn.add_sc scinsn
 | 
						|
    end
 | 
						|
 | 
						|
    def self.complement_name st
 | 
						|
      "#{st[0] ? st[0] : 'x'}#{st[1] ? st[1] : 'x'}"
 | 
						|
    end
 | 
						|
 | 
						|
    def add_stack_value st
 | 
						|
      len = st.length
 | 
						|
      if len == 0
 | 
						|
        st[0] = :a
 | 
						|
        [nil, :a]
 | 
						|
      elsif len == 1
 | 
						|
        if st[0] == :a
 | 
						|
          st[1] = :b
 | 
						|
        else
 | 
						|
          st[1] = :a
 | 
						|
        end
 | 
						|
        [nil, st[1]]
 | 
						|
      else
 | 
						|
        st[0], st[1] = st[1], st[0]
 | 
						|
        [st[1], st[1]]
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def calc_stack insn, ofrom, oafter, opops, orets
 | 
						|
      from = ofrom.dup
 | 
						|
      pops = opops.dup
 | 
						|
      rets = orets.dup
 | 
						|
      rest_scr = ofrom.dup
 | 
						|
 | 
						|
      pushs_before = []
 | 
						|
      pushs= []
 | 
						|
 | 
						|
      pops.each_with_index{|e, i|
 | 
						|
        if e[0] == '...'
 | 
						|
          pushs_before = from
 | 
						|
          from = []
 | 
						|
        end
 | 
						|
        r = from.pop
 | 
						|
        break unless r
 | 
						|
        pops[i] = pops[i].dup << r
 | 
						|
      }
 | 
						|
 | 
						|
      if oafter
 | 
						|
        from = oafter
 | 
						|
        from.each_with_index{|r, i|
 | 
						|
          rets[i] = rets[i].dup << r if rets[i]
 | 
						|
        }
 | 
						|
      else
 | 
						|
        rets = rets.reverse
 | 
						|
        rets.each_with_index{|e, i|
 | 
						|
          break if e[0] == '...'
 | 
						|
          pushed, r = add_stack_value from
 | 
						|
          rets[i] = rets[i].dup << r
 | 
						|
          if pushed
 | 
						|
            if rest_scr.pop
 | 
						|
              pushs << pushed
 | 
						|
            end
 | 
						|
 | 
						|
            if i - 2 >= 0
 | 
						|
              rets[i-2].pop
 | 
						|
            end
 | 
						|
          end
 | 
						|
        }
 | 
						|
      end
 | 
						|
 | 
						|
      if false #|| insn.name =~ /test3/
 | 
						|
        p ofrom
 | 
						|
        p pops
 | 
						|
        p rets
 | 
						|
        p pushs_before
 | 
						|
        p pushs
 | 
						|
        p from
 | 
						|
        exit
 | 
						|
      end
 | 
						|
 | 
						|
      ret = ["#{insn.name}_SC_#{InstructionsLoader.complement_name(ofrom)}_#{complement_name(from)}",
 | 
						|
            pops, rets, pushs_before, pushs, from]
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  class SourceCodeGenerator
 | 
						|
    def initialize insns
 | 
						|
      @insns = insns
 | 
						|
    end
 | 
						|
 | 
						|
    attr_reader :insns
 | 
						|
 | 
						|
    def generate
 | 
						|
      raise "should not reach here"
 | 
						|
    end
 | 
						|
 | 
						|
    def vpath
 | 
						|
      @insns.vpath
 | 
						|
    end
 | 
						|
 | 
						|
    def verbose?
 | 
						|
      @insns.verbose?
 | 
						|
    end
 | 
						|
 | 
						|
    def use_const?
 | 
						|
      @insns.use_const?
 | 
						|
    end
 | 
						|
 | 
						|
    def template(name)
 | 
						|
      ERB.new(vpath.read("template/#{name}"), nil, '%-')
 | 
						|
    end
 | 
						|
 | 
						|
    def build_string
 | 
						|
      @lines = []
 | 
						|
      yield
 | 
						|
      @lines.join("\n")
 | 
						|
    end
 | 
						|
 | 
						|
    EMPTY_STRING = ''.freeze
 | 
						|
 | 
						|
    def commit str = EMPTY_STRING
 | 
						|
      @lines << str
 | 
						|
    end
 | 
						|
 | 
						|
    def comment str
 | 
						|
      @lines << str if verbose?
 | 
						|
    end
 | 
						|
 | 
						|
    def output_path(fn)
 | 
						|
      d = @insns.destdir
 | 
						|
      fn = File.join(d, fn) if d
 | 
						|
      fn
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # vm.inc
 | 
						|
  class VmBodyGenerator < SourceCodeGenerator
 | 
						|
    # vm.inc
 | 
						|
    def generate
 | 
						|
      template('vm.inc.tmpl').result(binding)
 | 
						|
    end
 | 
						|
 | 
						|
    def generate_from_insnname insnname
 | 
						|
      make_insn_def @insns[insnname.to_s]
 | 
						|
    end
 | 
						|
 | 
						|
    #######
 | 
						|
    private
 | 
						|
 | 
						|
    def make_header_prepare_stack insn
 | 
						|
      comment "  /* prepare stack status */"
 | 
						|
 | 
						|
      push_ba = insn.pushsc
 | 
						|
      raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0
 | 
						|
 | 
						|
      n = 0
 | 
						|
      push_ba.each {|pushs| n += pushs.length}
 | 
						|
      commit "  CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, #{n});" if n > 0
 | 
						|
      push_ba.each{|pushs|
 | 
						|
        pushs.each{|r|
 | 
						|
          commit "  PUSH(SCREG(#{r}));"
 | 
						|
        }
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_operands insn
 | 
						|
      comment "  /* declare and get from iseq */"
 | 
						|
 | 
						|
      vars = insn.opes
 | 
						|
      n = 0
 | 
						|
      ops = []
 | 
						|
 | 
						|
      vars.each_with_index{|(type, var), i|
 | 
						|
        if type == '...'
 | 
						|
          break
 | 
						|
        end
 | 
						|
 | 
						|
        # skip make operands when body has no reference to this operand
 | 
						|
        # TODO: really needed?
 | 
						|
        re = /\b#{var}\b/n
 | 
						|
        if re =~ insn.body or re =~ insn.sp_inc or insn.rets.any?{|t, v| re =~ v} or re =~ 'ic' or re =~ 'ci' or re =~ 'cc'
 | 
						|
          ops << "  #{type} #{var} = (#{type})GET_OPERAND(#{i+1});"
 | 
						|
        end
 | 
						|
 | 
						|
        n   += 1
 | 
						|
      }
 | 
						|
      @opn = n
 | 
						|
 | 
						|
      # reverse or not?
 | 
						|
      # ops.join
 | 
						|
      commit ops.reverse
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_default_operands insn
 | 
						|
      vars = insn.defopes
 | 
						|
 | 
						|
      vars.each{|e|
 | 
						|
        next if e[1] == '*'
 | 
						|
        if use_const?
 | 
						|
          commit "  const #{e[0][0]} #{e[0][1]} = #{e[1]};"
 | 
						|
        else
 | 
						|
          commit "  #define #{e[0][1]} #{e[1]}"
 | 
						|
        end
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_footer_default_operands insn
 | 
						|
      comment " /* declare and initialize default opes */"
 | 
						|
      if use_const?
 | 
						|
        commit
 | 
						|
      else
 | 
						|
        vars = insn.defopes
 | 
						|
 | 
						|
        vars.each{|e|
 | 
						|
          next if e[1] == '*'
 | 
						|
          commit "#undef #{e[0][1]}"
 | 
						|
        }
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_stack_pops insn
 | 
						|
      comment "  /* declare and pop from stack */"
 | 
						|
 | 
						|
      n = 0
 | 
						|
      pops = []
 | 
						|
      vars = insn.pops
 | 
						|
      vars.each_with_index{|iter, i|
 | 
						|
        type, var, r = *iter
 | 
						|
        if type == '...'
 | 
						|
          break
 | 
						|
        end
 | 
						|
        if r
 | 
						|
          pops << "  #{type} #{var} = SCREG(#{r});"
 | 
						|
        else
 | 
						|
          pops << "  #{type} #{var} = TOPN(#{n});"
 | 
						|
          n += 1
 | 
						|
        end
 | 
						|
      }
 | 
						|
      @popn = n
 | 
						|
 | 
						|
      # reverse or not?
 | 
						|
      commit pops.reverse
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_temporary_vars insn
 | 
						|
      comment "  /* declare temporary vars */"
 | 
						|
 | 
						|
      insn.tvars.each{|var|
 | 
						|
        commit "  #{var[0]} #{var[1]};"
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_stack_val insn
 | 
						|
      comment "/* declare stack push val */"
 | 
						|
 | 
						|
      vars = insn.opes + insn.pops + insn.defopes.map{|e| e[0]}
 | 
						|
 | 
						|
      insn.rets.each{|var|
 | 
						|
        if vars.all?{|e| e[1] != var[1]} && var[1] != '...'
 | 
						|
          commit "  #{var[0]} #{var[1]};"
 | 
						|
        end
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_analysis insn
 | 
						|
      commit "  COLLECT_USAGE_INSN(BIN(#{insn.name}));"
 | 
						|
      insn.opes.each_with_index{|op, i|
 | 
						|
        commit "  COLLECT_USAGE_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});"
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_pc insn
 | 
						|
      commit  "  ADD_PC(1+#{@opn});"
 | 
						|
      commit  "  PREFETCH(GET_PC());"
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_popn insn
 | 
						|
      comment "  /* management */"
 | 
						|
      commit  "  POPN(#{@popn});" if @popn > 0
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_debug insn
 | 
						|
      comment "  /* for debug */"
 | 
						|
      commit  "  DEBUG_ENTER_INSN(\"#{insn.name}\");"
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header_defines insn
 | 
						|
      commit  "  #define CURRENT_INSN_#{insn.name} 1"
 | 
						|
      commit  "  #define INSN_IS_SC()     #{insn.sc ? 0 : 1}"
 | 
						|
      commit  "  #define INSN_LABEL(lab)  LABEL_#{insn.name}_##lab"
 | 
						|
      commit  "  #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}"
 | 
						|
    end
 | 
						|
 | 
						|
    def each_footer_stack_val insn
 | 
						|
      insn.rets.reverse_each{|v|
 | 
						|
        break if v[1] == '...'
 | 
						|
        yield v
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_footer_stack_val insn
 | 
						|
      comment "  /* push stack val */"
 | 
						|
 | 
						|
      n = 0
 | 
						|
      each_footer_stack_val(insn){|v|
 | 
						|
        n += 1 unless v[2]
 | 
						|
      }
 | 
						|
      commit "  CHECK_VM_STACK_OVERFLOW_FOR_INSN(VM_REG_CFP, #{n});" if n > 0
 | 
						|
      each_footer_stack_val(insn){|v|
 | 
						|
        if v[2]
 | 
						|
          commit "  SCREG(#{v[2]}) = #{v[1]};"
 | 
						|
        else
 | 
						|
          commit "  PUSH(#{v[1]});"
 | 
						|
        end
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def make_footer_undefs insn
 | 
						|
      commit "#undef CURRENT_INSN_#{insn.name}"
 | 
						|
      commit "#undef INSN_IS_SC"
 | 
						|
      commit "#undef INSN_LABEL"
 | 
						|
      commit "#undef LABEL_IS_SC"
 | 
						|
    end
 | 
						|
 | 
						|
    def make_header insn
 | 
						|
      label = insn.trace ? '' : "START_OF_ORIGINAL_INSN(#{insn.name});"
 | 
						|
      commit "INSN_ENTRY(#{insn.name}){#{label}"
 | 
						|
      make_header_prepare_stack insn
 | 
						|
      commit "{"
 | 
						|
      unless insn.trace
 | 
						|
        make_header_stack_val  insn
 | 
						|
        make_header_default_operands insn
 | 
						|
        make_header_operands   insn
 | 
						|
        make_header_stack_pops insn
 | 
						|
        make_header_temporary_vars insn
 | 
						|
        #
 | 
						|
        make_header_debug insn
 | 
						|
        make_header_pc insn
 | 
						|
        make_header_popn insn
 | 
						|
        make_header_defines insn
 | 
						|
        make_header_analysis insn
 | 
						|
      end
 | 
						|
      commit  "{"
 | 
						|
    end
 | 
						|
 | 
						|
    def make_footer insn
 | 
						|
      unless insn.trace
 | 
						|
        make_footer_stack_val insn
 | 
						|
        make_footer_default_operands insn
 | 
						|
        make_footer_undefs insn
 | 
						|
      end
 | 
						|
      commit "  END_INSN(#{insn.name});}}}"
 | 
						|
    end
 | 
						|
 | 
						|
    def make_insn_def insn
 | 
						|
      build_string do
 | 
						|
        make_header insn
 | 
						|
        if line = insn.body.instance_variable_get(:@line_no)
 | 
						|
          file = insn.body.instance_variable_get(:@file)
 | 
						|
          commit "#line #{line+1} \"#{file}\""
 | 
						|
          commit insn.body
 | 
						|
          commit '#line __CURRENT_LINE__ "__CURRENT_FILE__"'
 | 
						|
        else
 | 
						|
          commit insn.body
 | 
						|
        end
 | 
						|
        make_footer(insn)
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # vmtc.inc
 | 
						|
  class VmTCIncGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
      template('vmtc.inc.tmpl').result(binding)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # insns_info.inc
 | 
						|
  class InsnsInfoIncGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
      template('insns_info.inc.tmpl').result(binding)
 | 
						|
    end
 | 
						|
 | 
						|
    ###
 | 
						|
    private
 | 
						|
 | 
						|
    def op2typesig op
 | 
						|
      case op
 | 
						|
      when /^OFFSET/
 | 
						|
        "TS_OFFSET"
 | 
						|
      when /^rb_num_t/
 | 
						|
        "TS_NUM"
 | 
						|
      when /^lindex_t/
 | 
						|
        "TS_LINDEX"
 | 
						|
      when /^VALUE/
 | 
						|
        "TS_VALUE"
 | 
						|
      when /^ID/
 | 
						|
        "TS_ID"
 | 
						|
      when /GENTRY/
 | 
						|
        "TS_GENTRY"
 | 
						|
      when /^IC/
 | 
						|
        "TS_IC"
 | 
						|
      when /^CALL_INFO/
 | 
						|
        "TS_CALLINFO"
 | 
						|
      when /^CALL_CACHE/
 | 
						|
        "TS_CALLCACHE"
 | 
						|
      when /^\.\.\./
 | 
						|
        "TS_VARIABLE"
 | 
						|
      when /^CDHASH/
 | 
						|
        "TS_CDHASH"
 | 
						|
      when /^ISEQ/
 | 
						|
        "TS_ISEQ"
 | 
						|
      when /rb_insn_func_t/
 | 
						|
        "TS_FUNCPTR"
 | 
						|
      else
 | 
						|
        raise "unknown op type: #{op}"
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    TYPE_CHARS = {
 | 
						|
      'TS_OFFSET'    => 'O',
 | 
						|
      'TS_NUM'       => 'N',
 | 
						|
      'TS_LINDEX'    => 'L',
 | 
						|
      'TS_VALUE'     => 'V',
 | 
						|
      'TS_ID'        => 'I',
 | 
						|
      'TS_GENTRY'    => 'G',
 | 
						|
      'TS_IC'        => 'K',
 | 
						|
      'TS_CALLINFO'  => 'C',
 | 
						|
      'TS_CALLCACHE' => 'E',
 | 
						|
      'TS_CDHASH'    => 'H',
 | 
						|
      'TS_ISEQ'      => 'S',
 | 
						|
      'TS_VARIABLE'  => '.',
 | 
						|
      'TS_FUNCPTR'   => 'F',
 | 
						|
    }
 | 
						|
 | 
						|
    def max_length(array)
 | 
						|
      max = 0
 | 
						|
      array.each do |i|
 | 
						|
        if (n = i.length) > max
 | 
						|
          max = n
 | 
						|
        end
 | 
						|
      end
 | 
						|
      max
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # insns.inc
 | 
						|
  class InsnsIncGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
      template('insns.inc.tmpl').result(binding)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # minsns.inc
 | 
						|
  class MInsnsIncGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
      template('minsns.inc.tmpl').result(binding)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # optinsn.inc
 | 
						|
  class OptInsnIncGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
      optinsn_inc
 | 
						|
    end
 | 
						|
 | 
						|
    ###
 | 
						|
    private
 | 
						|
 | 
						|
    def val_as_type op
 | 
						|
      type = op[0][0]
 | 
						|
      val  = op[1]
 | 
						|
 | 
						|
      case type
 | 
						|
      when /^long/, /^rb_num_t/, /^lindex_t/
 | 
						|
        "INT2FIX(#{val})"
 | 
						|
      when /^VALUE/
 | 
						|
        val
 | 
						|
      when /^ID/
 | 
						|
        "INT2FIX(#{val})"
 | 
						|
      when /^ISEQ/, /^rb_insn_func_t/
 | 
						|
        val
 | 
						|
      when /GENTRY/
 | 
						|
        raise
 | 
						|
      when /^\.\.\./
 | 
						|
        raise
 | 
						|
      else
 | 
						|
        raise "type: #{type}"
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # optinsn.inc
 | 
						|
    def optinsn_inc
 | 
						|
      opt_insns_map = Hash.new{[]}
 | 
						|
 | 
						|
      @insns.each{|insn|
 | 
						|
        next if insn.defopes.size == 0
 | 
						|
        next if insn.type         == :sc
 | 
						|
        next if /^UNIFIED/ =~ insn.name.to_s
 | 
						|
 | 
						|
        originsn = insn.orig
 | 
						|
        opt_insns_map[originsn] <<= insn
 | 
						|
      }
 | 
						|
 | 
						|
      opt_insns_map.each_value do |optinsns|
 | 
						|
        sorted = optinsns.sort_by {|opti|
 | 
						|
          opti.defopes.find_all{|e| e[1] == '*'}.size
 | 
						|
        }
 | 
						|
        optinsns.replace(sorted)
 | 
						|
      end
 | 
						|
 | 
						|
      template('optinsn.inc.tmpl').result(binding)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # optunifs.inc
 | 
						|
  class OptUnifsIncGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
      template('optunifs.inc.tmpl').result(binding)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # opt_sc.inc
 | 
						|
  class OptSCIncGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
      sc_insn_info = []
 | 
						|
      @insns.each{|insn|
 | 
						|
        insns = insn.sc
 | 
						|
        if insns.size > 0
 | 
						|
          insns = ['SC_ERROR'] + insns.map{|e| "    BIN(#{e.name})"}
 | 
						|
        else
 | 
						|
          insns = Array.new(6){'SC_ERROR'}
 | 
						|
        end
 | 
						|
        sc_insn_info << "  {\n#{insns.join(",\n")}}"
 | 
						|
      }
 | 
						|
      sc_insn_info = sc_insn_info.join(",\n")
 | 
						|
 | 
						|
      sc_insn_next = @insns.map{|insn|
 | 
						|
        "  SCS_#{InstructionsLoader.complement_name(insn.nextsc).upcase}" +
 | 
						|
        (verbose? ? " /* #{insn.name} */" : '')
 | 
						|
      }.join(",\n")
 | 
						|
      template('opt_sc.inc.tmpl').result(binding)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # yasmdata.rb
 | 
						|
  class YASMDataRbGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
      insn_id2no = ''
 | 
						|
      @insns.each_with_index{|insn, i|
 | 
						|
        insn_id2no << "        :#{insn.name} => #{i},\n"
 | 
						|
      }
 | 
						|
      template('yasmdata.rb').result(binding)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ###################################################################
 | 
						|
  # yarvarch.*
 | 
						|
  class YARVDocGenerator < SourceCodeGenerator
 | 
						|
    def generate
 | 
						|
 | 
						|
    end
 | 
						|
 | 
						|
    def desc lang
 | 
						|
      d = ''
 | 
						|
      i = 0
 | 
						|
      cat = nil
 | 
						|
      @insns.each{|insn|
 | 
						|
        seq    = insn.opes.map{|t,v| v}.join(' ')
 | 
						|
        before = insn.pops.reverse.map{|t,v| v}.join(' ')
 | 
						|
        after  = insn.rets.reverse.map{|t,v| v}.join(' ')
 | 
						|
 | 
						|
        if cat != insn.comm[:c]
 | 
						|
          d << "** #{insn.comm[:c]}\n\n"
 | 
						|
          cat = insn.comm[:c]
 | 
						|
        end
 | 
						|
 | 
						|
        d << "*** #{insn.name}\n"
 | 
						|
        d << "\n"
 | 
						|
        d << insn.comm[lang] << "\n\n"
 | 
						|
        d << ":instruction sequence: 0x%02x " % i << seq << "\n"
 | 
						|
        d << ":stack: #{before} => #{after}\n\n"
 | 
						|
        i+=1
 | 
						|
      }
 | 
						|
      d
 | 
						|
    end
 | 
						|
 | 
						|
    def desc_ja
 | 
						|
      d = desc :j
 | 
						|
      template('yarvarch.ja').result(binding)
 | 
						|
    end
 | 
						|
 | 
						|
    def desc_en
 | 
						|
      d = desc :e
 | 
						|
      template('yarvarch.en').result(binding)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  class SourceCodeGenerator
 | 
						|
    Files = { # codes
 | 
						|
      'vm.inc'         => VmBodyGenerator,
 | 
						|
      'vmtc.inc'       => VmTCIncGenerator,
 | 
						|
      'insns.inc'      => InsnsIncGenerator,
 | 
						|
      'insns_info.inc' => InsnsInfoIncGenerator,
 | 
						|
    # 'minsns.inc'     => MInsnsIncGenerator,
 | 
						|
      'optinsn.inc'    => OptInsnIncGenerator,
 | 
						|
      'optunifs.inc'   => OptUnifsIncGenerator,
 | 
						|
      'opt_sc.inc'     => OptSCIncGenerator,
 | 
						|
      'yasmdata.rb'    => YASMDataRbGenerator,
 | 
						|
    }
 | 
						|
 | 
						|
    def generate args = []
 | 
						|
      args = Files.keys if args.empty?
 | 
						|
      args.each{|fn|
 | 
						|
        s = Files[fn].new(@insns).generate
 | 
						|
        open(output_path(fn), 'w') {|f| f.puts(s)}
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def self.def_options(opt)
 | 
						|
      opts = {
 | 
						|
        :"insns.def" => 'insns.def',
 | 
						|
        :"opope.def" => 'defs/opt_operand.def',
 | 
						|
        :"unif.def"  => 'defs/opt_insn_unif.def',
 | 
						|
      }
 | 
						|
 | 
						|
      opt.on("-Dname", /\AOPT_(\w+)\z/, "enable VM option") {|s, v|
 | 
						|
        opts[v] = true
 | 
						|
      }
 | 
						|
      opt.on("--enable=name[,name...]", Array,
 | 
						|
        "enable VM options (without OPT_ prefix)") {|*a|
 | 
						|
        a.each {|v| opts[v] = true}
 | 
						|
      }
 | 
						|
      opt.on("-Uname", /\AOPT_(\w+)\z/, "disable VM option") {|s, v|
 | 
						|
        opts[v] = false
 | 
						|
      }
 | 
						|
      opt.on("--disable=name[,name...]", Array,
 | 
						|
        "disable VM options (without OPT_ prefix)") {|*a|
 | 
						|
          a.each {|v| opts[v] = false}
 | 
						|
      }
 | 
						|
      opt.on("-i", "--insnsdef=FILE", "--instructions-def",
 | 
						|
        "instructions definition file") {|n|
 | 
						|
        opts[:insns_def] = n
 | 
						|
      }
 | 
						|
      opt.on("-o", "--opt-operanddef=FILE", "--opt-operand-def",
 | 
						|
        "vm option: operand definition file") {|n|
 | 
						|
        opts[:opope_def] = n
 | 
						|
      }
 | 
						|
      opt.on("-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def",
 | 
						|
        "vm option: instruction unification file") {|n|
 | 
						|
        opts[:unif_def] = n
 | 
						|
      }
 | 
						|
      opt.on("-C", "--[no-]use-const",
 | 
						|
        "use consts for default operands instead of macros") {|v|
 | 
						|
        opts[:use_const] = v
 | 
						|
      }
 | 
						|
      opt.on("-d", "--destdir", "--output-directory=DIR",
 | 
						|
        "make output file underneath DIR") {|v|
 | 
						|
        opts[:destdir] = v
 | 
						|
      }
 | 
						|
      opt.on("-V", "--[no-]verbose") {|v|
 | 
						|
        opts[:verbose] = v
 | 
						|
      }
 | 
						|
 | 
						|
      vpath = VPath.new
 | 
						|
      vpath.def_options(opt)
 | 
						|
 | 
						|
      proc {
 | 
						|
        opts[:VPATH] = vpath
 | 
						|
        build opts
 | 
						|
      }
 | 
						|
    end
 | 
						|
 | 
						|
    def self.build opts, vpath = ['./']
 | 
						|
      opts[:VPATH] ||= VPath.new(*vpath)
 | 
						|
      self.new InstructionsLoader.new(opts)
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 | 
						|
 |