yarv2llvmでの生成したコードの場当たり的な最適化

前回の日記の続きですが、こんな込み入ったことどー考えても私には口頭で説明できないのでここに書いてしまいます。あんまり効果なかったし...。

前回までのあらすじ

yarv2llvmでどう考えても無駄なコードが生成される。でも、コードが無駄かどうかは最適化後でないと判らないので困った。

本題

llvmrubyでは、LLVM::Module#inspectを使うとLLVMのbitコードが人間に読める(???)文字列の形で得ることができます。これは、最適化後でも可能です。そこで、このbitコードで問題になるパターンを正規表現で見つけ出して問題にならないよう様に書き換えてしまおうと考えました。

問題になるパターンはこんな感じです。

	%80 = load i32* %50		; <i32> [#uses=1]
	%81 = inttoptr i32 %80 to { { i32, i32 }, double }*		; <{ { i32, i32 }, double }*> [#uses=1]
	%82 = getelementptr { { i32, i32 }, double }* %81, i64 0, i32 1		; <double*> [#uses=1]
	%83 = load double* %82		; <double> [#uses=1]
	%84 = call i32 @rb_float_new(double %83)		; <i32> [#uses=1]

前回も説明したとおり、UNBOX化したdoubleデータをまた即座にBOX化するなということです。そこで、doubleをUNBOX化するパターンを見つけ出して、その結果をrb_float_newに渡しているときは、rb_float_newする代わりにUNBOX化する前のデータを返すように書き換えてしまうようにしました。

UNBOX化のパターンです。

    V2D_PATTERN = %r|
      \n
      \s+(\S+)\s=\sload\s\i32\*\s(\S+).*\n
      \s+(\S+)\s=\sinttoptr\s\S+\s\1\sto\s{\s{\si32,\si32\s},\sdouble\s}\*.*\n
      \s+(\S+)\s=\sgetelementptr\s{\s{\si32,\si32\s},\sdouble\s}\*\s\3,\si64\s0,\si32\s1.*\n
      \s+(\S+)\s=\sload\sdouble\*\s\4.*\n
    |x

ややこしいですが、\s,\nを空白に置き換えると少し読みやすくなります。

    V2D_PATTERN = %r|
       +(\S+) = load i32\* (\S+).*
       +(\S+) = inttoptr \S+ \1 to { { i32, i32 }, double }\*.*\n
       +(\S+) = getelementptr { { i32, i32 }, double }\* \3, i64\s0, i32 1.*
       +(\S+) = load double\* \4.*
    |x

レジスタの部分を空白を含まない任意の文字列としてパターンマッチングします。マッチングすると、使われたレジスタ名が記録されます。後は、UNBOX化されたdoubleのデータ(最後に出てくるレジスタ)がrb_float_newに渡されているパターンを探し出して、UNBOX化する前のデータに置き換えてしまいます。

こんな感じのソースです。

    def optimize_func(funcstr)
      res = funcstr.dup
      funcstr.scan(V2D_PATTERN) do |n|
        d2v_pattern = /[ \t]+(\S+)\s=\scall\si32\s@rb_float_new\(double #{n[4]}\)/
        if d2v_pattern =~ funcstr then
          res.gsub!(d2v_pattern, "\t#{$1} = load i32* #{n[1]}")
        end
      end
      res
    end

こうすることで、余分なrb_float_newを消すことができます。

でも、実際にAOベンチで比べてみると数秒速くなったのですがあまり効果は無いようです。適応できる場所の実行頻度が少なかったんだろうなーと思います。
でも、今後この命令パターンをこうできれば速いのにーって思ったときは最後の砦になるかなと思います。思いっきり保守性を犠牲にしてしまいますが・・・。