差分表示


*&date(Y-n-j[lL],2010/3/20); LLVMで Cに変換してみる

LLVM (llc) には Cソースコードを生成する機能があり、
C++ソースをCソースにすることができる模様。
//ろくに C++のない環境にもっていけるのでは、とちょっと期待したりもする。(必要があるわけでないけれど、かって欲しかったコトもあり)

//ただLLVM の場合、 
ただし C++ から C への直接変換するのではなく、
一旦.ll/.bc(llvm用アセンブラソース,ビットコード)にしたものを、
llc.exe で(ターゲットCPU用アセンブラソースのかわりに)
Cソースへ変換するというもの。
//(実際生成されたソースはアセンブラ的)

普通のアセンブラソースをCに変換した場合
オーバーヘッドは当然あり、
全体として問題になることはそうないかもだが
(昔アセンブラソースからCに変換しての移植作業を何本かやったときの印象)、
何割(何倍)か遅くなっていてもおかしくない。

が llvmの .ll/.bc は、
普通のアセンブラソース/オブジェファイルでなく、
それより情報の多い中間コードなわけだし、
llvm-gcc & llc でオプティマイズしているため、
Cソースになっても元よりも速くなる可能性もありそう。
//(実験物なのか実用物なのか...)

ということで、ちょっと、試してみた。
[[3/12にしたテスト:雑記/2010-03-12]]の1つ目。
C++でなく Cソースだけど。
(32bit色640x480→1920x1080拡大の50回生成の平均時間を求める奴.
実行環境は前回同様)

llvm-gcc で
 llvm-gcc -S -o tmp.ll -emit-llvm -O2 -DNDEBUG test.c
 llvm-as  -f -o tmp.bc tmp.ll
 llc -march=c -f -o test_dst.c tmp.bc

として test_dst.c を生成。~
なのだが、このまま別の(複数の)コンパイラでコンパイルしてみると
_&color(0){_imp_};_iobやQueryPerformanceCounter等が未定義参照だと
リンカに怒られる。
_&color(0){_imp_};_iob は stderr の本体(を収めた配列名)、
QueryPerfomance…は時間取得につかってるWin-APIのもの。

ヘッダ込みでコンパイルされた結果を C化してるのだから、 ~
''llvm-gcc でコンパイルする場合のヘッダ/ライブラリと、''~
''ターゲット・コンパイラでのヘッダ/ライブラリが同じ(矛盾しない)''~
でないと当然マズいのだった。

//>test_dst.c を覗くと、ターゲットコンパイラやos考慮の条件コンパイルでgcc(cygwin,mingw), vc, apple,sun, bsd 等の場合の調整は入っていたりする。やっていることは基本的な処理(allocaやmash関係等)の辻褄あわせの類だけれど。現状は llc 側にターゲットについて多少うめこまれている、と。

