まとめ
Cxx.instance(__current_compiler__).shadow
(clang::Module*
型) に IR が入っているので、shadow->dump()
として探すか、 shadow->getFunction("mangleされた関数名")->dump()
とする。cxx"..."
を使う場合はともかく、icxx"..."
を使う場合はmangleされた関数名を探すのがやっかいだったので、以下に手順をまとめます。
バージョン情報
Julia
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
下準備
パッケージを読み込む
julia> @time using Cxx
9.823152 seconds (1.68 M allocations: 74.998 MB, 2.13% gc time)
llvmの各種ヘッダーをincludeする
julia> @time include(Pkg.dir("Cxx","test","llvmincludes.jl"))
1.911098 seconds (32.05 k allocations: 1.390 MB)
打つのが面倒なのでエイリアスをつくる
julia> C = Cxx.instance(Cxx.__current_compiler__);
準備は以上です
1. cxx"..."
で定義した関数のIRをダンプする
例1.
julia> cxx"""
extern "C" {
int f() { return 1; }
}
"""
例2.
julia> cxx"""
double ff(int x) { return static_cast<double>(x); }
"""
extern "C"
でname manglingを避けた場合
簡単です。
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します
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できない場合は(多くの場合はそうだと思いますが)、
julia> icxx"$(C.shadow)->dump();"
として、大量に吐かれるIRの中から、目当ての物を探します。きっと後ろの方にあるので、探すのは難しくないはずです。
もう一つ、mangleされた名前を見つける方法もあるのですが、それは次で述べます。
2. icxx"..."
で定義した関数のIRをダンプする
この場合、cxx"..."
の場合と比べると面倒です。なぜなら、icxx"..."
が実行されるときに、動的にCxx.jlが適当な関数名で関数を生成するからです。
例えば、以下の様なJulia関数を定義したとします。
julia> f() = icxx"f();" + 1
f (generic function with 1 method)
先ほどのC++関数 f
の定義より、以下の様な結果になります。
julia> @assert f() == 2
モジュールをダンプして探すのもいいのですが、ここでは Gallium(正確にはASTInterpreter)を使った方法を一つ紹介します。
Gallium でcxxstr_impl
にステップイン(generated functionなので、sg
コマンドを使います)して FunctionDecl
を探して、mangleされた名前を求める
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*
型にキャストして、
julia> FD = pcpp"clang::FunctionDecl"(Ptr{Void}(0x00000003214dfe38))
(class clang::FunctionDecl *) @0x00000003214dfe38
mangleされた名前を求めれば、
julia> fname = Cxx.getMangledFunctionName(C, FD)
"_ZN6__icxx7icxx106Ev"
あとは cxx"..."
の場合と同様です
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 についてまだまだ無知なので、これがいい方法なのかは正直よくわかっていないのですが、試行錯誤した結果こうなりました。
おしまい
-
この記事の範囲では、v0.5なら https://github.com/Keno/Cxx.jl でもいいはずですが、RTTIを有効にして使いたかったのと、v0.6 では llvmのヘッダーのincludeでこけてしまう部分があったので、フォークしています ↩