TCC をリンカに
Linux の DMD で作業してるとすぐ気付くんですが、コンパイルは速いのでボトルネックは GCC というか GNU ld だったりするんです。小さいファイルの場合特に。んでていうか ld ってなんかやけに遅いよねぇとか思ってたのでリンカ書いてみる…とか思った時代が私にもあったのですが、ふと思い出して TCC でリンクしてみたら速かったのでした。
とりあえず ffi.d はテストがたくさんあるから遅い…てことでちょっと普段と違う環境で TCC でえいやっとやってみたところ 3.59sec vs 17.24sec とのことで、おおすばらしーと思ったのでした。んで家帰ってやってみるとエラー出るのでした。リンカスクリプトの AS_NEEDED が読めない問題。これは前から知っててた問題で、 libc.so の該当個所を適当に削ったりしてたんですが、まぁ TCC いじるのが正道だろうといじったら治ったのですが別のエラー。 __i686.get_pc_thunk.bx が二回定義されてるとかなんとか。適当にぐぐるとPC取ってくるものとのこと。どうでもいいけどシンボルの内容変わることとか無さそうなので適当に ad-hoc な修正をかます。
--- tccelf.c.orig 2005-06-18 07:09:15.000000000 +0900 +++ tccelf.c 2006-12-22 16:58:58.000000000 +0900 @@ -208,8 +208,11 @@ sym_bind, sh_num, esym_bind, esym->st_shndx); #endif /* NOTE: we accept that two DLL define the same symbol */ - if (s != tcc_state->dynsymtab_section) - error_noabort("'%s' defined twice", name); + if (s != tcc_state->dynsymtab_section) { + /* ad-hoc: ignore __i686.get_pc_thunk.bx */ + if (strcmp(name, "__i686.get_pc_thunk.bx")) + error_noabort("'%s' defined twice", name); + } } } else { do_patch: @@ -2308,7 +2311,18 @@ } else if (t != LD_TOK_NAME) { error_noabort("filename expected"); return -1; - } + } else if (!strcmp(filename, "AS_NEEDED")) { + /* ignore AS_NEEDED */ + t = ld_next(s1, cmd, sizeof(cmd)); + if (t != '(') + expect("("); + for(;;) { + t = ld_next(s1, cmd, sizeof(cmd)); + if (t == ')') break; + } + t = ld_next(s1, cmd, sizeof(cmd)); + continue; + } tcc_add_file(s1, filename); t = ld_next(s1, filename, sizeof(filename)); if (t == ',') {
んでやってみた。
sh -c make 10.98s user 2.02s system 95% cpu 13.587 total sh -c make 15.31s user 3.50s system 94% cpu 19.991 total
ffi.d を、上が TCC で下が GCC でビルドしてみた結果。なんかたいして速くなってねえ。
i@u ~/wrk/dffi> time tcc ffi.o test/c_v.o -lphobos -lpthread -lm tccmain.o tcc ffi.o test/c_v.o -lphobos -lpthread -lm tccmain.o 0.17s user 0.03s system 99% cpu 0.197 total i@u ~/wrk/dffi> time gcc ffi.o test/c_v.o -lphobos -lpthread -lm tccmain.o gcc ffi.o test/c_v.o -lphobos -lpthread -lm tccmain.o 0.28s user 0.06s system 98% cpu 0.336 total
ファイル一個だとこんなもん。まぁ速くはなってるか。 TCC を D のリンカとして使う場合、
int main(int argc, char* argv[]); _main(int argc, char* argv[]) { main(argc, argv); }
みたいなファイルを用意して tccmain.c として使っています。