2012年04月16日

ARM Cortex-A15の整数除算命令

ARMのCPUにはいままでずっと整数の除算命令がありませんでした。最近になって、Cortex-MシリーズとRシリーズには除算命令が追加されましたが、Cortex-AシリーズではCortex-A9までは除算命令はありませんでした。新しいCortex-A15には除算命令が追加されていました。

新しいgccとqemuはすでにこれに対応済みなので、試してみました。



Cortex-A15の整数除算命令

ARM Architecture Reference Manual を見ると、ARMv7-A の"The Virtulization Extensions" ではSDIVとUDIVの命令の実装を含むと書いてあります。

ARMv7-R profileではSCTLR.DZ bitでゼロ除算のときに例外を発生させるかどうかを設定することができますが、ARMv7-A profileの実装ではSDIVとUDIV命令はゼロ除算では常に例外を発生させることなく0を返すと書いてあります。

gccとqemuで動作を試す

Ubuntu 12.04 Beta2 を使用しています。

テストプログラム

$ cat idiv.c 
#include <stdio.h>
#include <limits.h>

int idiv(int x, int y) 
{
    return x / y;
}

int main()
{
    int x0, y0;

    x0 = 101; y0 = 3;
    printf("%d / %d = %d\n", x0, y0, idiv(x0, y0));

    x0 = INT_MIN; y0 = -1;
    printf("%d / %d = %d\n", x0, y0, idiv(x0, y0));

    x0 = 101; y0 = 0;
    printf("%d / %d = %d\n", x0, y0, idiv(x0, y0));

}

gccの生成コードを見てみる

$ arm-linux-gnueabi-gcc -O -S -o idiv_arm.s idiv.c

特にCPUを指定せずにコンパイルすると以下のように整数除算では__aeabi_idivというコンパイラのランタイムライブラリを呼び出します。

idiv:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	push	{r3, lr}
	bl	__aeabi_idiv
	pop	{r3, pc}
$ arm-linux-gnueabi-gcc -mcpu=cortex-a15 -O -S -o idiv_a15.s idiv.c

CPUをcortex-a15に指定してコンパイルすると以下のようにSDIV命令を使用するようになります。

idiv:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	sdiv	r0, r0, r1
	bx	lr

除数が0かどうかチェックするコードが挿入されるかと思いましたがそうはなっていませんね。

ユーザーモードqemuで動作を見てみる

コンパイル、リンクして実行オブジェクトを作ります。

$ arm-linux-gnueabi-gcc -mcpu=cortex-a15 -O -o idiv_a15 idiv.c
$ arm-linux-gnueabi-gcc -O -o idiv_arm idiv.c
$ file idiv_a15 idiv_arm
idiv_a15: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.31, BuildID[sha1]=0x6920c8ce8d71f1a7e040bfa53823357e672943fd, not stripped
idiv_arm: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.31, BuildID[sha1]=0x64eb7fac6ca05f180e2dcc7209e9986019d9d7bb, not stripped

bimfmtに登録されているqemu-arm-static で実行してみます。CPU種別は "any" になっています。

$ ./idiv_arm
101 / 3 = 33
-2147483648 / -1 = -2147483648
qemu: uncaught target signal 8 (Floating point exception) - core dumped
Floating point exception (core dumped)
$ ./idiv_a15
101 / 3 = 33
-2147483648 / -1 = -2147483648
101 / 0 = 0

__aeabi_idivを呼び出すほうでは、ゼロ除算でFloating point exceptionが発生しました。A15用のSDIV命令では例外は発生せずに結果は0になりました。

INT_MIN / (-1) はオーバーフローしますが、特に例外は発生せずに結果はINT_MINのままです。

今度は明示的にCPUの種別を指定して実行してみます。

$ qemu-arm -cpu cortex-a15 ./idiv_a15
101 / 3 = 33
-2147483648 / -1 = -2147483648
101 / 0 = 0
$ qemu-arm -cpu cortex-a9 ./idiv_a15
qemu: uncaught target signal 4 (Illegal instruction) - core dumped
Illegal instruction (core dumped)

CPUをcortex-a9に指定すると、SDIV命令がIllegal instructionになりました。

おまけ (x86_64の場合)

x86_64では、INT_MIN/(-1) もゼロ除算もどちらもFloating point exceptionが発生します。

$ gcc -O idiv.c
$ objdump -d a.out |less

関数idivは以下のようにidiv命令を使用していました。

0000000000400524 <idiv>:
  400524:       89 f8                   mov    %edi,%eax
  400526:       89 fa                   mov    %edi,%edx
  400528:       c1 fa 1f                sar    $0x1f,%edx
  40052b:       f7 fe                   idiv   %esi
  40052d:       c3                      retq   
$ ./a.out
101 / 3 = 33
Floating point exception (core dumped)

INT_MIN / (-1)でFloating point exception

$ vi idiv.c 
$ cat idiv.c 
#include <stdio.h>
#include <limits.h>

int idiv(int x, int y) 
{
    return x / y;
}

int main()
{
    int x0, y0;

    x0 = 101; y0 = 3;
    printf("%d / %d = %d\n", x0, y0, idiv(x0, y0));

    //x0 = INT_MIN; y0 = -1;
    //printf("%d / %d = %d\n", x0, y0, idiv(x0, y0));

    x0 = 101; y0 = 0;
    printf("%d / %d = %d\n", x0, y0, idiv(x0, y0));

}

INT_MIN / (-1)をコメントアウト。

$ gcc -O idiv.c
$ ./a.out
101 / 3 = 33
Floating point exception (core dumped)

ゼロ除算もFloating point exception

追記: 剰余算

以下の関数がCortex-A15でどうコンパイルされるか調べました。

int imod(int x, int y) 
{
    return x % y;
}
$ arm-linux-gnueabi-gcc -mcpu=cortex-a15 -O -o idiv_a15 idiv.c
$ arm-linux-gnueabi-objdump -d idiv_a15 |less

関数 imodの部分は以下の通り。

00008390 <imod>:
    8390:       fb90 f3f1       sdiv    r3, r0, r1
    8394:       fb03 0011       mls     r0, r3, r1, r0
    8398:       4770            bx      lr
    839a:       bf00            nop

sdivで商を求めた後に、乗算と減算で剰余を求めていました。



トラックバックURL

コメントする

名前
URL
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード