Rubyによる正規表現コンパイラ(その2)
ようやくできた! 苦しかった・・・。
昨日のruby66版の後ろにくっつけるか別ファイルにしてrequireするように書き換えてください。
次はベンチマークを行います。
llvmrubyではまったところ
- 関数の引数で渡した文字列はVALUE型なのでそのままでは、扱えません。rb_string_value_ptrを呼んでchar *へのポインタの中身を得る必要があります。ただし、rb_string_value_ptrの引数はVALUE *です。そのため、いったんallocで領域を確保して、そこに引数で渡った値を入れて、allocした領域をrb_string_value_ptrの引数にする必要があります。
- ブロックは互いに独立しています。アセンブラのラベルみたいな感覚でブロックがつながっていると思っているとassertエラーを食らいます。br命令で明示的にブロックにジャンプするようにする必要がある場合があります。
- 型が合わないとassertエラーが起きます。型チェックはかなり厳格です。Rubyのプログラムの気分でコーディングしていると泣きます。
追記
Rubyの仕様に合せて、マッチに失敗したときはnilを返すように変更しました。マッチが成功したときはRubyの仕様と異なります。
require 'llvm' class MatcherLLVM include LLVM include RubyHelpers def initialize @module = LLVM::Module.new('regexp') ExecutionEngine.get(@module) @func = @module.get_or_insert_function("regmatch", Type.function(VALUE, [VALUE])) eb = @func.create_block @main_block = eb.builder @rb_string_value_ptr = @module.external_function('rb_string_value_ptr', Type.function(P_CHAR, [P_VALUE])) @strlen = @module.external_function('strlen', Type.function(INT, [P_CHAR])) end def match(str) ExecutionEngine.run_function(@func, str) end def compile(stm) rstr = @func.arguments[0] blocks = {} rstrp = @main_block.alloca(VALUE, 4) @main_block.store(rstr, rstrp) str = @main_block.call(@rb_string_value_ptr, rstrp) len = @main_block.call(@strlen, str) idxp = @main_block.alloca(INT, 4) @main_block.store(-1.llvm(INT), idxp) starray = stm.state starray.each_with_index do |elest, stn| blocks[stn] = @func.create_block end @main_block.br(blocks[0]) starray.each_with_index do |elest, stn| @main_block.set_insert_point(blocks[stn]) if stn == starray.size - 1 then idx = @main_block.load(idxp) @main_block.return(2.llvm) # true else idx = @main_block.load(idxp) idx = @main_block.add(idx, 1.llvm(INT)) @main_block.store(idx, idxp) thenb = @func.create_block elseb = @func.create_block cmp = @main_block.icmp_sge(idx, len) @main_block.cond_br(cmp, thenb, elseb) @main_block.set_insert_point(thenb) @main_block.return(4.llvm) # nil @main_block.set_insert_point(elseb) defstat = elest[256] elest.each do |key, val| if val != defstat then idx = @main_block.load(idxp) chp = @main_block.gep(str, idx) ch = @main_block.load(chp) cmp = @main_block.icmp_eq(ch, key.llvm(CHAR)) elseb = @func.create_block @main_block.cond_br(cmp, blocks[val], elseb) @main_block.set_insert_point(elseb) end end if defstat then @main_block.br(blocks[defstat]) else @main_block.return(4.llvm) # nil end end end @func end end stm = reexp_comp(".*cc\\*dd*a") mllvm = MatcherLLVM.new mllvm.compile(stm) p mllvm.match("fffcccddddafff") p mllvm.match("nnncccaaaa") p mllvm.match("kkkdcc*dddannnn")