//今回のエラーは、他への置き換え(fprintf(stderr, は printf に、QueryPerf…関係は time.h clock()に)
とりあえずstderrやwin-apiを使わないようにして(外部はC標準関数だけにして)回避。~
ソースは、
[[これ>http://www.6809.net/tenk/html/prog/cc_test/bilinear_smp2_test.c.html]]と
[[これ>http://www.6809.net/tenk/html/sbr/pix32_resize.h.html]]。
~
それを llc で変換した後のソースは、
[[これ>http://www.6809.net/tenk/html/prog/cc_test/bilinear_smp2_test_dst_a.c.txt]]。

ついでに 一旦 ldでまとめたものを.llに戻してから llc したものも試す。
 llvm-gcc -S -o tmp.ll -emit-llvm -O2 -DNDEBUG test.c
 llvm-as  -f -o tmp.ll.bc  tmp.ll
 llvm-ld     -o tmp.ll.exe tmp.ll.bc
 llvm-dis -f -o tmp.2.ll   tmp.ll.exe.bc
 llvm-as  -f -o tmp.2.bc   tmp.2.ll
 llc -march=c -f -o test_dst.c tmp.2.bc

変換後のソースは、
[[これ>http://www.6809.net/tenk/html/prog/cc_test/bilinear_smp2_test_dst_b.c.txt]]。

前者の変換後ソースには元の関数を維持しているが、
後者の変換後ソースでは関数はmain()だけになっている。

~
2つの変換後のソースは、だいたい他のコンパイラでもコンパイル可能。~
(ダミーの alloca.h の読み込みを用意したり、
dmc では bool のtypedefが衝突するのでダミー alloca.h 内で誤魔化したり
する必要はあった)
(このとき使ったllvmは rev.98686 )

が失敗したモノもあり、~
vc64に関しては、ハング(作られたソースが32ビットcpu専用のソースかも?)。~
bcc 5.5.1 は宣言に型が多すぎる、と怒ってくれてコンパイルできず
(bcc 5.8.2は可)。~
clang は型チェック関係でひっかかるので挫折。~
mingw440 は、前者の変換ソースのものを実行するとハング。~
pcc では、後者の変換ソースでは、DEP チェックにひっかかりハング。~

実行できたものについての結果は以下 (単位:ミリ秒)

,コンパイラ            ,元の結果, 前者の変換での結果, 後者の変換での結果
,vc9sp1                ,     197 ,     153 ,     162 
,cygwin gcc344         ,     256 ,     238 ,     237 
,mingw gcc345          ,     255 ,     239 ,     237 
,mingw gcc440          ,     269 ,     --- ,     255 
,LLVM-gcc42(SSE?使用)  ,     103 ,     109 ,     104 
,open watcom(1.9rc1)   ,     397 ,     327 ,     317 
,dmc(8.51)             ,     352 ,     301 ,     317 
,pcc                   ,     323 ,     308 ,     --- 
,coins                 ,     493 ,     334 ,     317 
,PellesC               ,     408 ,     353 ,     350 
,TinyCC                ,     449 ,     397 ,     396 
//,bc58(TC++Expフリー版) ,     906 ,     750 ,     786 

測定したタイミングによっては10,20ぐらいの差がでたりするかもで、
ざっくりとした感じに受け止めてもらうとして。

LLVMのツールであるllvm-gccではあまり差はないようだが、
他のコンパイラでの結果は、多少なりとも
llc で変換したソースのほうが速くなってる雰囲気。

ただ前者と後者の変換については、モノによって多少違うも、
測定誤差でありえる範囲の差で、あまり違いはなさそうに思う
(関数の分割具合の違いによるオプティマイズの
トレードオフはあるだろうで、それが微妙に出てる可能性もあるけれど)

他のソースも試すべきたんだろうが、面倒で挫折...
他の例でも速くなるのか遅くなるのかどうかは結局ソースしだいと思うが、
それでも元のソースよりも速くなる例ができたわけだから
(元ソースがヘボいのだろう、というのは置いといて)、
他の例でもそう酷く遅くなるようなことはないんじゃないかと期待.

>※'元の結果'は3/12の結果と比べて少しずれてる。やってることはほぼ同じだけれどソース的には多少(時間取得関数だけでなくclang測定対策等)違うせいか、測定誤差といいきれない差のものがある. 前回のexeを実行すればやはり前回と同様の値なので、ソース変更の影響のよう。今回は今回、ということで。

>※[[zip>http://www.6809.net/tenk/html/prog/cc_test/cc_test_to_c.zip]]
[CR]
~
*** C++ → C

で、 肝心の c++ → c . ~
C++標準ライブラリの使用については、
実体がすべてヘッダでの定義ですんでいるようなモノ(std::sortとか)を使う分には、
(実体がライブラリ(アーカイブ .libや.a)化されているものを使わない範囲では)、
変換ソースを他のCコンパイラでもコンパイル&実行できる(た)。

が、g++(clang)側のライブラリ(.a)に実体がある関数や機能を使った場合は、
コンパイルできても当然リンクが行えない状態。
''bad_alloc'' みたいなライブラリものもそうだけれど、
''virtual使ったり 例外を使った場合''もそう。その他諸々。
C++らしい組み方をしてるとまずその手のものは使われてるだろうで...

足りてないリンク物の代用品をターゲット環境用にも用意できれば
なんとかなるかもだが、大仕事だろう。(あるいはターゲット対応をllcに施すとか... どちらにしろ個人がチョロですむ作業ではなく)
//標準ライブラリ関係はともかく、
//コンパイラ内部が使う専用(ライブラリ)関数はunkown情報に近いだろうしバージョンによる違いもありそうで...と思うもgccだとわりと固まってる? どっちにしろ
//個人がチョロですむ作業、というわけにはいかないだろう。
//(Cコンパイラでもマイナーなコンパイラの場合、メジャーなコンパイラ向けのライブラリ(.lib,.a)を使えるようにするためメジャーコンパイラの内部関数の代用品(辻褄合わせ)を用意してたりするわけでし)

// また、ソース的にはコンパイル可能な状態にできたとしても、古めのCコンパイラの場合は、識別子数や1関数の大きさ、名前の長さ等、某かの量的制限にひっかかりやすそうで...結局そういう環境は忘れるか C++ をあきらめる(あるもので我慢する)のが吉、という在り来りの結論。

I/Oを含まずtemplateを使ったような処理をベターCの範囲で書いてCに持ってくる、くらいなら...と思うも、やっぱ面倒だなあ.

~

あと、llc は c だけじゃなく c++ ソースも生成できる。~
計測に使ったソースを後者の変換をしてみたものが
[[これ>http://www.6809.net/tenk/html/prog/cc_test/bilinear_smp2_test_dst.cpp.txt]]。

なんかすごい、というか、目が点になる、というか。~
効率どうこう以前に趣旨が違うもののような気もする。
~
(パッと見でincludeフォルダ設定を追加してコンパイルを
試しみたけど、どれも上手くいかず。あまり追及する気にもなれず)

~

※と、まあ、発展途上のllvmを使っての感想なんで今後どうなっていくか、わからないけれど


*** 他のC++ → C トランスレータ

ついでのメモ。

元祖のC++実装の cfront は、Cソースへのトランスレータとして実装されている。
例外が実装されずに開発は終わっているが、
[[こちら>http://www.softwarepreservation.org/projects/c_plus_plus/index.html#cfront]] でR3.0.3等いくつかのバージョンのソースが公開されている模様。
(使えるかどうかはしらない)

現行のC++をサポートしているだろうトランスレータとしては
[[Comeau C/C++>http://www.comeaucomputing.com/]]。
有料だけど低価格($50)。
面倒がって未購入だけど。
他環境向けのCソース生成ならこちらのほうがよい?(夢見てるかも)...
といっても、ターゲット環境ごとの個別対応は必要だろうで、
Comeauのサイトで対応と書かれているのは比較的メジャーな環境のみのよう。



----
#comment