9
def initialize name, opes, pops, rets, comm, body, tvars, sp_inc,
10
orig = self, defopes = [], type = nil,
11
nsc = [], psc = [[], []]
14
@opes = opes # [[type, name], ...]
15
@pops = pops # [[type, name], ...]
16
@rets = rets # [[type, name], ...]
17
@comm = comm # {:c => category, :e => en desc, :j => ja desc}
39
attr_reader :name, :opes, :pops, :rets
40
attr_reader :body, :comm
41
attr_reader :nextsc, :pushsc
42
attr_reader :orig, :defopes, :type
44
attr_reader :unifs, :optimized
57
def add_optimized insn
61
def sp_increase_c_expr
62
if(pops.any?{|t, v| v == '...'} ||
63
rets.any?{|t, v| v == '...'})
65
raise "no sp increase definition" if @sp_inc.nil?
66
ret = "int inc = 0;\n"
68
@opes.each_with_index{|(t, v), i|
70
ret << " unsigned long #{v} = FIX2INT(opes[#{i}]);\n"
73
@defopes.each_with_index{|((t, var), val), i|
74
if t == 'rb_num_t' && val != '*'
75
ret << " unsigned long #{var} = #{val};\n"
79
ret << " #{@sp_inc};\n"
80
ret << " return depth + inc;"
83
"return depth + #{rets.size - pops.size};"
88
"#<Instruction:#{@name}>"
92
class InstructionsLoader
93
def initialize opts = {}
97
@vpath = opts[:VPATH] || File
98
@use_const = opts[:use_const]
99
@verbose = opts[:verbose]
100
@destdir = opts[:destdir]
102
(@vm_opts = load_vm_opts).each {|k, v|
103
@vm_opts[k] = opts[k] if opts.key?(k)
106
load_insns_def opts[:"insns.def"] || 'insns.def'
108
load_opt_operand_def opts[:"opope.def"] || 'opt_operand.def'
109
load_insn_unification_def opts[:"unif.def"] || 'opt_insn_unif.def'
110
make_stackcaching_insns if vm_opt?('STACK_CACHING')
116
%w[use_const verbose].each do |attr|
118
alias_method "#{attr}?", attr
143
def load_vm_opts file = nil
146
vpath.open(file) do |f|
147
f.grep(/^\#define\s+OPT_([A-Z_]+)\s+(\d+)/) do
148
opts[$1] = !$2.to_i.zero?
154
SKIP_COMMENT_PATTERN = Regexp.compile(Regexp.escape('/** ##skip'))
160
@insn_map[insn.name] = insn
163
def make_insn name, opes, pops, rets, comm, body, sp_inc
164
add_insn Instruction.new(name, opes, pops, rets, comm, body, [], sp_inc)
167
# str -> [[type, var], ...]
169
raise unless /\((.*?)\)/ =~ line
172
if /\s*(\S+)\s+(\S+)\s*/ =~ v
175
elsif /\s*\.\.\.\s*/ =~ v
185
def parse_comment comm
189
comm.each_line{|line|
209
def load_insns_def file
210
body = insn = opes = pops = rets = nil
213
vpath.open(file) {|f|
214
f.instance_variable_set(:@line_no, 0)
229
when SKIP_COMMENT_PATTERN
230
while line = f.gets.chomp
231
if /\s+\*\/$/ =~ line
236
# collect instruction comment
239
if /\s+\*\/\s*$/ =~ line
246
# start instruction body
249
opes = parse_vars(f.gets.chomp)
250
pops = parse_vars(f.gets.chomp).reverse
251
rets_str = f.gets.chomp
252
rets = parse_vars(rets_str).reverse
253
comment = parse_comment(comment)
257
sp_inc = rets_str[%r"//\s*(.+)", 1]
259
raise unless /^\{$/ =~ f.gets.chomp
262
# end instruction body
265
body.instance_variable_set(:@line_no, line_no)
266
body.instance_variable_set(:@file, f.path)
267
insn = make_insn(insn, opes, pops, rets, comment, body, sp_inc)
282
def load_opt_operand_def file
283
vpath.foreach(file) {|line|
284
line = line.gsub(/\#.*/, '').strip
285
next if line.length == 0
286
break if /__END__/ =~ line
287
/(\S+)\s+(.+)/ =~ line
290
add_opt_operand insn, opts.split(/,/).map{|e| e.strip}
294
def label_escape label
295
label.gsub(/\(/, '_O_').
300
def add_opt_operand insn_name, opts
301
insn = @insn_map[insn_name]
304
if opes.size != opts.size
305
raise "operand size mismatcvh for #{insn.name} (opes: #{opes.size}, opts: #{opts.size})"
308
ninsn = insn.name + '_OP_' + opts.map{|e| label_escape(e)}.join('_')
312
opts.each_with_index{|e, i|
319
make_insn_operand_optimiized(insn, ninsn, nopes, defv)
322
def make_insn_operand_optimiized orig_insn, name, opes, defopes
323
comm = orig_insn.comm.dup
324
comm[:c] = 'optimize'
325
add_insn insn = Instruction.new(
326
name, opes, orig_insn.pops, orig_insn.rets, comm,
327
orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
329
orig_insn.add_optimized insn
333
def load_insn_unification_def file
334
vpath.foreach(file) {|line|
335
line = line.gsub(/\#.*/, '').strip
336
next if line.length == 0
337
break if /__END__/ =~ line
338
make_unified_insns line.split.map{|e|
339
raise "unknown insn: #{e}" unless @insn_map[e]
345
def all_combination sets
346
ret = sets.shift.map{|e| [e]}
362
def make_unified_insns insns
363
if vm_opt?('UNIFY_ALL_COMBINATION')
364
insn_sets = insns.map{|insn|
365
[insn] + insn.optimized
368
all_combination(insn_sets).each{|insns_set|
369
make_unified_insn_each insns_set
372
make_unified_insn_each insns
376
def mk_private_val vals, i, redef
384
v[1] = "#{v[1]}_#{i}"
390
def mk_private_val2 vals, i, redef
397
v = pv[0] = pv[0].dup
400
v[1] = "#{v[1]}_#{i}"
406
def make_unified_insn_each insns
413
:e => 'unified insn',
414
:j => 'unified insn',
422
insns.each_with_index{|insn, i|
426
e_opes = mk_private_val(insn.opes, i, redef_vars)
427
e_pops = mk_private_val(insn.pops, i, redef_vars)
428
e_rets = mk_private_val(insn.rets, i, redef_vars)
430
e_defs = mk_private_val2(insn.defopes, i, redef_vars)
433
while pvar = e_pops.pop
436
raise "unsupported unif insn: #{insns.inspect}" if rvar[0] == '...'
437
passed_vars << [pvar, rvar]
448
defopes.concat e_defs
449
sp_inc += "#{insn.sp_inc}"
451
body += "{ /* unif: #{i} */\n" +
452
passed_vars.map{|rpvars|
455
"#define #{pv[1]} #{rv[1]}"
458
redef_vars.map{|v, type|
459
"#define #{v} #{v}_#{i}"
460
}.join("\n") + "\n" +
462
passed_vars.map{|rpvars|
463
"#undef #{rpvars[0][1]}"
466
redef_vars.keys.map{|v|
474
unless opes.any?{|var|
476
} || defopes.any?{|pvar|
477
pvar[0][1] == tvar[1]
482
add_insn insn = Instruction.new("UNIFIED_" + names.join('_'),
483
opes, pops, rets.reverse, comm, body,
485
insn.defopes.replace defopes
486
insns[0].add_unif [insn, insns]
490
SPECIAL_INSN_FOR_SC_AFTER = {
494
/\Aclassdef/ => [:a],
495
/\Amoduledef/ => [:a],
497
FROM_SC = [[], [:a], [:b], [:a, :b], [:b, :a]]
499
def make_stackcaching_insns
502
@insns.dup.each{|insn|
509
SPECIAL_INSN_FOR_SC_AFTER.any?{|k, v|
518
name, pops, rets, pushs1, pushs2, nextsc =
519
*calc_stack(insn, from, after, opops, orets)
521
make_insn_sc(insn, name, oopes, pops, rets, [pushs1, pushs2], nextsc)
526
def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc
527
comm = orig_insn.comm.dup
528
comm[:c] = 'optimize(sc)'
530
scinsn = Instruction.new(
531
name, opes, pops, rets, comm,
532
orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
533
orig_insn, orig_insn.defopes, :sc, nextsc, pushs)
536
orig_insn.add_sc scinsn
539
def self.complement_name st
540
"#{st[0] ? st[0] : 'x'}#{st[1] ? st[1] : 'x'}"
543
def add_stack_value st
556
st[0], st[1] = st[1], st[0]
561
def calc_stack insn, ofrom, oafter, opops, orets
570
pops.each_with_index{|e, i|
577
pops[i] = pops[i].dup << r
582
from.each_with_index{|r, i|
583
rets[i] = rets[i].dup << r if rets[i]
587
rets.each_with_index{|e, i|
588
break if e[0] == '...'
589
pushed, r = add_stack_value from
590
rets[i] = rets[i].dup << r
603
if false #|| insn.name =~ /test3/
613
ret = ["#{insn.name}_SC_#{InstructionsLoader.complement_name(ofrom)}_#{complement_name(from)}",
614
pops, rets, pushs_before, pushs, from]
618
class SourceCodeGenerator
626
raise "should not reach here"
647
EMPTY_STRING = ''.freeze
649
def commit str = EMPTY_STRING
654
@lines << str if verbose?
659
fn = File.join(d, fn) if d
664
###################################################################
666
class VmBodyGenerator < SourceCodeGenerator
672
vm_body << make_insn_def(insn)
674
src = vpath.read('template/vm.inc.tmpl')
675
ERB.new(src).result(binding)
678
def generate_from_insnname insnname
679
make_insn_def @insns[insnname.to_s]
685
def make_header_prepare_stack insn
686
comment " /* prepare stack status */"
688
push_ba = insn.pushsc
689
raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0
693
commit " PUSH(SCREG(#{r}));"
698
def make_header_operands insn
699
comment " /* declare and get from iseq */"
705
vars.each_with_index{|(type, var), i|
710
ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});"
720
def make_header_default_operands insn
726
commit " const #{e[0][0]} #{e[0][1]} = #{e[1]};"
728
commit " #define #{e[0][1]} #{e[1]}"
733
def make_footer_default_operands insn
734
comment " /* declare and initialize default opes */"
742
commit "#undef #{e[0][1]}"
747
def make_header_stack_pops insn
748
comment " /* declare and pop from stack */"
753
vars.each_with_index{|iter, i|
759
pops << " #{type} #{var} = SCREG(#{r});"
761
pops << " #{type} #{var} = TOPN(#{n});"
771
def make_header_temporary_vars insn
772
comment " /* declare temporary vars */"
774
insn.tvars.each{|var|
775
commit " #{var[0]} #{var[1]};"
779
def make_header_stack_val insn
780
comment "/* declare stack push val */"
782
vars = insn.opes + insn.pops + insn.defopes.map{|e| e[0]}
785
if vars.all?{|e| e[1] != var[1]} && var[1] != '...'
786
commit " #{var[0]} #{var[1]};"
791
def make_header_analysis insn
792
commit " USAGE_ANALYSIS_INSN(BIN(#{insn.name}));"
793
insn.opes.each_with_index{|op, i|
794
commit " USAGE_ANALYSIS_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});"
798
def make_header_pc insn
799
commit " ADD_PC(1+#{@opn});"
800
commit " PREFETCH(GET_PC());"
803
def make_header_popn insn
804
comment " /* management */"
805
commit " POPN(#{@popn});" if @popn > 0
808
def make_hader_debug insn
809
comment " /* for debug */"
810
commit " DEBUG_ENTER_INSN(\"#{insn.name}\");"
813
def make_header_defines insn
814
commit " #define CURRENT_INSN_#{insn.name} 1"
815
commit " #define INSN_IS_SC() #{insn.sc ? 0 : 1}"
816
commit " #define INSN_LABEL(lab) LABEL_#{insn.name}_##lab"
817
commit " #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}"
820
def make_footer_stack_val insn
821
comment " /* push stack val */"
823
insn.rets.reverse_each{|v|
828
commit " SCREG(#{v[2]}) = #{v[1]};"
830
commit " PUSH(#{v[1]});"
835
def make_footer_undefs insn
836
commit "#undef CURRENT_INSN_#{insn.name}"
837
commit "#undef INSN_IS_SC"
838
commit "#undef INSN_LABEL"
839
commit "#undef LABEL_IS_SC"
843
commit "INSN_ENTRY(#{insn.name}){"
844
make_header_prepare_stack insn
846
make_header_stack_val insn
847
make_header_default_operands insn
848
make_header_operands insn
849
make_header_stack_pops insn
850
make_header_temporary_vars insn
852
make_hader_debug insn
854
make_header_popn insn
855
make_header_defines insn
856
make_header_analysis insn
861
make_footer_stack_val insn
862
make_footer_default_operands insn
863
make_footer_undefs insn
864
commit " END_INSN(#{insn.name});}}}"
867
def make_insn_def insn
870
if line = insn.body.instance_variable_get(:@line_no)
871
file = insn.body.instance_variable_get(:@file)
872
commit "#line #{line+1} \"#{file}\""
874
commit '#line __CURRENT_LINE__ "__CURRENT_FILE__"'
883
###################################################################
885
class VmTCIncGenerator < SourceCodeGenerator
888
insns_table = build_string do
890
commit " LABEL_PTR(#{insn.name}),"
894
insn_end_table = build_string do
896
commit " ELABEL_PTR(#{insn.name}),\n"
900
ERB.new(vpath.read('template/vmtc.inc.tmpl')).result(binding)
904
###################################################################
906
class InsnsInfoIncGenerator < SourceCodeGenerator
938
when /rb_insn_func_t/
941
raise "unknown op type: #{op}"
956
'TS_VARIABLE' => '.',
963
insn_type_chars = TYPE_CHARS.map{|t, c|
964
"#define #{t} '#{c}'"
970
insn_names << " \"#{insn.name}\",\n"
975
operands_num_info = ''
979
ot = opes.map{|type, var|
980
TYPE_CHARS.fetch(op2typesig(type))
982
operands_info << "\"#{ot.join}\"" << ", \n"
985
operands_num_info << " #{num},\n"
992
stack_num_info << " #{num},\n"
998
stack_increase << <<-EOS
999
case BIN(#{insn.name}):{
1000
#{insn.sp_increase_c_expr}
1004
ERB.new(vpath.read('template/insns_info.inc.tmpl')).result(binding)
1008
###################################################################
1010
class InsnsIncGenerator < SourceCodeGenerator
1013
insns = build_string do
1015
commit " %-30s = %d,\n" % ["BIN(#{insn.name})", i]
1020
ERB.new(vpath.read('template/insns.inc.tmpl')).result(binding)
1024
###################################################################
1026
class MInsnsIncGenerator < SourceCodeGenerator
1029
defs = build_string do
1031
commit " rb_define_const(mYarvInsns, %-30s, INT2FIX(%d));\n" %
1032
["\"I#{insn.name}\"", i]
1036
ERB.new(vpath.read('template/minsns.inc.tmpl')).result(binding)
1040
###################################################################
1042
class OptInsnIncGenerator < SourceCodeGenerator
1055
when /^long/, /^rb_num_t/, /^lindex_t/, /^dindex_t/
1061
when /^ISEQ/, /^rb_insn_func_t/
1068
raise "type: #{type}"
1075
opt_insns_map = Hash.new{|h, k| h[k] = []}
1078
next if insn.defopes.size == 0
1079
next if insn.type == :sc
1080
next if /^UNIFIED/ =~ insn.name.to_s
1082
originsn = insn.orig
1083
opt_insns_map[originsn] << insn
1086
rule = build_string do
1087
opt_insns_map.each{|originsn, optinsns|
1088
commit "case BIN(#{originsn.name}):"
1090
optinsns.sort_by{|opti|
1091
opti.defopes.find_all{|e| e[1] == '*'}.size
1095
commit " " + opti.defopes.map{|opinfo|
1097
next if opinfo[1] == '*'
1098
"insnobj->operands[#{i-1}] == #{val_as_type(opinfo)}"
1099
}.compact.join('&& ')
1103
opti.defopes.each{|opinfo|
1106
commit " insnobj->operands[#{idx}] = insnobj->operands[#{n}];"
1114
commit " insnobj->insn_id = BIN(#{opti.name});"
1115
commit " insnobj->operand_size = #{idx};"
1116
commit " break;\n }\n"
1122
ERB.new(vpath.read('template/optinsn.inc.tmpl')).result(binding)
1126
###################################################################
1128
class OptUnifsIncGenerator < SourceCodeGenerator
1130
unif_insns_each = ''
1132
unif_insns_data = []
1134
insns = @insns.find_all{|insn| !insn.is_sc}
1136
size = insn.unifs.size
1138
insn.unifs.sort_by{|unif| -unif[1].size}.each_with_index{|unif, i|
1140
uni_insn, uni_insns = *unif
1141
uni_insns = uni_insns[1..-1]
1142
unif_insns_each << "static int UNIFIED_#{insn.name}_#{i}[] = {" +
1143
" BIN(#{uni_insn.name}), #{uni_insns.size + 2}, \n " +
1144
uni_insns.map{|e| "BIN(#{e.name})"}.join(", ") + "};\n"
1150
unif_insns << "static int *UNIFIED_#{insn.name}[] = {(int *)#{size+1}, \n"
1151
unif_insns << (0...size).map{|e| " UNIFIED_#{insn.name}_#{e}"}.join(",\n") + "};\n"
1152
unif_insns_data << " UNIFIED_#{insn.name}"
1154
unif_insns_data << " 0"
1157
unif_insns_data = "static int **unified_insns_data[] = {\n" +
1158
unif_insns_data.join(",\n") + "};\n"
1159
ERB.new(vpath.read('template/optunifs.inc.tmpl')).result(binding)
1163
###################################################################
1165
class OptSCIncGenerator < SourceCodeGenerator
1171
insns = ['SC_ERROR'] + insns.map{|e| " BIN(#{e.name})"}
1173
insns = Array.new(6){'SC_ERROR'}
1175
sc_insn_info << " {\n#{insns.join(",\n")}}"
1177
sc_insn_info = sc_insn_info.join(",\n")
1179
sc_insn_next = @insns.map{|insn|
1180
" SCS_#{InstructionsLoader.complement_name(insn.nextsc).upcase}" +
1181
(verbose? ? " /* #{insn.name} */" : '')
1183
ERB.new(vpath.read('template/opt_sc.inc.tmpl')).result(binding)
1187
###################################################################
1189
class YASMDataRbGenerator < SourceCodeGenerator
1192
@insns.each_with_index{|insn, i|
1193
insn_id2no << " :#{insn.name} => #{i},\n"
1195
ERB.new(vpath.read('template/yasmdata.rb.tmpl')).result(binding)
1199
###################################################################
1201
class YARVDocGenerator < SourceCodeGenerator
1211
seq = insn.opes.map{|t,v| v}.join(' ')
1212
before = insn.pops.reverse.map{|t,v| v}.join(' ')
1213
after = insn.rets.reverse.map{|t,v| v}.join(' ')
1215
if cat != insn.comm[:c]
1216
d << "** #{insn.comm[:c]}\n\n"
1220
d << "*** #{insn.name}\n"
1222
d << insn.comm[lang] + "\n\n"
1223
d << ":instruction sequence: 0x%02x #{seq}\n" % i
1224
d << ":stack: #{before} => #{after}\n\n"
1232
ERB.new(vpath.read('template/yarvarch.ja')).result(binding)
1237
ERB.new(vpath.read('template/yarvarch.en')).result(binding)
1242
def search(meth, base, *rest)
1244
meth.call(base, *rest)
1245
rescue Errno::ENOENT => error
1247
return meth.call(File.join(dir, base), *rest) rescue nil
1253
def process(*args, &block)
1254
search(File.method(__callee__), *args, &block)
1261
f = search(File.method(:open), *args)
1266
f.close unless f.closed?
1274
open(*args) {|f| f.read}
1277
def foreach(file, *args, &block)
1278
open(file) {|f| f.each(*args, &block)}
1281
def self.def_options(opt)
1285
opt.on("-I", "--srcdir=DIR", "add a directory to search path") {|dir|
1288
opt.on("-L", "--vpath=PATH LIST", "add directories to search path") {|dirs|
1289
vpath |= dirs.split(path_sep)
1291
opt.on("--path-separator=SEP", /\A\W\z/, "separator for vpath") {|sep|
1296
vpath.extend(self) unless vpath.empty?
1301
class SourceCodeGenerator
1303
'vm.inc' => VmBodyGenerator,
1304
'vmtc.inc' => VmTCIncGenerator,
1305
'insns.inc' => InsnsIncGenerator,
1306
'insns_info.inc' => InsnsInfoIncGenerator,
1307
# 'minsns.inc' => MInsnsIncGenerator,
1308
'optinsn.inc' => OptInsnIncGenerator,
1309
'optunifs.inc' => OptUnifsIncGenerator,
1310
'opt_sc.inc' => OptSCIncGenerator,
1311
'yasmdata.rb' => YASMDataRbGenerator,
1314
def generate args = []
1315
args = Files.keys if args.empty?
1317
s = Files[fn].new(@insns).generate
1318
open(output_path(fn), 'w') {|f| f.puts(s)}
1322
def self.def_options(opt)
1324
:"insns.def" => 'insns.def',
1325
:"opope.def" => 'opt_operand.def',
1326
:"unif.def" => 'opt_insn_unif.def',
1329
opt.on("-Dname", /\AOPT_(\w+)\z/, "enable VM option") {|s, v|
1332
opt.on("--enable=name[,name...]", Array,
1333
"enable VM options (without OPT_ prefix)") {|*a|
1334
a.each {|v| opts[v] = true}
1336
opt.on("-Uname", /\AOPT_(\w+)\z/, "disable VM option") {|s, v|
1339
opt.on("--disable=name[,name...]", Array,
1340
"disable VM options (without OPT_ prefix)") {|*a|
1341
a.each {|v| opts[v] = false}
1343
opt.on("-i", "--insnsdef=FILE", "--instructions-def",
1344
"instructions definition file") {|n|
1345
opts[:insns_def] = n
1347
opt.on("-o", "--opt-operanddef=FILE", "--opt-operand-def",
1348
"vm option: operand definition file") {|n|
1349
opts[:opope_def] = n
1351
opt.on("-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def",
1352
"vm option: instruction unification file") {|n|
1355
opt.on("-C", "--[no-]use-const",
1356
"use consts for default operands instead of macros") {|v|
1357
opts[:use_const] = v
1359
opt.on("-d", "--destdir", "--output-directory=DIR",
1360
"make output file underneath DIR") {|v|
1363
opt.on("-V", "--[no-]verbose") {|v|
1367
vpath = VPATH.def_options(opt)
1370
opts[:VPATH] = vpath.call
1375
def self.build opts, vpath = ['./']
1376
opts[:VPATH] = vpath.extend(VPATH) unless opts[:VPATH]
1377
self.new InstructionsLoader.new(opts)