1#!./miniruby 2# -*- coding: us-ascii -*- 3# 4# 5 6require 'erb' 7$:.unshift(File.dirname(__FILE__)) 8require 'vpath' 9 10class RubyVM 11 class Instruction 12 def initialize name, opes, pops, rets, comm, body, tvars, sp_inc, 13 orig = self, defopes = [], type = nil, 14 nsc = [], psc = [[], []] 15 16 @name = name 17 @opes = opes # [[type, name], ...] 18 @pops = pops # [[type, name], ...] 19 @rets = rets # [[type, name], ...] 20 @comm = comm # {:c => category, :e => en desc, :j => ja desc} 21 @body = body # '...' 22 23 @orig = orig 24 @defopes = defopes 25 @type = type 26 @tvars = tvars 27 28 @nextsc = nsc 29 @pushsc = psc 30 @sc = [] 31 @unifs = [] 32 @optimized = [] 33 @is_sc = false 34 @sp_inc = sp_inc 35 end 36 37 def add_sc sci 38 @sc << sci 39 sci.set_sc 40 end 41 42 attr_reader :name, :opes, :pops, :rets 43 attr_reader :body, :comm 44 attr_reader :nextsc, :pushsc 45 attr_reader :orig, :defopes, :type 46 attr_reader :sc 47 attr_reader :unifs, :optimized 48 attr_reader :is_sc 49 attr_reader :tvars 50 attr_reader :sp_inc 51 52 def set_sc 53 @is_sc = true 54 end 55 56 def add_unif insns 57 @unifs << insns 58 end 59 60 def add_optimized insn 61 @optimized << insn 62 end 63 64 def sp_increase_c_expr 65 if(pops.any?{|t, v| v == '...'} || 66 rets.any?{|t, v| v == '...'}) 67 # user definision 68 raise "no sp increase definition" if @sp_inc.nil? 69 ret = "int inc = 0;\n" 70 71 @opes.each_with_index{|(t, v), i| 72 if (t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc)) || 73 (@defopes.any?{|t, val| re =~ val}) 74 ret << " int #{v} = FIX2INT(opes[#{i}]);\n" 75 elsif (t == 'CALL_INFO' && ((re = /\b#{v}\b/n) =~ @sp_inc)) 76 ret << " CALL_INFO #{v} = (CALL_INFO)(opes[#{i}]);\n" 77 end 78 } 79 80 @defopes.each_with_index{|((t, var), val), i| 81 if t == 'rb_num_t' && val != '*' && /\b#{var}\b/ =~ @sp_inc 82 ret << " #{t} #{var} = #{val};\n" 83 end 84 } 85 86 ret << " #{@sp_inc};\n" 87 ret << " return depth + inc;" 88 ret 89 else 90 "return depth + #{rets.size - pops.size};" 91 end 92 end 93 94 def inspect 95 "#<Instruction:#{@name}>" 96 end 97 end 98 99 class InstructionsLoader 100 def initialize opts = {} 101 @insns = [] 102 @insn_map = {} 103 104 @vpath = opts[:VPATH] || File 105 @use_const = opts[:use_const] 106 @verbose = opts[:verbose] 107 @destdir = opts[:destdir] 108 109 (@vm_opts = load_vm_opts).each {|k, v| 110 @vm_opts[k] = opts[k] if opts.key?(k) 111 } 112 113 load_insns_def opts[:"insns.def"] || 'insns.def' 114 115 load_opt_operand_def opts[:"opope.def"] || 'defs/opt_operand.def' 116 load_insn_unification_def opts[:"unif.def"] || 'defs/opt_insn_unif.def' 117 make_stackcaching_insns if vm_opt?('STACK_CACHING') 118 end 119 120 attr_reader :vpath 121 attr_reader :destdir 122 123 %w[use_const verbose].each do |attr| 124 attr_reader attr 125 alias_method "#{attr}?", attr 126 remove_method attr 127 end 128 129 def [](s) 130 @insn_map[s.to_s] 131 end 132 133 def each 134 @insns.each{|insn| 135 yield insn 136 } 137 end 138 139 def size 140 @insns.size 141 end 142 143 ### 144 private 145 146 def vm_opt? name 147 @vm_opts[name] 148 end 149 150 def load_vm_opts file = nil 151 file ||= 'vm_opts.h' 152 opts = {} 153 vpath.open(file) do |f| 154 f.grep(/^\#define\s+OPT_([A-Z_]+)\s+(\d+)/) do 155 opts[$1] = !$2.to_i.zero? 156 end 157 end 158 opts 159 end 160 161 SKIP_COMMENT_PATTERN = Regexp.compile(Regexp.escape('/** ##skip')) 162 163 include Enumerable 164 165 def add_insn insn 166 @insns << insn 167 @insn_map[insn.name] = insn 168 end 169 170 def make_insn name, opes, pops, rets, comm, body, sp_inc 171 add_insn Instruction.new(name, opes, pops, rets, comm, body, [], sp_inc) 172 end 173 174 # str -> [[type, var], ...] 175 def parse_vars line 176 raise unless /\((.*?)\)/ =~ line 177 vars = $1.split(',') 178 vars.map!{|v| 179 if /\s*(\S+)\s+(\S+)\s*/ =~ v 180 type = $1 181 var = $2 182 elsif /\s*\.\.\.\s*/ =~ v 183 type = var = '...' 184 else 185 raise 186 end 187 [type, var] 188 } 189 vars 190 end 191 192 def parse_comment comm 193 c = 'others' 194 j = '' 195 e = '' 196 comm.each_line{|line| 197 case line 198 when /@c (.+)/ 199 c = $1 200 when /@e (.+)/ 201 e = $1 202 when /@e\s*$/ 203 e = '' 204 when /@j (.+)$/ 205 j = $1 206 when /@j\s*$/ 207 j = '' 208 end 209 } 210 { :c => c, 211 :e => e, 212 :j => j, 213 } 214 end 215 216 def load_insns_def file 217 body = insn = opes = pops = rets = nil 218 comment = '' 219 220 vpath.open(file) {|f| 221 f.instance_variable_set(:@line_no, 0) 222 class << f 223 def line_no 224 @line_no 225 end 226 def gets 227 @line_no += 1 228 super 229 end 230 end 231 232 while line = f.gets 233 line.chomp! 234 case line 235 236 when SKIP_COMMENT_PATTERN 237 while line = f.gets.chomp 238 if /\s+\*\/$/ =~ line 239 break 240 end 241 end 242 243 # collect instruction comment 244 when /^\/\*\*$/ 245 while line = f.gets 246 if /\s+\*\/\s*$/ =~ line 247 break 248 else 249 comment << line 250 end 251 end 252 253 # start instruction body 254 when /^DEFINE_INSN$/ 255 insn = f.gets.chomp 256 opes = parse_vars(f.gets.chomp) 257 pops = parse_vars(f.gets.chomp).reverse 258 rets_str = f.gets.chomp 259 rets = parse_vars(rets_str).reverse 260 comment = parse_comment(comment) 261 insn_in = true 262 body = '' 263 264 sp_inc = rets_str[%r"//\s*(.+)", 1] 265 266 raise unless /^\{$/ =~ f.gets.chomp 267 line_no = f.line_no 268 269 # end instruction body 270 when /^\}/ 271 if insn_in 272 body.instance_variable_set(:@line_no, line_no) 273 body.instance_variable_set(:@file, f.path) 274 insn = make_insn(insn, opes, pops, rets, comment, body, sp_inc) 275 insn_in = false 276 comment = '' 277 end 278 279 else 280 if insn_in 281 body << line + "\n" 282 end 283 end 284 end 285 } 286 end 287 288 ## opt op 289 def load_opt_operand_def file 290 vpath.foreach(file) {|line| 291 line = line.gsub(/\#.*/, '').strip 292 next if line.length == 0 293 break if /__END__/ =~ line 294 /(\S+)\s+(.+)/ =~ line 295 insn = $1 296 opts = $2 297 add_opt_operand insn, opts.split(/,/).map{|e| e.strip} 298 } if file 299 end 300 301 def label_escape label 302 label.gsub(/\(/, '_O_'). 303 gsub(/\)/, '_C_'). 304 gsub(/\*/, '_WC_') 305 end 306 307 def add_opt_operand insn_name, opts 308 insn = @insn_map[insn_name] 309 opes = insn.opes 310 311 if opes.size != opts.size 312 raise "operand size mismatcvh for #{insn.name} (opes: #{opes.size}, opts: #{opts.size})" 313 end 314 315 ninsn = insn.name + '_OP_' + opts.map{|e| label_escape(e)}.join('_') 316 nopes = [] 317 defv = [] 318 319 opts.each_with_index{|e, i| 320 if e == '*' 321 nopes << opes[i] 322 end 323 defv << [opes[i], e] 324 } 325 326 make_insn_operand_optimized(insn, ninsn, nopes, defv) 327 end 328 329 def make_insn_operand_optimized orig_insn, name, opes, defopes 330 comm = orig_insn.comm.dup 331 comm[:c] = 'optimize' 332 add_insn insn = Instruction.new( 333 name, opes, orig_insn.pops, orig_insn.rets, comm, 334 orig_insn.body, orig_insn.tvars, orig_insn.sp_inc, 335 orig_insn, defopes) 336 orig_insn.add_optimized insn 337 end 338 339 ## insn unif 340 def load_insn_unification_def file 341 vpath.foreach(file) {|line| 342 line = line.gsub(/\#.*/, '').strip 343 next if line.length == 0 344 break if /__END__/ =~ line 345 make_unified_insns line.split.map{|e| 346 raise "unknown insn: #{e}" unless @insn_map[e] 347 @insn_map[e] 348 } 349 } if file 350 end 351 352 def all_combination sets 353 ret = sets.shift.map{|e| [e]} 354 355 sets.each{|set| 356 prev = ret 357 ret = [] 358 prev.each{|ary| 359 set.each{|e| 360 eary = ary.dup 361 eary << e 362 ret << eary 363 } 364 } 365 } 366 ret 367 end 368 369 def make_unified_insns insns 370 if vm_opt?('UNIFY_ALL_COMBINATION') 371 insn_sets = insns.map{|insn| 372 [insn] + insn.optimized 373 } 374 375 all_combination(insn_sets).each{|insns_set| 376 make_unified_insn_each insns_set 377 } 378 else 379 make_unified_insn_each insns 380 end 381 end 382 383 def mk_private_val vals, i, redef 384 vals.dup.map{|v| 385 # v[0] : type 386 # v[1] : var name 387 388 v = v.dup 389 if v[0] != '...' 390 redef[v[1]] = v[0] 391 v[1] = "#{v[1]}_#{i}" 392 end 393 v 394 } 395 end 396 397 def mk_private_val2 vals, i, redef 398 vals.dup.map{|v| 399 # v[0][0] : type 400 # v[0][1] : var name 401 # v[1] : default val 402 403 pv = v.dup 404 v = pv[0] = pv[0].dup 405 if v[0] != '...' 406 redef[v[1]] = v[0] 407 v[1] = "#{v[1]}_#{i}" 408 end 409 pv 410 } 411 end 412 413 def make_unified_insn_each insns 414 names = [] 415 opes = [] 416 pops = [] 417 rets = [] 418 comm = { 419 :c => 'optimize', 420 :e => 'unified insn', 421 :j => 'unified insn', 422 } 423 body = '' 424 passed = [] 425 tvars = [] 426 defopes = [] 427 sp_inc = '' 428 429 insns.each_with_index{|insn, i| 430 names << insn.name 431 redef_vars = {} 432 433 e_opes = mk_private_val(insn.opes, i, redef_vars) 434 e_pops = mk_private_val(insn.pops, i, redef_vars) 435 e_rets = mk_private_val(insn.rets, i, redef_vars) 436 # ToDo: fix it 437 e_defs = mk_private_val2(insn.defopes, i, redef_vars) 438 439 passed_vars = [] 440 while pvar = e_pops.pop 441 rvar = rets.pop 442 if rvar 443 raise "unsupported unif insn: #{insns.inspect}" if rvar[0] == '...' 444 passed_vars << [pvar, rvar] 445 tvars << rvar 446 else 447 e_pops.push pvar 448 break 449 end 450 end 451 452 opes.concat e_opes 453 pops.concat e_pops 454 rets.concat e_rets 455 defopes.concat e_defs 456 sp_inc += "#{insn.sp_inc}" 457 458 body += "{ /* unif: #{i} */\n" + 459 passed_vars.map{|rpvars| 460 pv = rpvars[0] 461 rv = rpvars[1] 462 "#define #{pv[1]} #{rv[1]}" 463 }.join("\n") + 464 "\n" + 465 redef_vars.map{|v, type| 466 "#define #{v} #{v}_#{i}" 467 }.join("\n") + "\n" + 468 insn.body + 469 passed_vars.map{|rpvars| 470 "#undef #{rpvars[0][1]}" 471 }.join("\n") + 472 "\n" + 473 redef_vars.keys.map{|v| 474 "#undef #{v}" 475 }.join("\n") + 476 "\n}\n" 477 } 478 479 tvars_ary = [] 480 tvars.each{|tvar| 481 unless opes.any?{|var| 482 var[1] == tvar[1] 483 } || defopes.any?{|pvar| 484 pvar[0][1] == tvar[1] 485 } 486 tvars_ary << tvar 487 end 488 } 489 add_insn insn = Instruction.new("UNIFIED_" + names.join('_'), 490 opes, pops, rets.reverse, comm, body, 491 tvars_ary, sp_inc) 492 insn.defopes.replace defopes 493 insns[0].add_unif [insn, insns] 494 end 495 496 ## sc 497 SPECIAL_INSN_FOR_SC_AFTER = { 498 /\Asend/ => [:a], 499 /\Aend/ => [:a], 500 /\Ayield/ => [:a], 501 /\Aclassdef/ => [:a], 502 /\Amoduledef/ => [:a], 503 } 504 FROM_SC = [[], [:a], [:b], [:a, :b], [:b, :a]] 505 506 def make_stackcaching_insns 507 pops = rets = nil 508 509 @insns.dup.each{|insn| 510 opops = insn.pops 511 orets = insn.rets 512 oopes = insn.opes 513 ocomm = insn.comm 514 oname = insn.name 515 516 after = SPECIAL_INSN_FOR_SC_AFTER.find {|k, v| k =~ oname} 517 518 insns = [] 519 FROM_SC.each{|from| 520 name, pops, rets, pushs1, pushs2, nextsc = 521 *calc_stack(insn, from, after, opops, orets) 522 523 make_insn_sc(insn, name, oopes, pops, rets, [pushs1, pushs2], nextsc) 524 } 525 } 526 end 527 528 def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc 529 comm = orig_insn.comm.dup 530 comm[:c] = 'optimize(sc)' 531 532 scinsn = Instruction.new( 533 name, opes, pops, rets, comm, 534 orig_insn.body, orig_insn.tvars, orig_insn.sp_inc, 535 orig_insn, orig_insn.defopes, :sc, nextsc, pushs) 536 537 add_insn scinsn 538 orig_insn.add_sc scinsn 539 end 540 541 def self.complement_name st 542 "#{st[0] ? st[0] : 'x'}#{st[1] ? st[1] : 'x'}" 543 end 544 545 def add_stack_value st 546 len = st.length 547 if len == 0 548 st[0] = :a 549 [nil, :a] 550 elsif len == 1 551 if st[0] == :a 552 st[1] = :b 553 else 554 st[1] = :a 555 end 556 [nil, st[1]] 557 else 558 st[0], st[1] = st[1], st[0] 559 [st[1], st[1]] 560 end 561 end 562 563 def calc_stack insn, ofrom, oafter, opops, orets 564 from = ofrom.dup 565 pops = opops.dup 566 rets = orets.dup 567 rest_scr = ofrom.dup 568 569 pushs_before = [] 570 pushs= [] 571 572 pops.each_with_index{|e, i| 573 if e[0] == '...' 574 pushs_before = from 575 from = [] 576 end 577 r = from.pop 578 break unless r 579 pops[i] = pops[i].dup << r 580 } 581 582 if oafter 583 from = oafter 584 from.each_with_index{|r, i| 585 rets[i] = rets[i].dup << r if rets[i] 586 } 587 else 588 rets = rets.reverse 589 rets.each_with_index{|e, i| 590 break if e[0] == '...' 591 pushed, r = add_stack_value from 592 rets[i] = rets[i].dup << r 593 if pushed 594 if rest_scr.pop 595 pushs << pushed 596 end 597 598 if i - 2 >= 0 599 rets[i-2].pop 600 end 601 end 602 } 603 end 604 605 if false #|| insn.name =~ /test3/ 606 p ofrom 607 p pops 608 p rets 609 p pushs_before 610 p pushs 611 p from 612 exit 613 end 614 615 ret = ["#{insn.name}_SC_#{InstructionsLoader.complement_name(ofrom)}_#{complement_name(from)}", 616 pops, rets, pushs_before, pushs, from] 617 end 618 end 619 620 class SourceCodeGenerator 621 def initialize insns 622 @insns = insns 623 end 624 625 attr_reader :insns 626 627 def generate 628 raise "should not reach here" 629 end 630 631 def vpath 632 @insns.vpath 633 end 634 635 def verbose? 636 @insns.verbose? 637 end 638 639 def use_const? 640 @insns.use_const? 641 end 642 643 def build_string 644 @lines = [] 645 yield 646 @lines.join("\n") 647 end 648 649 EMPTY_STRING = ''.freeze 650 651 def commit str = EMPTY_STRING 652 @lines << str 653 end 654 655 def comment str 656 @lines << str if verbose? 657 end 658 659 def output_path(fn) 660 d = @insns.destdir 661 fn = File.join(d, fn) if d 662 fn 663 end 664 end 665 666 ################################################################### 667 # vm.inc 668 class VmBodyGenerator < SourceCodeGenerator 669 # vm.inc 670 def generate 671 vm_body = '' 672 @insns.each{|insn| 673 vm_body << "\n" 674 vm_body << make_insn_def(insn) 675 } 676 src = vpath.read('template/vm.inc.tmpl') 677 ERB.new(src).result(binding) 678 end 679 680 def generate_from_insnname insnname 681 make_insn_def @insns[insnname.to_s] 682 end 683 684 ####### 685 private 686 687 def make_header_prepare_stack insn 688 comment " /* prepare stack status */" 689 690 push_ba = insn.pushsc 691 raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0 692 693 n = 0 694 push_ba.each {|pushs| n += pushs.length} 695 commit " CHECK_VM_STACK_OVERFLOW(REG_CFP, #{n});" if n > 0 696 push_ba.each{|pushs| 697 pushs.each{|r| 698 commit " PUSH(SCREG(#{r}));" 699 } 700 } 701 end 702 703 def make_header_operands insn 704 comment " /* declare and get from iseq */" 705 706 vars = insn.opes 707 n = 0 708 ops = [] 709 710 vars.each_with_index{|(type, var), i| 711 if type == '...' 712 break 713 end 714 715 # skip make operands when body has no reference to this operand 716 # TODO: really needed? 717 re = /\b#{var}\b/n 718 if re =~ insn.body or re =~ insn.sp_inc or insn.rets.any?{|t, v| re =~ v} or re =~ 'ic' or re =~ 'ci' 719 ops << " #{type} #{var} = (#{type})GET_OPERAND(#{i+1});" 720 end 721 722 n += 1 723 } 724 @opn = n 725 726 # reverse or not? 727 # ops.join 728 commit ops.reverse 729 end 730 731 def make_header_default_operands insn 732 vars = insn.defopes 733 734 vars.each{|e| 735 next if e[1] == '*' 736 if use_const? 737 commit " const #{e[0][0]} #{e[0][1]} = #{e[1]};" 738 else 739 commit " #define #{e[0][1]} #{e[1]}" 740 end 741 } 742 end 743 744 def make_footer_default_operands insn 745 comment " /* declare and initialize default opes */" 746 if use_const? 747 commit 748 else 749 vars = insn.defopes 750 751 vars.each{|e| 752 next if e[1] == '*' 753 commit "#undef #{e[0][1]}" 754 } 755 end 756 end 757 758 def make_header_stack_pops insn 759 comment " /* declare and pop from stack */" 760 761 n = 0 762 pops = [] 763 vars = insn.pops 764 vars.each_with_index{|iter, i| 765 type, var, r = *iter 766 if type == '...' 767 break 768 end 769 if r 770 pops << " #{type} #{var} = SCREG(#{r});" 771 else 772 pops << " #{type} #{var} = TOPN(#{n});" 773 n += 1 774 end 775 } 776 @popn = n 777 778 # reverse or not? 779 commit pops.reverse 780 end 781 782 def make_header_temporary_vars insn 783 comment " /* declare temporary vars */" 784 785 insn.tvars.each{|var| 786 commit " #{var[0]} #{var[1]};" 787 } 788 end 789 790 def make_header_stack_val insn 791 comment "/* declare stack push val */" 792 793 vars = insn.opes + insn.pops + insn.defopes.map{|e| e[0]} 794 795 insn.rets.each{|var| 796 if vars.all?{|e| e[1] != var[1]} && var[1] != '...' 797 commit " #{var[0]} #{var[1]};" 798 end 799 } 800 end 801 802 def make_header_analysis insn 803 commit " COLLECT_USAGE_INSN(BIN(#{insn.name}));" 804 insn.opes.each_with_index{|op, i| 805 commit " COLLECT_USAGE_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});" 806 } 807 end 808 809 def make_header_pc insn 810 commit " ADD_PC(1+#{@opn});" 811 commit " PREFETCH(GET_PC());" 812 end 813 814 def make_header_popn insn 815 comment " /* management */" 816 commit " POPN(#{@popn});" if @popn > 0 817 end 818 819 def make_hader_debug insn 820 comment " /* for debug */" 821 commit " DEBUG_ENTER_INSN(\"#{insn.name}\");" 822 end 823 824 def make_header_defines insn 825 commit " #define CURRENT_INSN_#{insn.name} 1" 826 commit " #define INSN_IS_SC() #{insn.sc ? 0 : 1}" 827 commit " #define INSN_LABEL(lab) LABEL_#{insn.name}_##lab" 828 commit " #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}" 829 end 830 831 def each_footer_stack_val insn 832 insn.rets.reverse_each{|v| 833 break if v[1] == '...' 834 yield v 835 } 836 end 837 838 def make_footer_stack_val insn 839 comment " /* push stack val */" 840 841 n = 0 842 each_footer_stack_val(insn){|v| 843 n += 1 unless v[2] 844 } 845 commit " CHECK_VM_STACK_OVERFLOW(REG_CFP, #{n});" if n > 0 846 each_footer_stack_val(insn){|v| 847 if v[2] 848 commit " SCREG(#{v[2]}) = #{v[1]};" 849 else 850 commit " PUSH(#{v[1]});" 851 end 852 } 853 end 854 855 def make_footer_undefs insn 856 commit "#undef CURRENT_INSN_#{insn.name}" 857 commit "#undef INSN_IS_SC" 858 commit "#undef INSN_LABEL" 859 commit "#undef LABEL_IS_SC" 860 end 861 862 def make_header insn 863 commit "INSN_ENTRY(#{insn.name}){" 864 make_header_prepare_stack insn 865 commit "{" 866 make_header_stack_val insn 867 make_header_default_operands insn 868 make_header_operands insn 869 make_header_stack_pops insn 870 make_header_temporary_vars insn 871 # 872 make_hader_debug insn 873 make_header_pc insn 874 make_header_popn insn 875 make_header_defines insn 876 make_header_analysis insn 877 commit "{" 878 end 879 880 def make_footer insn 881 make_footer_stack_val insn 882 make_footer_default_operands insn 883 make_footer_undefs insn 884 commit " END_INSN(#{insn.name});}}}" 885 end 886 887 def make_insn_def insn 888 build_string do 889 make_header insn 890 if line = insn.body.instance_variable_get(:@line_no) 891 file = insn.body.instance_variable_get(:@file) 892 commit "#line #{line+1} \"#{file}\"" 893 commit insn.body 894 commit '#line __CURRENT_LINE__ "__CURRENT_FILE__"' 895 else 896 insn.body 897 end 898 make_footer(insn) 899 end 900 end 901 end 902 903 ################################################################### 904 # vmtc.inc 905 class VmTCIncGenerator < SourceCodeGenerator 906 def generate 907 908 insns_table = build_string do 909 @insns.each{|insn| 910 commit " LABEL_PTR(#{insn.name})," 911 } 912 end 913 914 insn_end_table = build_string do 915 @insns.each{|insn| 916 commit " ELABEL_PTR(#{insn.name}),\n" 917 } 918 end 919 920 ERB.new(vpath.read('template/vmtc.inc.tmpl')).result(binding) 921 end 922 end 923 924 ################################################################### 925 # insns_info.inc 926 class InsnsInfoIncGenerator < SourceCodeGenerator 927 def generate 928 insns_info_inc 929 end 930 931 ### 932 private 933 934 def op2typesig op 935 case op 936 when /^OFFSET/ 937 "TS_OFFSET" 938 when /^rb_num_t/ 939 "TS_NUM" 940 when /^lindex_t/ 941 "TS_LINDEX" 942 when /^VALUE/ 943 "TS_VALUE" 944 when /^ID/ 945 "TS_ID" 946 when /GENTRY/ 947 "TS_GENTRY" 948 when /^IC/ 949 "TS_IC" 950 when /^CALL_INFO/ 951 "TS_CALLINFO" 952 when /^\.\.\./ 953 "TS_VARIABLE" 954 when /^CDHASH/ 955 "TS_CDHASH" 956 when /^ISEQ/ 957 "TS_ISEQ" 958 when /rb_insn_func_t/ 959 "TS_FUNCPTR" 960 else 961 raise "unknown op type: #{op}" 962 end 963 end 964 965 TYPE_CHARS = { 966 'TS_OFFSET' => 'O', 967 'TS_NUM' => 'N', 968 'TS_LINDEX' => 'L', 969 'TS_VALUE' => 'V', 970 'TS_ID' => 'I', 971 'TS_GENTRY' => 'G', 972 'TS_IC' => 'K', 973 'TS_CALLINFO' => 'C', 974 'TS_CDHASH' => 'H', 975 'TS_ISEQ' => 'S', 976 'TS_VARIABLE' => '.', 977 'TS_FUNCPTR' => 'F', 978 } 979 980 # insns_info.inc 981 def insns_info_inc 982 # insn_type_chars 983 insn_type_chars = TYPE_CHARS.map{|t, c| 984 "#define #{t} '#{c}'" 985 }.join("\n") 986 987 # insn_names 988 insn_names = '' 989 @insns.each{|insn| 990 insn_names << " \"#{insn.name}\",\n" 991 } 992 993 # operands info 994 operands_info = '' 995 operands_num_info = '' 996 997 @insns.each{|insn| 998 opes = insn.opes 999 operands_info << ' ' 1000 ot = opes.map{|type, var| 1001 TYPE_CHARS.fetch(op2typesig(type)) 1002 } 1003 operands_info << "\"#{ot.join}\"" << ", \n" 1004 1005 num = opes.size + 1 1006 operands_num_info << " #{num},\n" 1007 } 1008 1009 # stack num 1010 stack_num_info = '' 1011 @insns.each{|insn| 1012 num = insn.rets.size 1013 stack_num_info << " #{num},\n" 1014 } 1015 1016 # stack increase 1017 stack_increase = '' 1018 @insns.each{|insn| 1019 stack_increase << <<-EOS 1020 case BIN(#{insn.name}):{ 1021 #{insn.sp_increase_c_expr} 1022 } 1023 EOS 1024 } 1025 ERB.new(vpath.read('template/insns_info.inc.tmpl')).result(binding) 1026 end 1027 end 1028 1029 ################################################################### 1030 # insns.inc 1031 class InsnsIncGenerator < SourceCodeGenerator 1032 def generate 1033 i=0 1034 insns = build_string do 1035 @insns.each{|insn| 1036 commit " %-30s = %d,\n" % ["BIN(#{insn.name})", i] 1037 i+=1 1038 } 1039 end 1040 1041 ERB.new(vpath.read('template/insns.inc.tmpl')).result(binding) 1042 end 1043 end 1044 1045 ################################################################### 1046 # minsns.inc 1047 class MInsnsIncGenerator < SourceCodeGenerator 1048 def generate 1049 i=0 1050 defs = build_string do 1051 @insns.each{|insn| 1052 commit " rb_define_const(mYarvInsns, %-30s, INT2FIX(%d));\n" % 1053 ["\"I#{insn.name}\"", i] 1054 i+=1 1055 } 1056 end 1057 ERB.new(vpath.read('template/minsns.inc.tmpl')).result(binding) 1058 end 1059 end 1060 1061 ################################################################### 1062 # optinsn.inc 1063 class OptInsnIncGenerator < SourceCodeGenerator 1064 def generate 1065 optinsn_inc 1066 end 1067 1068 ### 1069 private 1070 1071 def val_as_type op 1072 type = op[0][0] 1073 val = op[1] 1074 1075 case type 1076 when /^long/, /^rb_num_t/, /^lindex_t/ 1077 "INT2FIX(#{val})" 1078 when /^VALUE/ 1079 val 1080 when /^ID/ 1081 "INT2FIX(#{val})" 1082 when /^ISEQ/, /^rb_insn_func_t/ 1083 val 1084 when /GENTRY/ 1085 raise 1086 when /^\.\.\./ 1087 raise 1088 else 1089 raise "type: #{type}" 1090 end 1091 end 1092 1093 # optinsn.inc 1094 def optinsn_inc 1095 rule = '' 1096 opt_insns_map = Hash.new{|h, k| h[k] = []} 1097 1098 @insns.each{|insn| 1099 next if insn.defopes.size == 0 1100 next if insn.type == :sc 1101 next if /^UNIFIED/ =~ insn.name.to_s 1102 1103 originsn = insn.orig 1104 opt_insns_map[originsn] << insn 1105 } 1106 1107 rule = build_string do 1108 opt_insns_map.each{|originsn, optinsns| 1109 commit "case BIN(#{originsn.name}):" 1110 1111 optinsns.sort_by{|opti| 1112 opti.defopes.find_all{|e| e[1] == '*'}.size 1113 }.each{|opti| 1114 commit " if(" 1115 i = 0 1116 commit " " + opti.defopes.map{|opinfo| 1117 i += 1 1118 next if opinfo[1] == '*' 1119 "insnobj->operands[#{i-1}] == #{val_as_type(opinfo)}" 1120 }.compact.join('&& ') 1121 commit " ){" 1122 idx = 0 1123 n = 0 1124 opti.defopes.each{|opinfo| 1125 if opinfo[1] == '*' 1126 if idx != n 1127 commit " insnobj->operands[#{idx}] = insnobj->operands[#{n}];" 1128 end 1129 idx += 1 1130 else 1131 # skip 1132 end 1133 n += 1 1134 } 1135 commit " insnobj->insn_id = BIN(#{opti.name});" 1136 commit " insnobj->operand_size = #{idx};" 1137 commit " break;\n }\n" 1138 } 1139 commit " break;"; 1140 } 1141 end 1142 1143 ERB.new(vpath.read('template/optinsn.inc.tmpl')).result(binding) 1144 end 1145 end 1146 1147 ################################################################### 1148 # optunifs.inc 1149 class OptUnifsIncGenerator < SourceCodeGenerator 1150 def generate 1151 unif_insns_each = '' 1152 unif_insns = '' 1153 unif_insns_data = [] 1154 1155 insns = @insns.find_all{|insn| !insn.is_sc} 1156 insns.each{|insn| 1157 size = insn.unifs.size 1158 if size > 0 1159 insn.unifs.sort_by{|unif| -unif[1].size}.each_with_index{|unif, i| 1160 1161 uni_insn, uni_insns = *unif 1162 uni_insns = uni_insns[1..-1] 1163 unif_insns_each << "static const int UNIFIED_#{insn.name}_#{i}[] = {" + 1164 " BIN(#{uni_insn.name}), #{uni_insns.size + 2}, \n " + 1165 uni_insns.map{|e| "BIN(#{e.name})"}.join(", ") + "};\n" 1166 } 1167 else 1168 1169 end 1170 if size > 0 1171 unif_insns << "static const int *const UNIFIED_#{insn.name}[] = {(int *)#{size+1}, \n" 1172 unif_insns << (0...size).map{|e| " UNIFIED_#{insn.name}_#{e}"}.join(",\n") + "};\n" 1173 unif_insns_data << " UNIFIED_#{insn.name}" 1174 else 1175 unif_insns_data << " 0" 1176 end 1177 } 1178 unif_insns_data = "static const int *const *const unified_insns_data[] = {\n" + 1179 unif_insns_data.join(",\n") + "};\n" 1180 ERB.new(vpath.read('template/optunifs.inc.tmpl')).result(binding) 1181 end 1182 end 1183 1184 ################################################################### 1185 # opt_sc.inc 1186 class OptSCIncGenerator < SourceCodeGenerator 1187 def generate 1188 sc_insn_info = [] 1189 @insns.each{|insn| 1190 insns = insn.sc 1191 if insns.size > 0 1192 insns = ['SC_ERROR'] + insns.map{|e| " BIN(#{e.name})"} 1193 else 1194 insns = Array.new(6){'SC_ERROR'} 1195 end 1196 sc_insn_info << " {\n#{insns.join(",\n")}}" 1197 } 1198 sc_insn_info = sc_insn_info.join(",\n") 1199 1200 sc_insn_next = @insns.map{|insn| 1201 " SCS_#{InstructionsLoader.complement_name(insn.nextsc).upcase}" + 1202 (verbose? ? " /* #{insn.name} */" : '') 1203 }.join(",\n") 1204 ERB.new(vpath.read('template/opt_sc.inc.tmpl')).result(binding) 1205 end 1206 end 1207 1208 ################################################################### 1209 # yasmdata.rb 1210 class YASMDataRbGenerator < SourceCodeGenerator 1211 def generate 1212 insn_id2no = '' 1213 @insns.each_with_index{|insn, i| 1214 insn_id2no << " :#{insn.name} => #{i},\n" 1215 } 1216 ERB.new(vpath.read('template/yasmdata.rb.tmpl')).result(binding) 1217 end 1218 end 1219 1220 ################################################################### 1221 # yarvarch.* 1222 class YARVDocGenerator < SourceCodeGenerator 1223 def generate 1224 1225 end 1226 1227 def desc lang 1228 d = '' 1229 i = 0 1230 cat = nil 1231 @insns.each{|insn| 1232 seq = insn.opes.map{|t,v| v}.join(' ') 1233 before = insn.pops.reverse.map{|t,v| v}.join(' ') 1234 after = insn.rets.reverse.map{|t,v| v}.join(' ') 1235 1236 if cat != insn.comm[:c] 1237 d << "** #{insn.comm[:c]}\n\n" 1238 cat = insn.comm[:c] 1239 end 1240 1241 d << "*** #{insn.name}\n" 1242 d << "\n" 1243 d << insn.comm[lang] + "\n\n" 1244 d << ":instruction sequence: 0x%02x #{seq}\n" % i 1245 d << ":stack: #{before} => #{after}\n\n" 1246 i+=1 1247 } 1248 d 1249 end 1250 1251 def desc_ja 1252 d = desc :j 1253 ERB.new(vpath.read('template/yarvarch.ja')).result(binding) 1254 end 1255 1256 def desc_en 1257 d = desc :e 1258 ERB.new(vpath.read('template/yarvarch.en')).result(binding) 1259 end 1260 end 1261 1262 class SourceCodeGenerator 1263 Files = { # codes 1264 'vm.inc' => VmBodyGenerator, 1265 'vmtc.inc' => VmTCIncGenerator, 1266 'insns.inc' => InsnsIncGenerator, 1267 'insns_info.inc' => InsnsInfoIncGenerator, 1268 # 'minsns.inc' => MInsnsIncGenerator, 1269 'optinsn.inc' => OptInsnIncGenerator, 1270 'optunifs.inc' => OptUnifsIncGenerator, 1271 'opt_sc.inc' => OptSCIncGenerator, 1272 'yasmdata.rb' => YASMDataRbGenerator, 1273 } 1274 1275 def generate args = [] 1276 args = Files.keys if args.empty? 1277 args.each{|fn| 1278 s = Files[fn].new(@insns).generate 1279 open(output_path(fn), 'w') {|f| f.puts(s)} 1280 } 1281 end 1282 1283 def self.def_options(opt) 1284 opts = { 1285 :"insns.def" => 'insns.def', 1286 :"opope.def" => 'defs/opt_operand.def', 1287 :"unif.def" => 'defs/opt_insn_unif.def', 1288 } 1289 1290 opt.on("-Dname", /\AOPT_(\w+)\z/, "enable VM option") {|s, v| 1291 opts[v] = true 1292 } 1293 opt.on("--enable=name[,name...]", Array, 1294 "enable VM options (without OPT_ prefix)") {|*a| 1295 a.each {|v| opts[v] = true} 1296 } 1297 opt.on("-Uname", /\AOPT_(\w+)\z/, "disable VM option") {|s, v| 1298 opts[v] = false 1299 } 1300 opt.on("--disable=name[,name...]", Array, 1301 "disable VM options (without OPT_ prefix)") {|*a| 1302 a.each {|v| opts[v] = false} 1303 } 1304 opt.on("-i", "--insnsdef=FILE", "--instructions-def", 1305 "instructions definition file") {|n| 1306 opts[:insns_def] = n 1307 } 1308 opt.on("-o", "--opt-operanddef=FILE", "--opt-operand-def", 1309 "vm option: operand definition file") {|n| 1310 opts[:opope_def] = n 1311 } 1312 opt.on("-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def", 1313 "vm option: instruction unification file") {|n| 1314 opts[:unif_def] = n 1315 } 1316 opt.on("-C", "--[no-]use-const", 1317 "use consts for default operands instead of macros") {|v| 1318 opts[:use_const] = v 1319 } 1320 opt.on("-d", "--destdir", "--output-directory=DIR", 1321 "make output file underneath DIR") {|v| 1322 opts[:destdir] = v 1323 } 1324 opt.on("-V", "--[no-]verbose") {|v| 1325 opts[:verbose] = v 1326 } 1327 1328 vpath = VPath.new 1329 vpath.def_options(opt) 1330 1331 proc { 1332 opts[:VPATH] = vpath 1333 build opts 1334 } 1335 end 1336 1337 def self.build opts, vpath = ['./'] 1338 opts[:VPATH] ||= VPath.new(*vpath) 1339 self.new InstructionsLoader.new(opts) 1340 end 1341 end 1342end 1343 1344