Nexus 7 の Ubuntu で ARM の abi softfp と hard-float を比べる

Nexus 7 の Ubuntu 13.04 は armhf (hard-float) です。
Android NDK は softfp なので、
Ubuntu の方が関数呼び出しが効率化されていると考えられます。
比べてみました。

// 元のソース
float func2( float a, float b, float c )
{
    return  a * b + c;
}

float func3( float a, float b, float c )
{
    return  a + b - c;
}

float func1( float a, float b )
{
    return  a * b + a;
}

↓gcc によるコンパイル結果 (softfp)

// -marm -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3

    fmsr    s14, r0
    fmsr    s13, r2
    fmsr    s15, r1
    fmacs   s13, s14, s15
    fmrs    r0, s13
    bx  lr

    fmsr    s13, r0
    fmsr    s14, r1
    fadds   s15, s13, s14
    fmsr    s13, r2
    fsubs   s13, s15, s13
    fmrs    r0, s13
    bx  lr

    fmsr    s13, r0
    fmsr    s15, r1
    fmacs   s13, s13, s15
    fmrs    r0, s13
    bx  lr

soft といっても浮動小数点演算をエミュレーション実行しているわけではなく、
上記のように VFP や NEON 等の HW 演算ユニットが使われています。

あくまで ABI (Calling Convention) の話で、関数の呼び出し時のレジスタの
使われ方が異なります。
softfp の場合は FPU (VFP) が無い場合 (soft) と互換性が取れるように、
レジスタ渡しの場合に浮動小数点値も整数レジスタ(r)に入ります。

上の結果でも、毎回整数レジスタ(r)に入った引数を VFP レジスタ(s)へ
コピーしていることがわかります。

-mfloat-abi=hard を指定すると↓下記のように不要な転送が無くなりました。
引数や戻り値としてそのまま VFP レジスタ(s)が使われています。

// -marm -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3

    fmacs   s2, s0, s1
    fcpys   s0, s2
    bx  lr

    fadds   s0, s0, s1
    fsubs   s0, s0, s2
    bx  lr

    fmacs   s0, s0, s1
    bx  lr

↓さらに vfpv4 を指定すると、fmacs の代わりに vfma が使われていることがわかります。

// -marm -march=armv7-a -mfloat-abi=hard -mfpu=vfpv4

    vfma.f32    s2, s0, s1
    fcpys   s0, s2
    bx  lr

    fadds   s0, s0, s1
    fsubs   s0, s0, s2
    bx  lr

    vfma.f32    s0, s0, s1
    bx  lr

NEON 命令も試してみました。

// 元のソース
#include   

float32x4_t func4( float32x4_t a, float32x4_t b )
{
    return  a * b + a;
}

-ffast-math を付けると neon 命令に変換できます。

↓softfp では 128bit x2 の値をレジスタだけで渡すことができません。
2つ目の引数が stack に入っています。

// -marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -ffast-math

	vmov	d16, r0, r1  @ v4sf
	vmov	d17, r2, r3
	vld1.64	{d18-d19}, [sp:64]
	vmla.f32	q8, q9, q8
	vmov	r0, r1, d16  @ v4sf
	vmov	r2, r3, d17
	bx	lr

↓hard の場合すべてレジスタで受け渡し可能となります。

// -marm -march=armv7-a -mfloat-abi=hard -mfpu=neon -ffast-math

	vmla.f32	q0, q1, q0
	bx	lr

以上より hard-float の場合に下記の 2つのメリットあるようです。
(他にもあるかもしれません)

・整数レジスタとの転送が不要となる
・引数として利用可能なレジスタの個数が増える

具体的な速度は測定していませんが、より高速に実行できると考えられます。

Android NDK だけでなく iOS の新しいアーキテクチャ armv7s でも softfp
相当となっているようです。

関連エントリ
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)