mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
1221 lines
28 KiB
Ruby
1221 lines
28 KiB
Ruby
|
#!/usr/bin/env ruby
|
||
|
#
|
||
|
#
|
||
|
#
|
||
|
# $verbose = true
|
||
|
# $use_const = true
|
||
|
|
||
|
require 'pp'
|
||
|
require 'erb'
|
||
|
|
||
|
class InsnsDef
|
||
|
class InsnInfo
|
||
|
|
||
|
def initialize name, opes, pops, rets, comm, body, tvars, sp_inc,
|
||
|
orig = self, defopes = [], type = nil,
|
||
|
nsc = [], psc = [[], []]
|
||
|
|
||
|
@name = name
|
||
|
@opes = opes # [[type, name], ...]
|
||
|
@pops = pops # [[type, name], ...]
|
||
|
@rets = rets # [[type, name], ...]
|
||
|
@comm = comm # {:c => category, :e => en desc, :j => ja desc}
|
||
|
@body = body # '...'
|
||
|
|
||
|
@orig = orig
|
||
|
@defopes = defopes
|
||
|
@type = type
|
||
|
@tvars = tvars
|
||
|
|
||
|
@nextsc = nsc
|
||
|
@pushsc = psc
|
||
|
@sc = []
|
||
|
@unifs = []
|
||
|
@optimized = []
|
||
|
@is_sc = false
|
||
|
@sp_inc = sp_inc
|
||
|
end
|
||
|
|
||
|
def add_sc sci
|
||
|
@sc << sci
|
||
|
sci.set_sc
|
||
|
end
|
||
|
|
||
|
attr_reader :name, :opes, :pops, :rets
|
||
|
attr_reader :body, :comm
|
||
|
attr_reader :nextsc, :pushsc
|
||
|
attr_reader :orig, :defopes, :type
|
||
|
attr_reader :sc
|
||
|
attr_reader :unifs, :optimized
|
||
|
attr_reader :is_sc
|
||
|
attr_reader :tvars
|
||
|
attr_reader :sp_inc
|
||
|
|
||
|
def set_sc
|
||
|
@is_sc = true
|
||
|
end
|
||
|
|
||
|
def add_unif insns
|
||
|
@unifs << insns
|
||
|
end
|
||
|
|
||
|
def add_optimized insn
|
||
|
@optimized << insn
|
||
|
end
|
||
|
|
||
|
def sp_increase_c_expr
|
||
|
if(pops.any?{|t, v| v == '...'} ||
|
||
|
rets.any?{|t, v| v == '...'})
|
||
|
# user definision
|
||
|
raise "no sp increase definition" if @sp_inc.nil?
|
||
|
|
||
|
ret = "int inc = 0;\n"
|
||
|
|
||
|
@opes.each_with_index{|(t, v), i|
|
||
|
if t == 'num_t'
|
||
|
ret << " unsigned long #{v} = FIX2INT(opes[#{i}]);\n"
|
||
|
end
|
||
|
}
|
||
|
@defopes.each_with_index{|((t, var), val), i|
|
||
|
if t == 'num_t' && val != '*'
|
||
|
ret << " unsigned long #{var} = #{val};\n"
|
||
|
end
|
||
|
}
|
||
|
|
||
|
ret << " #{@sp_inc};\n"
|
||
|
ret << " return depth + inc;"
|
||
|
ret
|
||
|
else
|
||
|
"return depth + #{rets.size - pops.size};"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def inspect
|
||
|
"#<InsnInfo:#{@name}>"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def initialize file, optopfile, uniffile
|
||
|
@insns = []
|
||
|
@insn_map = {}
|
||
|
|
||
|
load_insns_def file
|
||
|
|
||
|
load_opt_operand_def optopfile
|
||
|
load_insn_unification_def uniffile
|
||
|
make_stackcaching_insns if $opts['OPT_STACK_CACHING']
|
||
|
end
|
||
|
|
||
|
attr_reader :insns
|
||
|
attr_reader :insn_map
|
||
|
|
||
|
SKIP_COMMENT_PATTERN = Regexp.compile(Regexp.escape('/** ##skip'))
|
||
|
|
||
|
include Enumerable
|
||
|
def each
|
||
|
@insns.each{|insn|
|
||
|
yield insn
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def add_insn insn
|
||
|
@insns << insn
|
||
|
@insn_map[insn.name] = insn
|
||
|
end
|
||
|
|
||
|
def make_insn name, opes, pops, rets, comm, body, sp_inc
|
||
|
add_insn InsnInfo.new(name, opes, pops, rets, comm, body, [], sp_inc)
|
||
|
end
|
||
|
|
||
|
|
||
|
# str -> [[type, var], ...]
|
||
|
def parse_vars line
|
||
|
raise unless /\((.*?)\)/ =~ line
|
||
|
vars = $1.split(',')
|
||
|
vars.map!{|v|
|
||
|
if /\s*(\S+)\s+(\S+)\s*/ =~ v
|
||
|
type = $1
|
||
|
var = $2
|
||
|
elsif /\s*\.\.\.\s*/ =~ v
|
||
|
type = var = '...'
|
||
|
else
|
||
|
raise
|
||
|
end
|
||
|
[type, var]
|
||
|
}
|
||
|
vars
|
||
|
end
|
||
|
|
||
|
def parse_comment comm
|
||
|
c = 'others'
|
||
|
j = ''
|
||
|
e = ''
|
||
|
comm.each_line{|line|
|
||
|
case line
|
||
|
when /@c (.+)/
|
||
|
c = $1
|
||
|
when /@e (.+)/
|
||
|
e = $1
|
||
|
when /@e\s*$/
|
||
|
e = ''
|
||
|
when /@j (.+)$/
|
||
|
j = $1
|
||
|
when /@j\s*$/
|
||
|
j = ''
|
||
|
end
|
||
|
}
|
||
|
{ :c => c,
|
||
|
:e => e,
|
||
|
:j => j,
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def load_insns_def file
|
||
|
body = insn = opes = pops = rets = nil
|
||
|
comment = ''
|
||
|
|
||
|
open(file){|f|
|
||
|
f.instance_variable_set(:@line_no, 0)
|
||
|
class << f
|
||
|
def line_no
|
||
|
@line_no
|
||
|
end
|
||
|
def gets
|
||
|
@line_no += 1
|
||
|
super
|
||
|
end
|
||
|
end
|
||
|
|
||
|
while line = f.gets
|
||
|
line.chomp!
|
||
|
case line
|
||
|
|
||
|
when SKIP_COMMENT_PATTERN
|
||
|
while line = f.gets.chomp
|
||
|
if /\s+\*\/$/ =~ line
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# collect instruction comment
|
||
|
when /^\/\*\*$/
|
||
|
while line = f.gets
|
||
|
if /\s+\*\/\s*$/ =~ line
|
||
|
break
|
||
|
else
|
||
|
comment << line
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# start instruction body
|
||
|
when /^DEFINE_INSN$/
|
||
|
insn = f.gets.chomp
|
||
|
opes = parse_vars(f.gets.chomp)
|
||
|
pops = parse_vars(f.gets.chomp).reverse
|
||
|
rets_str = f.gets.chomp
|
||
|
rets = parse_vars(rets_str).reverse
|
||
|
comment = parse_comment(comment)
|
||
|
insn_in = true
|
||
|
body = ''
|
||
|
|
||
|
if /\/\/(.+)/ =~ rets_str
|
||
|
sp_inc = $1
|
||
|
else
|
||
|
sp_inc = nil
|
||
|
end
|
||
|
|
||
|
raise unless /^\{$/ =~ f.gets.chomp
|
||
|
line_no = f.line_no
|
||
|
|
||
|
# end instruction body
|
||
|
when /^\}/
|
||
|
if insn_in
|
||
|
body.instance_variable_set(:@line_no, line_no)
|
||
|
insn = make_insn(insn, opes, pops, rets, comment, body, sp_inc)
|
||
|
insn_in = false
|
||
|
comment = ''
|
||
|
end
|
||
|
|
||
|
#
|
||
|
else
|
||
|
if insn_in
|
||
|
body << line + "\n"
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
}
|
||
|
end
|
||
|
|
||
|
## opt op
|
||
|
def load_opt_operand_def file
|
||
|
open(file){|f| f.each{|line|
|
||
|
line = line.gsub(/\#.*/, '').strip
|
||
|
next if line.length == 0
|
||
|
break if /__END__/ =~ line
|
||
|
/(\S+)\s+(.+)/ =~ line
|
||
|
insn = $1
|
||
|
opts = $2
|
||
|
add_opt_operand insn, opts.split(/,/).map{|e| e.strip}
|
||
|
}}
|
||
|
end
|
||
|
|
||
|
def label_escape label
|
||
|
label.gsub(/\(/, '_O_').
|
||
|
gsub(/\)/, '_C_').
|
||
|
gsub(/\*/, '_WC_')
|
||
|
end
|
||
|
|
||
|
def add_opt_operand insn_name, opts
|
||
|
insn = @insn_map[insn_name]
|
||
|
opes = insn.opes
|
||
|
|
||
|
if opes.size != opts.size
|
||
|
raise "operand size mismatcvh for #{insn.name} (opes: #{opes.size}, opts: #{opts.size})"
|
||
|
end
|
||
|
|
||
|
ninsn = insn.name + '_OP_' + opts.map{|e| label_escape(e)}.join('_')
|
||
|
nopes = []
|
||
|
defv = []
|
||
|
|
||
|
opts.each_with_index{|e, i|
|
||
|
if e == '*'
|
||
|
nopes << opes[i]
|
||
|
end
|
||
|
defv << [opes[i], e]
|
||
|
}
|
||
|
|
||
|
make_insn_operand_optimiized(insn, ninsn, nopes, defv)
|
||
|
end
|
||
|
|
||
|
def make_insn_operand_optimiized orig_insn, name, opes, defopes
|
||
|
comm = orig_insn.comm.dup
|
||
|
comm[:c] = 'optimize'
|
||
|
add_insn insn = InsnInfo.new(
|
||
|
name, opes, orig_insn.pops, orig_insn.rets, comm,
|
||
|
orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
|
||
|
orig_insn, defopes)
|
||
|
orig_insn.add_optimized insn
|
||
|
end
|
||
|
|
||
|
|
||
|
## insn unif
|
||
|
def load_insn_unification_def file
|
||
|
open(file){|f| f.each{|line|
|
||
|
line = line.gsub(/\#.*/, '').strip
|
||
|
next if line.length == 0
|
||
|
break if /__END__/ =~ line
|
||
|
make_unified_insns line.split.map{|e|
|
||
|
raise "unknown insn: #{e}" unless @insn_map[e]
|
||
|
@insn_map[e]
|
||
|
}
|
||
|
}}
|
||
|
end
|
||
|
|
||
|
def all_combination sets
|
||
|
ret = sets.shift.map{|e| [e]}
|
||
|
|
||
|
sets.each{|set|
|
||
|
prev = ret
|
||
|
ret = []
|
||
|
prev.each{|ary|
|
||
|
set.each{|e|
|
||
|
eary = ary.dup
|
||
|
eary << e
|
||
|
ret << eary
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ret
|
||
|
end
|
||
|
|
||
|
def make_unified_insns insns
|
||
|
if $opts['OPT_UNIFY_ALL_COMBINATION']
|
||
|
insn_sets = insns.map{|insn|
|
||
|
[insn] + insn.optimized
|
||
|
}
|
||
|
|
||
|
all_combination(insn_sets).each{|insns_set|
|
||
|
make_unified_insn_each insns_set
|
||
|
}
|
||
|
else
|
||
|
make_unified_insn_each insns
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def mk_private_val vals, i, redef
|
||
|
vals.dup.map{|v|
|
||
|
# v[0] : type
|
||
|
# v[1] : var name
|
||
|
|
||
|
v = v.dup
|
||
|
if v[0] != '...'
|
||
|
redef[v[1]] = v[0]
|
||
|
v[1] = "#{v[1]}_#{i}"
|
||
|
end
|
||
|
v
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def mk_private_val2 vals, i, redef
|
||
|
vals.dup.map{|v|
|
||
|
# v[0][0] : type
|
||
|
# v[0][1] : var name
|
||
|
# v[1] : default val
|
||
|
|
||
|
pv = v.dup
|
||
|
v = pv[0] = pv[0].dup
|
||
|
if v[0] != '...'
|
||
|
redef[v[1]] = v[0]
|
||
|
v[1] = "#{v[1]}_#{i}"
|
||
|
end
|
||
|
pv
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def make_unified_insn_each insns
|
||
|
names = []
|
||
|
opes = []
|
||
|
pops = []
|
||
|
rets = []
|
||
|
comm = {
|
||
|
:c => 'optimize',
|
||
|
:e => 'unified insn',
|
||
|
:j => 'unified insn',
|
||
|
}
|
||
|
body = ''
|
||
|
passed = []
|
||
|
tvars = []
|
||
|
defopes = []
|
||
|
sp_inc = ''
|
||
|
|
||
|
insns.each_with_index{|insn, i|
|
||
|
names << insn.name
|
||
|
|
||
|
redef_vars = {}
|
||
|
|
||
|
e_opes = mk_private_val(insn.opes, i, redef_vars)
|
||
|
e_pops = mk_private_val(insn.pops, i, redef_vars)
|
||
|
e_rets = mk_private_val(insn.rets, i, redef_vars)
|
||
|
# ToDo: fix it
|
||
|
e_defs = mk_private_val2(insn.defopes, i, redef_vars)
|
||
|
|
||
|
passed_vars = []
|
||
|
while pvar = e_pops.pop
|
||
|
rvar = rets.pop
|
||
|
|
||
|
if rvar
|
||
|
raise "unsupported unif insn: #{insns.inspect}" if rvar[0] == '...'
|
||
|
passed_vars << [pvar, rvar]
|
||
|
tvars << rvar
|
||
|
else
|
||
|
e_pops.push pvar
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
|
||
|
opes.concat e_opes
|
||
|
pops.concat e_pops
|
||
|
rets.concat e_rets
|
||
|
defopes.concat e_defs
|
||
|
sp_inc += "#{insn.sp_inc}"
|
||
|
|
||
|
body += #
|
||
|
"{ /* unif: #{i} */\n" +
|
||
|
passed_vars.map{|rpvars|
|
||
|
pv = rpvars[0]
|
||
|
rv = rpvars[1]
|
||
|
"#define #{pv[1]} #{rv[1]}"
|
||
|
}.join("\n") +
|
||
|
"\n" +
|
||
|
redef_vars.map{|v, type|
|
||
|
"#define #{v} #{v}_#{i}"
|
||
|
}.join("\n") + "\n" +
|
||
|
insn.body +
|
||
|
passed_vars.map{|rpvars|
|
||
|
"#undef #{rpvars[0][1]}"
|
||
|
}.join("\n") +
|
||
|
"\n" +
|
||
|
redef_vars.keys.map{|v|
|
||
|
"#undef #{v}"
|
||
|
}.join("\n") +
|
||
|
"\n}\n"
|
||
|
}
|
||
|
|
||
|
tvars_ary = []
|
||
|
tvars.each{|tvar|
|
||
|
unless opes.any?{|var|
|
||
|
var[1] == tvar[1]
|
||
|
} || defopes.any?{|pvar|
|
||
|
pvar[0][1] == tvar[1]
|
||
|
}
|
||
|
tvars_ary << tvar
|
||
|
end
|
||
|
}
|
||
|
add_insn insn = InsnInfo.new("UNIFIED_" + names.join('_'),
|
||
|
opes, pops, rets.reverse, comm, body,
|
||
|
tvars_ary, sp_inc)
|
||
|
insn.defopes.replace defopes
|
||
|
insns[0].add_unif [insn, insns]
|
||
|
end
|
||
|
|
||
|
|
||
|
## sc
|
||
|
SPECIAL_INSN_FOR_SC_AFTER = {
|
||
|
/\Asend/ => [:a],
|
||
|
/\Aend/ => [:a],
|
||
|
/\Ayield/ => [:a],
|
||
|
/\Aclassdef/ => [:a],
|
||
|
/\Amoduledef/ => [:a],
|
||
|
}
|
||
|
FROM_SC = [[], [:a], [:b], [:a, :b], [:b, :a]]
|
||
|
|
||
|
def make_stackcaching_insns
|
||
|
pops = rets = nil
|
||
|
|
||
|
@insns.dup.each{|insn|
|
||
|
opops = insn.pops
|
||
|
orets = insn.rets
|
||
|
oopes = insn.opes
|
||
|
ocomm = insn.comm
|
||
|
|
||
|
after = nil
|
||
|
SPECIAL_INSN_FOR_SC_AFTER.any?{|k, v|
|
||
|
if k =~ insn.name
|
||
|
after = v
|
||
|
break
|
||
|
end
|
||
|
}
|
||
|
|
||
|
insns = []
|
||
|
FROM_SC.each{|from|
|
||
|
name, pops, rets, pushs1, pushs2, nextsc =
|
||
|
*calc_stack(insn, from, after, opops, orets)
|
||
|
|
||
|
#
|
||
|
make_insn_sc(insn, name, oopes, pops, rets, [pushs1, pushs2], nextsc)
|
||
|
}
|
||
|
}
|
||
|
# exit! 1
|
||
|
end
|
||
|
|
||
|
def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc
|
||
|
comm = orig_insn.comm.dup
|
||
|
comm[:c] = 'optimize(sc)'
|
||
|
|
||
|
scinsn = InsnInfo.new(
|
||
|
name, opes, pops, rets, comm,
|
||
|
orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
|
||
|
orig_insn, orig_insn.defopes, :sc, nextsc, pushs)
|
||
|
|
||
|
add_insn scinsn
|
||
|
#
|
||
|
orig_insn.add_sc scinsn
|
||
|
end
|
||
|
|
||
|
def complement_name st
|
||
|
"#{st[0] ? st[0] : 'x'}#{st[1] ? st[1] : 'x'}"
|
||
|
end
|
||
|
|
||
|
def add_stack_value st
|
||
|
len = st.length
|
||
|
if len == 0
|
||
|
st[0] = :a
|
||
|
[nil, :a]
|
||
|
elsif len == 1
|
||
|
if st[0] == :a
|
||
|
st[1] = :b
|
||
|
else
|
||
|
st[1] = :a
|
||
|
end
|
||
|
[nil, st[1]]
|
||
|
else
|
||
|
st[0], st[1] = st[1], st[0]
|
||
|
[st[1], st[1]]
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def calc_stack insn, ofrom, oafter, opops, orets
|
||
|
from = ofrom.dup
|
||
|
pops = opops.dup
|
||
|
rets = orets.dup
|
||
|
rest_scr = ofrom.dup
|
||
|
|
||
|
pushs_before = []
|
||
|
pushs= []
|
||
|
|
||
|
pops.each_with_index{|e, i|
|
||
|
if e[0] == '...'
|
||
|
pushs_before = from
|
||
|
from = []
|
||
|
end
|
||
|
r = from.pop
|
||
|
break unless r
|
||
|
pops[i] = pops[i].dup << r
|
||
|
}
|
||
|
|
||
|
if oafter
|
||
|
from = oafter
|
||
|
from.each_with_index{|r, i|
|
||
|
rets[i] = rets[i].dup << r if rets[i]
|
||
|
}
|
||
|
else
|
||
|
rets = rets.reverse
|
||
|
rets.each_with_index{|e, i|
|
||
|
break if e[0] == '...'
|
||
|
pushed, r = add_stack_value from
|
||
|
rets[i] = rets[i].dup << r
|
||
|
if pushed
|
||
|
if rest_scr.pop
|
||
|
pushs << pushed
|
||
|
end
|
||
|
|
||
|
if i - 2 >= 0
|
||
|
rets[i-2].pop
|
||
|
end
|
||
|
end
|
||
|
}
|
||
|
end
|
||
|
|
||
|
if false #|| insn.name =~ /test3/
|
||
|
p ofrom
|
||
|
p pops
|
||
|
p rets
|
||
|
p pushs_before
|
||
|
p pushs
|
||
|
p from
|
||
|
exit
|
||
|
end
|
||
|
|
||
|
ret = ["#{insn.name}_SC_#{complement_name(ofrom)}_#{complement_name(from)}",
|
||
|
pops, rets, pushs_before, pushs, from]
|
||
|
#p ret
|
||
|
ret
|
||
|
end
|
||
|
|
||
|
###################################################################
|
||
|
# vm.inc
|
||
|
def make_header_prepare_stack insn
|
||
|
ret = []
|
||
|
push_ba = insn.pushsc
|
||
|
raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0
|
||
|
|
||
|
push_ba.each{|pushs|
|
||
|
pushs.each{|r|
|
||
|
ret << " PUSH(SCREG(#{r}));"
|
||
|
}
|
||
|
}
|
||
|
ret.join("\n") + "\n"
|
||
|
end
|
||
|
|
||
|
def make_header_operands insn
|
||
|
vars = insn.opes
|
||
|
n = 0
|
||
|
ops = []
|
||
|
|
||
|
vars.each_with_index{|(type, var), i|
|
||
|
if type == '...'
|
||
|
break
|
||
|
end
|
||
|
|
||
|
ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});"
|
||
|
n += 1
|
||
|
}
|
||
|
@opn = n
|
||
|
|
||
|
# reverse or not?
|
||
|
# ops.join
|
||
|
ops.reverse.join("\n") + "\n"
|
||
|
end
|
||
|
|
||
|
def make_header_default_operands insn
|
||
|
ret = []
|
||
|
vars = insn.defopes
|
||
|
|
||
|
vars.each{|e|
|
||
|
next if e[1] == '*'
|
||
|
if $use_const
|
||
|
ret << " const #{e[0][0]} #{e[0][1]} = #{e[1]};"
|
||
|
else
|
||
|
ret << " #define #{e[0][1]} #{e[1]}"
|
||
|
end
|
||
|
}
|
||
|
ret.join("\n") + "\n"
|
||
|
end
|
||
|
|
||
|
def make_footer_default_operands insn
|
||
|
if $use_const
|
||
|
"\n"
|
||
|
else
|
||
|
ret = []
|
||
|
vars = insn.defopes
|
||
|
|
||
|
vars.each{|e|
|
||
|
next if e[1] == '*'
|
||
|
ret << "#undef #{e[0][1]}\n"
|
||
|
}
|
||
|
ret.join("\n") + "\n"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def make_header_stack_pops insn
|
||
|
n = 0
|
||
|
pops = []
|
||
|
|
||
|
vars = insn.pops
|
||
|
|
||
|
vars.each_with_index{|iter, i|
|
||
|
type, var, r = *iter
|
||
|
|
||
|
if type == '...'
|
||
|
break
|
||
|
end
|
||
|
if r
|
||
|
pops << " #{type} #{var} = SCREG(#{r});"
|
||
|
else
|
||
|
pops << " #{type} #{var} = TOPN(#{n});"
|
||
|
n += 1
|
||
|
end
|
||
|
}
|
||
|
@popn = n
|
||
|
|
||
|
# reverse or not?
|
||
|
pops.reverse.join("\n") + "\n"
|
||
|
end
|
||
|
|
||
|
def make_header_temporary_vars insn
|
||
|
ret = []
|
||
|
insn.tvars.each{|var|
|
||
|
ret << " #{var[0]} #{var[1]};"
|
||
|
}
|
||
|
ret.join("\n") + "\n"
|
||
|
end
|
||
|
|
||
|
def make_header_stack_val insn
|
||
|
ret = []
|
||
|
|
||
|
vars = insn.opes + insn.pops + insn.defopes.map{|e| e[0]}
|
||
|
|
||
|
insn.rets.each{|var|
|
||
|
if vars.all?{|e| e[1] != var[1]} && var[1] != '...'
|
||
|
ret << " #{var[0]} #{var[1]};"
|
||
|
end
|
||
|
}
|
||
|
ret.join("\n") + "\n"
|
||
|
end
|
||
|
|
||
|
def make_footer_stack_val insn
|
||
|
ret = []
|
||
|
insn.rets.reverse_each{|v|
|
||
|
if v[1] == '...'
|
||
|
break
|
||
|
end
|
||
|
if v[2]
|
||
|
ret << " SCREG(#{v[2]}) = #{v[1]};"
|
||
|
else
|
||
|
ret << " PUSH(#{v[1]});"
|
||
|
end
|
||
|
}
|
||
|
ret.join("\n") + "\n"
|
||
|
end
|
||
|
|
||
|
def make_header insn
|
||
|
ret = "\nINSN_ENTRY(#{insn.name}){\n"
|
||
|
ret += " /* prepare stack status */\n" if $verbose
|
||
|
ret += make_header_prepare_stack insn
|
||
|
ret += "{\n"
|
||
|
ret += " /* declare stack push val */\n" if $verbose
|
||
|
ret += make_header_stack_val insn
|
||
|
ret += " /* declare and initialize default opes */\n" if $verbose
|
||
|
ret += make_header_default_operands insn
|
||
|
ret += " /* declare and get from iseq */\n" if $verbose
|
||
|
ret += make_header_operands insn
|
||
|
ret += " /* declare and pop from stack */\n" if $verbose
|
||
|
ret += make_header_stack_pops insn
|
||
|
ret += " /* declare temporary vars */\n" if $verbose
|
||
|
ret += make_header_temporary_vars insn
|
||
|
|
||
|
ret += " /* for debug */\n" if $verbose
|
||
|
ret += " DEBUG_ENTER_INSN(\"#{insn.name}\");\n"
|
||
|
ret += " /* management */\n" if $verbose
|
||
|
ret += " ADD_PC(1+#{@opn});\n"
|
||
|
ret += " PREFETCH(GET_PC());\n"
|
||
|
ret += " POPN(#{@popn});\n" if @popn > 0
|
||
|
ret += " #define CURRENT_INSN_#{insn.name} 1\n"
|
||
|
ret += " #define INSN_IS_SC() #{insn.sc ? 0 : 1}\n"
|
||
|
ret += " #define INSN_LABEL(lab) LABEL_#{insn.name}_##lab\n"
|
||
|
|
||
|
ret += " #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}\n"
|
||
|
|
||
|
ret += " USAGE_ANALYSIS_INSN(BIN(#{insn.name}));\n"
|
||
|
insn.opes.each_with_index{|op, i|
|
||
|
ret += " USAGE_ANALYSIS_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});\n"
|
||
|
}
|
||
|
ret += "{\n"
|
||
|
|
||
|
end
|
||
|
|
||
|
def make_footer insn
|
||
|
ret = ''
|
||
|
ret = " /* push stack val */\n" if $verbose
|
||
|
ret += make_footer_stack_val insn
|
||
|
# debug info
|
||
|
|
||
|
# epilogue
|
||
|
ret += make_footer_default_operands insn
|
||
|
ret += "#undef CURRENT_INSN_#{insn.name}\n"
|
||
|
ret += "#undef INSN_IS_SC\n"
|
||
|
ret += "#undef INSN_LABEL\n"
|
||
|
ret += "#undef LABEL_IS_SC\n"
|
||
|
ret += " END_INSN(#{insn.name});\n}}\n"
|
||
|
ret += "}\n"
|
||
|
end
|
||
|
|
||
|
def make_insn_def insn
|
||
|
ret = make_header insn
|
||
|
if line = insn.body.instance_variable_get(:@line_no)
|
||
|
ret << "#line #{line+1} \"#{$insns_def}\"" << "\n"
|
||
|
ret << insn.body
|
||
|
ret << '#line __CURRENT_LINE__ "vm.inc"' << "\n"
|
||
|
else
|
||
|
ret << insn.body
|
||
|
end
|
||
|
ret << make_footer(insn)
|
||
|
end
|
||
|
|
||
|
# vm.inc
|
||
|
def vm_inc
|
||
|
vm_body = ''
|
||
|
|
||
|
@insns.each{|insn|
|
||
|
vm_body << "\n"
|
||
|
vm_body << make_insn_def(insn)
|
||
|
}
|
||
|
src = File.read(File.join($srcdir, '/template/vm.inc.tmpl'))
|
||
|
ERB.new(src).result(binding)
|
||
|
end
|
||
|
|
||
|
|
||
|
###################################################################
|
||
|
# vmtc.inc
|
||
|
|
||
|
def vmtc_inc
|
||
|
insns_table = ''
|
||
|
insns_end_table = ''
|
||
|
|
||
|
@insns.each{|insn|
|
||
|
insns_table << " LABEL_PTR(#{insn.name}),\n"
|
||
|
}
|
||
|
@insns.each{|insn|
|
||
|
insns_end_table << " ELABEL_PTR(#{insn.name}),\n"
|
||
|
}
|
||
|
|
||
|
ERB.new(File.read($srcdir + '/template/vmtc.inc.tmpl')).result(binding)
|
||
|
end
|
||
|
|
||
|
|
||
|
###################################################################
|
||
|
# insns_info.inc
|
||
|
|
||
|
def op2typesig op
|
||
|
case op
|
||
|
when /^OFFSET/
|
||
|
"TS_OFFSET"
|
||
|
when /^num_t/
|
||
|
"TS_NUM"
|
||
|
when /^lindex_t/
|
||
|
"TS_LINDEX"
|
||
|
when /^dindex_t/
|
||
|
"TS_DINDEX"
|
||
|
when /^VALUE/
|
||
|
"TS_VALUE"
|
||
|
when /^ID/
|
||
|
"TS_ID"
|
||
|
when /GENTRY/
|
||
|
"TS_GENTRY"
|
||
|
when /^IC/
|
||
|
"TS_IC"
|
||
|
when /^\.\.\./
|
||
|
"TS_VARIABLE"
|
||
|
when /^CDHASH/
|
||
|
"TS_CDHASH"
|
||
|
when /^ISEQ/
|
||
|
"TS_ISEQ"
|
||
|
else
|
||
|
raise "unknown op type: #{op}"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
TYPE_CHARS = {
|
||
|
'TS_OFFSET' => 'O',
|
||
|
'TS_NUM' => 'N',
|
||
|
'TS_LINDEX' => 'L',
|
||
|
'TS_DINDEX' => 'D',
|
||
|
'TS_VALUE' => 'V',
|
||
|
'TS_ID' => 'I',
|
||
|
'TS_GENTRY' => 'G',
|
||
|
'TS_IC' => 'C',
|
||
|
'TS_CDHASH' => 'H',
|
||
|
'TS_ISEQ' => 'S',
|
||
|
'TS_VARIABLE' => '.',
|
||
|
}
|
||
|
|
||
|
# insns_info.inc
|
||
|
def insns_info_inc
|
||
|
# insn_type_chars
|
||
|
insn_type_chars = TYPE_CHARS.map{|t, c|
|
||
|
"#define #{t} '#{c}'"
|
||
|
}.join("\n")
|
||
|
|
||
|
# insn_names
|
||
|
insn_names = ''
|
||
|
@insns.each{|insn|
|
||
|
insn_names << " \"#{insn.name}\",\n"
|
||
|
}
|
||
|
|
||
|
# operands info
|
||
|
operands_info = ''
|
||
|
operands_num_info = ''
|
||
|
@insns.each{|insn|
|
||
|
opes = insn.opes
|
||
|
operands_info << ' '
|
||
|
ot = opes.map{|type, var|
|
||
|
TYPE_CHARS.fetch(op2typesig(type))
|
||
|
}
|
||
|
operands_info << "\"#{ot.join}\"" << ", \n"
|
||
|
|
||
|
num = opes.size + 1
|
||
|
operands_num_info << " #{num},\n"
|
||
|
}
|
||
|
|
||
|
# stack num
|
||
|
stack_num_info = ''
|
||
|
@insns.each{|insn|
|
||
|
num = insn.rets.size
|
||
|
stack_num_info << " #{num},\n"
|
||
|
}
|
||
|
|
||
|
# stack increase
|
||
|
stack_increase = ''
|
||
|
@insns.each{|insn|
|
||
|
stack_increase << <<-EOS
|
||
|
case BIN(#{insn.name}):{
|
||
|
#{insn.sp_increase_c_expr}
|
||
|
}
|
||
|
EOS
|
||
|
}
|
||
|
ERB.new(File.read($srcdir + '/template/insns_info.inc.tmpl')).result(binding)
|
||
|
end
|
||
|
|
||
|
|
||
|
###################################################################
|
||
|
# insns.inc
|
||
|
def insns_inc
|
||
|
insns = ''
|
||
|
i=0
|
||
|
@insns.each{|insn|
|
||
|
insns << " %-30s = %d,\n" % ["BIN(#{insn.name})", i]
|
||
|
i+=1
|
||
|
}
|
||
|
ERB.new(File.read($srcdir + '/template/insns.inc.tmpl')).result(binding)
|
||
|
end
|
||
|
|
||
|
|
||
|
###################################################################
|
||
|
# minsns.inc
|
||
|
def minsns_inc
|
||
|
defs = ''
|
||
|
i=0
|
||
|
@insns.each{|insn|
|
||
|
defs << " rb_define_const(mYarvInsns, %-30s, INT2FIX(%d));\n" %
|
||
|
["\"I#{insn.name}\"", i]
|
||
|
i+=1
|
||
|
}
|
||
|
ERB.new(File.read($srcdir + '/template/minsns.inc.tmpl')).result(binding)
|
||
|
end
|
||
|
|
||
|
|
||
|
###################################################################
|
||
|
# optinsn.inc
|
||
|
def val_as_type op
|
||
|
type = op[0][0]
|
||
|
val = op[1]
|
||
|
|
||
|
case type
|
||
|
when /^long/, /^num_t/, /^lindex_t/, /^dindex_t/
|
||
|
"INT2FIX(#{val})"
|
||
|
when /^VALUE/
|
||
|
val
|
||
|
when /^ID/
|
||
|
"INT2FIX(#{val})"
|
||
|
when /^ISEQ/
|
||
|
val
|
||
|
when /GENTRY/
|
||
|
raise
|
||
|
when /^\.\.\./
|
||
|
raise
|
||
|
else
|
||
|
raise "type: #{type}"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# optinsn.inc
|
||
|
def optinsn_inc
|
||
|
rule = ''
|
||
|
opt_insns_map = Hash.new{|h, k| h[k] = []}
|
||
|
|
||
|
@insns.each{|insn|
|
||
|
next if insn.defopes.size == 0
|
||
|
next if insn.type == :sc
|
||
|
next if /^UNIFIED/ =~ insn.name.to_s
|
||
|
|
||
|
originsn = insn.orig
|
||
|
opt_insns_map[originsn] << insn
|
||
|
}
|
||
|
|
||
|
opt_insns_map.each{|originsn, optinsns|
|
||
|
rule += "case BIN(#{originsn.name}):\n"
|
||
|
|
||
|
optinsns.sort_by{|opti|
|
||
|
opti.defopes.find_all{|e| e[1] == '*'}.size
|
||
|
}.each{|opti|
|
||
|
rule += " if(\n"
|
||
|
i = 0
|
||
|
rule += ' ' + opti.defopes.map{|opinfo|
|
||
|
i += 1
|
||
|
next if opinfo[1] == '*'
|
||
|
"insnobj->operands[#{i-1}] == #{val_as_type(opinfo)}\n"
|
||
|
}.compact.join('&& ')
|
||
|
rule += " ){\n"
|
||
|
idx = 0
|
||
|
n = 0
|
||
|
opti.defopes.each{|opinfo|
|
||
|
if opinfo[1] == '*'
|
||
|
if idx != n
|
||
|
rule += " insnobj->operands[#{idx}] = insnobj->operands[#{n}];\n"
|
||
|
end
|
||
|
idx += 1
|
||
|
else
|
||
|
# skip
|
||
|
end
|
||
|
n += 1
|
||
|
}
|
||
|
rule += " insnobj->insn_id = BIN(#{opti.name});\n"
|
||
|
rule += " insnobj->operand_size = #{idx};\n"
|
||
|
rule += " break;\n }\n"
|
||
|
}
|
||
|
rule += " break;\n";
|
||
|
}
|
||
|
ERB.new(File.read($srcdir + '/template/optinsn.inc.tmpl')).result(binding)
|
||
|
end
|
||
|
|
||
|
|
||
|
###################################################################
|
||
|
# optunifs.inc
|
||
|
def optunifs_unc
|
||
|
unif_insns_each = ''
|
||
|
unif_insns = ''
|
||
|
unif_insns_data = []
|
||
|
|
||
|
insns = find_all{|insn| !insn.is_sc}
|
||
|
insns.each{|insn|
|
||
|
size = insn.unifs.size
|
||
|
if size > 0
|
||
|
require 'pp'
|
||
|
|
||
|
insn.unifs.sort_by{|unif| -unif[1].size}.each_with_index{|unif, i|
|
||
|
|
||
|
uni_insn, uni_insns = *unif
|
||
|
uni_insns = uni_insns[1..-1]
|
||
|
unif_insns_each << "static int UNIFIED_#{insn.name}_#{i}[] = {" +
|
||
|
" BIN(#{uni_insn.name}), #{uni_insns.size + 2}, \n " +
|
||
|
uni_insns.map{|e| "BIN(#{e.name})"}.join(", ") + "};\n"
|
||
|
}
|
||
|
else
|
||
|
|
||
|
end
|
||
|
if size > 0
|
||
|
unif_insns << "static int *UNIFIED_#{insn.name}[] = {(int *)#{size+1}, \n"
|
||
|
unif_insns << (0...size).map{|e| " UNIFIED_#{insn.name}_#{e}"}.join(",\n") + "};\n"
|
||
|
unif_insns_data << " UNIFIED_#{insn.name}"
|
||
|
else
|
||
|
unif_insns_data << " 0"
|
||
|
end
|
||
|
}
|
||
|
unif_insns_data = "static int **unified_insns_data[] = {\n" +
|
||
|
unif_insns_data.join(",\n") + "};\n"
|
||
|
ERB.new(File.read($srcdir + '/template/optunifs.inc.tmpl')).result(binding)
|
||
|
end
|
||
|
|
||
|
###################################################################
|
||
|
# opt_sc.inc
|
||
|
def opt_sc_inc
|
||
|
sc_insn_info = []
|
||
|
@insns.each{|insn|
|
||
|
insns = insn.sc
|
||
|
if insns.size > 0
|
||
|
insns = ['SC_ERROR'] + insns.map{|e| " BIN(#{e.name})"}
|
||
|
else
|
||
|
insns = Array.new(6){'SC_ERROR'}
|
||
|
end
|
||
|
sc_insn_info << " {\n#{insns.join(",\n")}}"
|
||
|
}
|
||
|
sc_insn_info = sc_insn_info.join(",\n")
|
||
|
|
||
|
sc_insn_next = @insns.map{|insn|
|
||
|
" SCS_#{complement_name(insn.nextsc).upcase}" +
|
||
|
($verbose ? " /* #{insn.name} */" : '')
|
||
|
}.join(",\n")
|
||
|
ERB.new(File.read($srcdir + '/template/opt_sc.inc.tmpl')).result(binding)
|
||
|
end
|
||
|
|
||
|
###################################################################
|
||
|
# yasmdata.rb
|
||
|
def yasmdata_rb
|
||
|
insn_id2no = ''
|
||
|
@insns.each_with_index{|insn, i|
|
||
|
insn_id2no << " :#{insn.name} => #{i},\n"
|
||
|
}
|
||
|
ERB.new(File.read($srcdir + '/template/yasmdata.rb.tmpl')).result(binding)
|
||
|
end
|
||
|
|
||
|
###################################################################
|
||
|
# yarvarch.*
|
||
|
def desc lang
|
||
|
d = ''
|
||
|
i = 0
|
||
|
cat = nil
|
||
|
@insns.each{|insn|
|
||
|
seq = insn.opes.map{|t,v| v}.join(' ')
|
||
|
before = insn.pops.reverse.map{|t,v| v}.join(' ')
|
||
|
after = insn.rets.reverse.map{|t,v| v}.join(' ')
|
||
|
|
||
|
if cat != insn.comm[:c]
|
||
|
d << "** #{insn.comm[:c]}\n\n"
|
||
|
cat = insn.comm[:c]
|
||
|
end
|
||
|
|
||
|
d << "*** #{insn.name}\n"
|
||
|
d << "\n"
|
||
|
d << insn.comm[lang] + "\n\n"
|
||
|
d << ":instruction sequence: 0x%02x #{seq}\n" % i
|
||
|
d << ":stack: #{before} => #{after}\n\n"
|
||
|
i+=1
|
||
|
}
|
||
|
d
|
||
|
end
|
||
|
|
||
|
def desc_ja
|
||
|
d = desc :j
|
||
|
ERB.new(File.read($srcdir + '/template/yarvarch.ja')).result(binding)
|
||
|
end
|
||
|
|
||
|
def desc_en
|
||
|
d = desc :e
|
||
|
ERB.new(File.read($srcdir + '/template/yarvarch.en')).result(binding)
|
||
|
end
|
||
|
|
||
|
def vm_macro_inc
|
||
|
ret = ''
|
||
|
flag = false
|
||
|
File.read($srcdir + '/vm_macro.def').each_line{|line|
|
||
|
line.rstrip!
|
||
|
if /^MACRO\s/ =~ line
|
||
|
line.sub!(/^MACRO/, '#define')
|
||
|
flag = true
|
||
|
elsif /^\}/ =~ line
|
||
|
flag = false
|
||
|
end
|
||
|
|
||
|
ret << line + (flag ? " \\" : '') + "\n"
|
||
|
}
|
||
|
ret
|
||
|
end
|
||
|
|
||
|
Files = { # codes
|
||
|
'vm.inc' => :vm_inc,
|
||
|
'vmtc.inc' => :vmtc_inc,
|
||
|
'insns.inc' => :insns_inc,
|
||
|
'insns_info.inc' => :insns_info_inc,
|
||
|
# 'minsns.inc' => :minsns_inc,
|
||
|
'optinsn.inc' => :optinsn_inc,
|
||
|
'optunifs.inc' => :optunifs_unc,
|
||
|
'opt_sc.inc' => :opt_sc_inc,
|
||
|
'yasmdata.rb' => :yasmdata_rb,
|
||
|
'vm_macro.inc' => :vm_macro_inc,
|
||
|
}
|
||
|
|
||
|
def self.make_sources insns_def, opopt_def, unif_def, args = []
|
||
|
insns = InsnsDef.new(insns_def, opopt_def, unif_def)
|
||
|
|
||
|
args = Files.keys if args.empty?
|
||
|
|
||
|
args.each{|fn|
|
||
|
s = Files[fn]
|
||
|
|
||
|
open(fn, 'w'){|f|
|
||
|
f.puts(insns.__send__(s))
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
##############################################
|
||
|
files = []
|
||
|
$opts = {}
|
||
|
$srcdir = '.'
|
||
|
insns_def = 'insns.def'
|
||
|
opope_def = 'opt_operand.def'
|
||
|
unif_def = 'opt_insn_unif.def'
|
||
|
|
||
|
ARGV.each{|e|
|
||
|
case e
|
||
|
when /\A\-D(\w+)/
|
||
|
$opts[$1] = true
|
||
|
when /\A--srcdir=(.+)/
|
||
|
$srcdir = $1
|
||
|
when /\A--insnsdef=(.+)/
|
||
|
insns_def = $1
|
||
|
when /\A--opt-operanddef=(.+)/
|
||
|
opope_def = $1
|
||
|
when /\A--opt-insnunifdef=(.+)/
|
||
|
unif_def = $1
|
||
|
when /\A-/
|
||
|
# ignore
|
||
|
else
|
||
|
files << e
|
||
|
end
|
||
|
}
|
||
|
|
||
|
optfile = File.join(Dir.pwd, 'vm_opts.h')
|
||
|
basefile = File.join($srcdir, 'vm_opts.h.base')
|
||
|
if !FileTest.exist?(optfile) || File.mtime(optfile) < File.mtime(basefile)
|
||
|
require 'fileutils'
|
||
|
FileUtils.cp(File.join($srcdir, 'vm_opts.h.base'), optfile)
|
||
|
end
|
||
|
|
||
|
if optfile
|
||
|
open(optfile){|f|
|
||
|
f.each_line{|line|
|
||
|
if /^\#define\s+(OPT_[A-Z_]+)\s+1/ =~ line
|
||
|
$opts[$1] = true
|
||
|
end
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
$insns_def = File.join($srcdir, insns_def)
|
||
|
$opope_def = File.join($srcdir, opope_def)
|
||
|
$unif_def = File.join($srcdir, unif_def)
|
||
|
|
||
|
def insns_def_new
|
||
|
InsnsDef.new $insns_def, $opope_def, $unif_def
|
||
|
end
|
||
|
|
||
|
if $0 == __FILE__
|
||
|
InsnsDef.make_sources $insns_def, $opope_def, $unif_def, files
|
||
|
end
|
||
|
|