yarv2llvmを書くことにしました

レンラダが不完全だけど動き出して気づいたのは、これ手でllvmに直していたら苦労するなってことです。しかも、レンラダのアルゴリズムを替えると苦労はみんな水の泡。
そこで、YARVの出力をllvmにするプログラムを作ろうと思い立ちました。Syoyoさんのpy2llvm(http://code.google.com/p/py2llvm/)の劣化コピーみたいな感じです。

方針はこんな感じです

  1. RubyのプログラムをRubyVM::InstructionSequence.compileを使ってコンパイルし、YARVバイトコードを得る
  2. バイトコードを一部トレースしてllvmを得る。llvmを生成する処はllvmrubyを使用する
  3. 型推論はサボりまくり、プログラマには必要なだけ型指定を要求する

llvmrubyのruby_vm.rbと違い、YARVをスタックマシンとしてコンパイルするのではなく、トレースしてレジスタマシン(SSAマシン)としてコンパイルします。

とりあえず、こんなテストをしてみました。getlocal, setlocal, 四則演算をトラバースしてもとのRubyの式を再生するというものです。instruction.rbは長くなるので、githubに置きました(http://github.com/miura1729/yarv2llvm/tree/master/lib/instruction.rb)

require 'instruction'

include VMLib
is = RubyVM::InstructionSequence.compile(File.read(ARGV[0])).to_a
iseq = InstSeqTree.new(nil, is)

stack = []
iseq.traverse_code([nil, nil, nil]) do |code, info, header|
  code.lblock_list.each do |ln|
    p ln
    local = ["", :self] + code.header['locals'].reverse
    p local
    code.lblock[ln].each do |ins|
      nm = ins[0]
      p1 = ins[1]
      case nm 
      when :getlocal
        stack.push local[p1]
      when :setlocal
        src = stack.pop
        p "#{local[p1]} = #{src}"
      when :opt_plus
        s1 = stack.pop
        s2 = stack.pop
        stack.push "#{s1} + #{s2}"
      when :opt_minus
        s1 = stack.pop
        s2 = stack.pop
        stack.push "#{s1} - #{s2}"
      when :opt_mult
        s1 = stack.pop
        s2 = stack.pop
        stack.push "#{s1} * #{s2}"
      when :opt_div
        s1 = stack.pop
        s2 = stack.pop
        stack.push "#{s1} / #{s2}"
      end
    end
  end
end

こんなプログラムを食わせると、

class Foo
def foo(sx, sy, sz)
  vx = sx
  vy = sy
  vz = sz
  vs = Math.sqrt(vx * vx + vy * vy + vz * vz)
  vx = vx / vs
  vy = vy / vs
  vz = vz / vs
end
end

こんな出力になります。

nil
["", :self]
nil
["", :self]
nil
["", :self, :vs, :vz, :vy, :vx, :sz, :sy, :sx]
"vx = sx"
"vy = sy"
"vz = sz"
:label_22
["", :self, :vs, :vz, :vy, :vx, :sz, :sy, :sx]
:label_29
["", :self, :vs, :vz, :vy, :vx, :sz, :sy, :sx]
"vs = vz * vz + vy * vy + vx * vx"
"vx = vs / vx"
"vy = vs / vy"
"vz = vs / vz"