TCC をリンカに

LinuxDMD で作業してるとすぐ気付くんですが、コンパイルは速いのでボトルネック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 として使っています。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h