Cxx.jlで生成したC++関数のIRをダンプする方法

## まとめ

`Cxx.instance(__current_compiler__).shadow` (`clang::Module*`型) に IR が入っているので、`shadow->dump()` として探すか、 `shadow->getFunction("mangleされた関数名")->dump()` とする。`cxx"..."` を使う場合はともかく、`icxx"..."` を使う場合はmangleされた関数名を探すのがやっかいだったので、以下に手順をまとめます。


## バージョン情報

### Julia

```jlcon
julia> versioninfo()
Julia Version 0.6.0-dev.446
Commit cd94c99* (2016-09-01 13:15 UTC)
Platform Info:
  System: Darwin (x86_64-apple-darwin15.5.0)
  CPU: Intel(R) Core(TM) i5-4258U CPU @ 2.40GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell)
  LAPACK: libopenblas64_
  LIBM: libopenlibm
  LLVM: libLLVM-3.7.1 (ORCJIT, haswell)
```

### Cxx.jl

[r9y9/Cxx.jl@0f924cd](https://github.com/r9y9/Cxx.jl/commit/0f924cdf5792d9174957c075cdf4a3a82226f9b7)[^1]

[^1]: この記事の範囲では、v0.5なら https://github.com/Keno/Cxx.jl でもいいはずですが、RTTIを有効にして使いたかったのと、v0.6 では llvmのヘッダーのincludeでこけてしまう部分があったので、フォークしています


## 下準備

パッケージを読み込む

```jlcon
julia> @time using Cxx
  9.823152 seconds (1.68 M allocations: 74.998 MB, 2.13% gc time)
```

llvmの各種ヘッダーをincludeする

```jlcon
julia> @time include(Pkg.dir("Cxx","test","llvmincludes.jl"))
  1.911098 seconds (32.05 k allocations: 1.390 MB)
```

打つのが面倒なのでエイリアスをつくる

```jlcon
julia> C = Cxx.instance(Cxx.__current_compiler__);
```

準備は以上です

## 1. `cxx"..."` で定義した関数のIRをダンプする

例1. 

```jlcon
julia> cxx"""
       extern "C" {
       int f() { return 1; }
       }
       """
```

例2. 

```jlcon
julia> cxx"""
       double ff(int x) { return static_cast<double>(x); }
       """
```

### `extern "C"`でname manglingを避けた場合

簡単です。

```jlcon
julia> llvmf = icxx"""$(C.shadow)->getFunction($(pointer("f")));"""; llvmf != C_NULL && icxx"$(llvmf)->dump();"

; Function Attrs: nounwind
define i32 @f() #0 {
entry:
  ret i32 1
}
```

でOKです

### `extern "C"` しない場合

manglingされた名前を調べる必要があります。簡単な関数名の場合は、何となく推測できるので、自分の頭でmanglingします

```jlcon
julia> llvmf = icxx"""$(C.shadow)->getFunction($(pointer("_Z2ffi")));"""; llvmf != C_NULL && icxx"$(llvmf)->dump();"

; Function Attrs: nounwind
define double @_Z2ffi(i32 %x) #0 {
entry:
  %x.addr = alloca i32, align 4
  store i32 %x, i32* %x.addr, align 4
  %0 = load i32, i32* %x.addr, align 4
  %conv = sitofp i32 %0 to double
  ret double %conv
}
```

もし頭のなかでmanglingできない場合は（多くの場合はそうだと思いますが）、

```jlcon
julia> icxx"$(C.shadow)->dump();"
```

として、大量に吐かれるIRの中から、目当ての物を探します。きっと後ろの方にあるので、探すのは難しくないはずです。

もう一つ、mangleされた名前を見つける方法もあるのですが、それは次で述べます。


## 2. `icxx"..."` で定義した関数のIRをダンプする

この場合、`cxx"..."` の場合と比べると面倒です。なぜなら、`icxx"..."` が実行されるときに、動的にCxx.jlが適当な関数名で関数を生成するからです。

例えば、以下の様なJulia関数を定義したとします。

```jlcon
julia> f() = icxx"f();" + 1
f (generic function with 1 method)
```

先ほどのC++関数 `f` の定義より、以下の様な結果になります。

```jlcon
julia> @assert f() == 2
```

モジュールをダンプして探すのもいいのですが、ここでは [Gallium](https://github.com/Keno/Gallium.jl)（正確には[ASTInterpreter](https://github.com/Keno/ASTInterpreter.jl)）を使った方法を一つ紹介します。

### Gallium で`cxxstr_impl`にステップイン（generated functionなので、`sg` コマンドを使います）して `FunctionDecl` を探して、mangleされた名前を求める

```jlcon
ulia> @enter f()
In REPL[16]:1
1 f() = icxx"f();" + 1

About to run: (Core.apply_type)(Cxx.SourceBuf{id},33)
1|debug > sg
In /Users/ryuyamamoto/.julia/v0.6/Cxx/src/cxxstr.jl:203
202
203   immutable SourceBuf{id}; end
204   sourceid{id}(::Type{SourceBuf{id}}) = id
205

About to run: (Core.apply_type)(Cxx.SourceBuf{id},33)
1|debug >
In /Users/ryuyamamoto/.julia/v0.6/Cxx/src/cxxstr.jl:203
202
203   immutable SourceBuf{id}; end
204   sourceid{id}(::Type{SourceBuf{id}}) = id
205

About to run: return Cxx.SourceBuf{33}()
1|debug >
In REPL[16]:1
1 f() = icxx"f();" + 1

About to run: (Cxx.cxxstr_impl)(Cxx.CxxInstance{1}(),Cxx.SourceBuf{33}())
1|debug >
In /Users/ryuyamamoto/.julia/v0.6/Cxx/src/cxxstr.jl:343
342   @generated function cxxstr_impl(CT, sourcebuf, args...)
343       C = instance(CT)
344       id = sourceid(sourcebuf)
345       buf, filename, line, col = sourcebuffers[id]

About to run: (Cxx.instance)(Cxx.CxxInstance{1})
1|debug > n
In /Users/ryuyamamoto/.julia/v0.6/Cxx/src/cxxstr.jl:343
343       C = instance(CT)
344       id = sourceid(sourcebuf)
345       buf, filename, line, col = sourcebuffers[id]
346

About to run: (Cxx.sourceid)(Cxx.SourceBuf{33})
1|debug >
In /Users/ryuyamamoto/.julia/v0.6/Cxx/src/cxxstr.jl:343
344       id = sourceid(sourcebuf)
345       buf, filename, line, col = sourcebuffers[id]
346
347       FD, llvmargs, argidxs, symargs = CreateFunctionWithBody(C,buf, args...; filename = filename, line = line, col = col)

About to run: (getindex)(<suppressed 74 bytes of output>,33)
1|debug >
In /Users/ryuyamamoto/.julia/v0.6/Cxx/src/cxxstr.jl:343
346
347       FD, llvmargs, argidxs, symargs = CreateFunctionWithBody(C,buf, args...; filename = filename, line = line, col = col)
348       EmitTopLevelDecl(C,FD)
349

About to run: (Core.kwfunc)(Cxx.CreateFunctionWithBody)
1|debug >
In /Users/ryuyamamoto/.julia/v0.6/Cxx/src/cxxstr.jl:343
347       FD, llvmargs, argidxs, symargs = CreateFunctionWithBody(C,buf, args...; filename = filename, line = line, col = col)
348       EmitTopLevelDecl(C,FD)
349
350       for T in args

About to run: (Cxx.EmitTopLevelDecl)(<suppressed 550 bytes of output>,(class clang::Decl *) @0x00000003214dfe38
)
1|julia > FD |> dump
FunctionDecl 0x3214dfe38 <line:1:1, line:3:1> line:1:1 icxx106 'int (void)'
`-CompoundStmt 0x3214dffa0 <col:1, line:3:1>
  `-ReturnStmt 0x3214e0018 <line:1:1, line:2:3>
    `-CallExpr 0x3214dff78 <col:1, col:3> 'int'
      `-ImplicitCastExpr 0x3214dff60 <col:1> 'int (*)(void)' <FunctionToPointerDecay>
        `-DeclRefExpr 0x3214dff38 <col:1> 'int (void)' lvalue Function 0x31d0de120 'f' 'int (void)'
nothing
```

この `FD` というのが `FunctionDecl` です。このポインタを覚えておいて、
`clang::FunctionDecl*` 型にキャストして、

```jlcon
julia> FD = pcpp"clang::FunctionDecl"(Ptr{Void}(0x00000003214dfe38))
(class clang::FunctionDecl *) @0x00000003214dfe38
```

mangleされた名前を求めれば、

```
julia> fname = Cxx.getMangledFunctionName(C, FD)
"_ZN6__icxx7icxx106Ev"
```

あとは `cxx"..."` の場合と同様です

```jlcon
julia> llvmf = icxx"""$(C.shadow)->getFunction($(pointer(fname)));"""; llvmf != C_NULL && icxx"$(llvmf)->dump();"

; Function Attrs: nounwind
define i32 @_ZN6__icxx7icxx106Ev() #0 {
entry:
  %call = call i32 @f()
  ret i32 %call
}
```

以上です。

長かった。llvm, clang についてまだまだ無知なので、これがいい方法なのかは正直よくわかっていないのですが、試行錯誤した結果こうなりました。

おしまい