LoginSignup
1
1

More than 5 years have passed since last update.

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

Posted at

まとめ

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

r9y9/Cxx.jl@0f924cd1

下準備

パッケージを読み込む

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 についてまだまだ無知なので、これがいい方法なのかは正直よくわかっていないのですが、試行錯誤した結果こうなりました。

おしまい


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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1