V850 GCC 4.6.1のテスト その 1

v850用クロス開発環境の構築 - Life is very short


で作った GCCtestgen
かけていたところ終了しないプログラムがあったのでそれについて。

問題が発生するプログラム

testgenの gcc.4-2-01/t0501.cです。以下に示すのは最小化し、
関数名等一部変更したものです。

#include <stdio.h>

static void func1(char *string, short constant);
void func2(char *string, short constant);
static void func3(char *string, short constant);

short statusFlag = 0;

int main(void)
{
        func1("A", 1);
        if (statusFlag == 0)
          puts("@OK@\n");
        else
          puts("@NG@\n");
        
        return (0);
}

static void func1(char *string, short constant)
{
        short           i;

        for (i = 0; i < constant; i++, string++)
                if (*string != 'A' + i)
                        statusFlag++;

        func2("ABC", 3);
}

void func2(char *string, short constant)
{
        short           i;

        for (i = 0; i < constant; i++, string++)
                if (*string != 'A' + i)
                        statusFlag++;

        func3("ABCDEFG", 7);
}

static void func3(char *string, short constant)
{
        short           i;

        for (i = 0; i < constant; i++, string++)
                if (*string != 'A' + i)
                        statusFlag++;
}

これを以下のコンパイルオプションでコンパイルすると
プログラムが終了しません. (プログラム名は t0501_min.cとしています。)

  % v850-linux-elf-gcc -Wall -Wextra -g -O2 -funroll-loops t0501_min.c
  % v850-linux-elf-run a.out
  # 終わらない

ちなみに func2を staticにすると PASSします。もともとは全部グローバル
なんですが、そのあたりも問題があるんじゃと考えて、最小化したところ
このような構成になっています。また funroll-loopsを外すと PASSします。
問題は funroll-loopsにあるものと思われます。

オブジェクトコードを見る

オブジェクトコードを生成してそれを確認します.

  % v850-linux-elf-gcc -Wall -Wextra -g -O2 -funroll-loops -c t0501_min.c
  % v850-linux-elf-objdump -D t0501_min.o 

gistに main, func2の出力を貼りました。
Object file create by v850-gcc (4.6.1) · GitHub


注目するのは func2の以下の部分です。

  74:	06 ff 00 00       ld.b   0[r6], lp
  78:	d8 fa                shl    24, lp
  7a:	b8 fa                sar    24, lp
  7c:	eb f9                cmp  r11, lp

lp(link pointer)レジスタを計算に使っています。これは MIPSでいうと raレジスタ
ようなもので、関数からリターンしたときに戻るアドレスが入っているレジスタです。
普通演算目的には使わないと思います。もし使うのであれば、スタックに退避し、
使用後元に戻す必要がありますが、そのようなコードがありません。


でこの関数の最後では lpレジスタが使われたことなんて知ってかしらずか,
lpのアドレスにジャンプしています。

  124:   7f 00           jmp     [lp

結果として呼び出し元の関数ではなく、演算に使われた際の値とする
アドレスにジャンプし、無限ループに陥っていたということになります。

最後に

v850 GCC 4.6.1で問題が発生する場合を紹介しました。
他にも問題あるので、それも見て、書くべきだったらレポートを書こうかなと
思います。


他のバージョンは試していないのでわからないですが、
4.6.1使うなら、ループアンローリングはしない方がいいと思います。

追記

gcc-4.5.3で確認したところ、問題が発生しませんでした。
gcc-4.6からの問題のようです。差分そんなにないだろって思ったん
ですが、1000行単位で diffが取れたので調査は大変そうです。

追記 その 2(2011/JUL/10)

Currentも駄目だったんですが、以下の修正を行うことで testgenはすべて
パスしました。CALL_USED_REGISTERSの lp(r31)レジスタを 1から 0に変更。
理由は MIPSの raレジスタの設定がそうなっていたというだけです。

Index: v850.h
===================================================================
--- v850.h	(リビジョン 175973)
+++ v850.h	(作業コピー)
@@ -223,7 +223,7 @@
   { 1, 1, 1, 1, 1, 1, 1, 1, \
     1, 1, 1, 1, 1, 1, 1, 1, \
     1, 1, 1, 1, 0, 0, 0, 0, \
-    0, 0, 0, 0, 0, 0, 1, 1, \
+    0, 0, 0, 0, 0, 0, 1, 0, \
     1, 1,	\
     1, 1}