pcc(Portable C Compiler)のバグ

はじめに

pcc(Portable C Compiler)の 1.0.0がリリースされたということで
testgenを使ってテストしてみたんですが、
float周りのテストがえらくこけるなってことでその原因を調べました。
その経緯について記します。

環境

Ubuntu 10.10(32bit) pcc1.0.0, pcc-lib1.0.0
ビルドに使用したコンパイラ gcc-4.4.5(Ubuntu/Linaro 4.4.4-14ubuntu5)

バグ再現プログラム

以下のプログラムを実行した場合期待した値(1.0)が出力されません.

#include <stdio.h>

static void k_and_r_func(arg)
    float arg;
{
    printf("%f\n", arg);
}

int main (void)
{
    float i = 1.0;

    k_and_r_func(i);

    return 0;
}

実行結果

最適化レベルは O0(最適化なし), O2でも再現します。

   % pcc -O0 test.c
   % ./a.out
   0.000000

こうなります。GCCllvm-clangではともに 1.000000となります。

問題点

問題は K&Rの引数の渡し方に対応したコードが正しく出力されていない
ことです。K&Rでは float型の引数はすべて double型として扱われます。
(なお char, shortの引数は intとして扱われます。)
アセンブリの出力を見ると、呼び出し側が引数を積むときは doubleに
しているんですが、受け取る側は floatのまま受け取ろうとしているので
正しい結果が得られていないということがわかりました。

解決策

関数の定義を K&Rで書いても、プロトタイプ宣言を書けば、正しく動作
しました。K&Rでなく、ANSI-Cとして扱われて、格上げすることがない
コードが生成されたためです。また引数を ANSI-Cで書いても正しく動作しました。

アセンブリコードの確認

プロトタイプ宣言あり、なしの場合で出力されるアセンブリコードを
確認してみました。

   % pcc -S -o k_and_r.s test.c(プロトタイプ宣言なし, K&R)
   % pcc -S -o ansi.s test.c      (プロトタイプ宣言あり, ANSI-C)
   % diff -u k_ans_r.s ansi.s

ラベルの番号が違ってしまい差分はわりとあったのですが、
本質的な違いは main関数(caller)の引数を積む以下の部分だけでした。

-       flds -4(%ebp)
-       subl $8,%esp
-       fstpl (%esp)
+       pushl -4(%ebp)
        call k_and_r_func
-       addl $8, %esp
+       addl $4, %esp
        movl $0,-8(%ebp)

これ自身はあっているのですが、呼ばれる側(callee)のコードが
一切変わっていないことが問題です。


gccで同じことをやるとちゃんと caller, calleeでコードの違いが見られます。

 k_and_r_func:
        pushl   %ebp
        movl    %esp, %ebp
-       subl    $40, %esp
-       fldl    8(%ebp)
-       fstps   -12(%ebp)
-       flds    -12(%ebp)
-       fstps   -16(%ebp)
-       flds    -16(%ebp)
+       subl    $24, %esp
+       flds    8(%ebp)
        movl    $.LC0, %eax
        fstpl   4(%esp)
        movl    %eax, (%esp)
@@ -29,8 +25,8 @@
        subl    $32, %esp
        movl    $0x3f800000, %eax
        movl    %eax, 28(%esp)
-       flds    28(%esp)
-       fstpl   (%esp)
+       movl    28(%esp), %eax
+       movl    %eax, (%esp)
        call    k_and_r_func
        movl    $0, %eax

考察

今時 K&Rで新規に書くってことはほとんどないので、今から書くようなコードに
ついては問題が出るってことはないと思います。ただ C言語は過去の資源が膨大
なのでそういうのを pccでビルドして実行するときに問題が出るかもしれないです。
(まあ pccを使うというのがそもそもないという可能性が高そうですけどね。)


やっぱり GCCllvm-clangに比べるとまだまだ問題があるのかなと思いました。
OpenBSDはメインで使うというのをどこかで聞いたような気もするんだけど、
大丈夫なんですかね。カーネルK&Rのコードが残っていたりしますし。
カーネル浮動小数点を使うことはほとんどないので大丈夫なのかもしれないですが。
まあプロトタイプ宣言をちゃんと書けばいいので、そこまで心配する必要はないと
思います。

まとめ

pccのバグを紹介しました。pcc-libの関係で現在は 1.0.0しか確認できて
いませんが、最新版で確認して既知の問題でなければちゃんとレポートを
書こうかなと思います。

追記

20110423 CVS版を試してみたところバグがフィックスされていました。
念の為再テストを行い。他の問題がないかについて調べてみます。