2012年04月11日

arm-linux-gnueabihf-gcc (Hard Float)の生成コードを見てみる

Ubuntu 12.04LTSでのarm-linux-gnueabihf-gccのツールチェインのhfはHard Floatの意味です。

関数呼び出し規約(Calling Convention)が変更されて、double, floatの引数はFPUのレジスタに積んで渡し、戻り値もFPUのレジスタを使うようになっています。今までは、FPUが利用可能だったとしても、Soft Floatと互換性を保つために汎用レジスタで引き渡していました。当然これらは混ぜて使うことはできません。全てのライブラリに影響があります。

簡単なプログラムで実際に生成されるコードを見てみます。



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

ツールチェインのインストール

$ sudo apt-get install g++-arm-linux-gnueabi
$ sudo apt-get install g++-arm-linux-gnueabihf

これで/usr/bin に2通りのARMのツールチェインが揃いました。

$ ls /usr/bin/arm-linux-gnueabi*
/usr/bin/arm-linux-gnueabi-addr2line    /usr/bin/arm-linux-gnueabihf-gcov
/usr/bin/arm-linux-gnueabi-ar           /usr/bin/arm-linux-gnueabihf-gcov-4.6
/usr/bin/arm-linux-gnueabi-as           /usr/bin/arm-linux-gnueabihf-gprof
/usr/bin/arm-linux-gnueabi-c++filt      /usr/bin/arm-linux-gnueabihf-ld
/usr/bin/arm-linux-gnueabi-cpp          /usr/bin/arm-linux-gnueabihf-ld.bfd
/usr/bin/arm-linux-gnueabi-cpp-4.6      /usr/bin/arm-linux-gnueabihf-ld.gold
/usr/bin/arm-linux-gnueabi-elfedit      /usr/bin/arm-linux-gnueabihf-nm
/usr/bin/arm-linux-gnueabi-g++          /usr/bin/arm-linux-gnueabihf-objcopy
/usr/bin/arm-linux-gnueabi-g++-4.6      /usr/bin/arm-linux-gnueabihf-objdump
/usr/bin/arm-linux-gnueabi-gcc          /usr/bin/arm-linux-gnueabihf-ranlib
/usr/bin/arm-linux-gnueabi-gcc-4.6      /usr/bin/arm-linux-gnueabihf-readelf
/usr/bin/arm-linux-gnueabi-gcov         /usr/bin/arm-linux-gnueabihf-size
/usr/bin/arm-linux-gnueabi-gcov-4.6     /usr/bin/arm-linux-gnueabihf-strings
/usr/bin/arm-linux-gnueabi-gprof        /usr/bin/arm-linux-gnueabihf-strip
/usr/bin/arm-linux-gnueabihf-addr2line  /usr/bin/arm-linux-gnueabi-ld
/usr/bin/arm-linux-gnueabihf-ar         /usr/bin/arm-linux-gnueabi-ld.bfd
/usr/bin/arm-linux-gnueabihf-as         /usr/bin/arm-linux-gnueabi-ld.gold
/usr/bin/arm-linux-gnueabihf-c++filt    /usr/bin/arm-linux-gnueabi-nm
/usr/bin/arm-linux-gnueabihf-cpp        /usr/bin/arm-linux-gnueabi-objcopy
/usr/bin/arm-linux-gnueabihf-cpp-4.6    /usr/bin/arm-linux-gnueabi-objdump
/usr/bin/arm-linux-gnueabihf-elfedit    /usr/bin/arm-linux-gnueabi-ranlib
/usr/bin/arm-linux-gnueabihf-g++        /usr/bin/arm-linux-gnueabi-readelf
/usr/bin/arm-linux-gnueabihf-g++-4.6    /usr/bin/arm-linux-gnueabi-size
/usr/bin/arm-linux-gnueabihf-gcc        /usr/bin/arm-linux-gnueabi-strings
/usr/bin/arm-linux-gnueabihf-gcc-4.6    /usr/bin/arm-linux-gnueabi-strip

テストプログラム

$ cat hello_float.c
#include <stdio.h>
double dadd(double x, double y) 
{
    return x + y;
}

float fadd(float x, float y) 
{
    return x + y;
}

int main()
{
    float x0 = 1.2f;
    float y0 = 2.3f;
    double x1 = 3.4d;
    double y1 = 4.5d;
    printf("%f + %f = %f\n", x0, y0, fadd(x0, y0));
    printf("%f + %f = %f\n", x1, y1, dadd(x1, y1));
}

このプログラムを2通りのツールチェインでコンパイルして、daddとfaddの部分のコードを比較してみます。

arm-linux-eabiでのコード

$ arm-linux-gnueabi-gcc -S -O -o hello_float_gnueabi.s hello_float.c
	.align	2
	.global	dadd
	.thumb
	.thumb_func
	.type	dadd, %function
dadd:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	fmdrr	d6, r0, r1
	fmdrr	d7, r2, r3
	faddd	d6, d6, d7
	fmrrd	r0, r1, d6
	bx	lr
	.size	dadd, .-dadd
	.align	2
	.global	fadd
	.thumb
	.thumb_func
	.type	fadd, %function
fadd:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	fmsr	s14, r0
	fmsr	s15, r1
	fadds	s14, s14, s15
	fmrs	r0, s14
	bx	lr
	.size	fadd, .-fadd

fadd[s|d]の前後で汎用レジスタとFPUレジスタの転送を行っています。

arm-linux-gnueabihfでのコード

$ arm-linux-gnueabihf-gcc -S -O -o hello_float_gnueabihf.s hello_float.c
	.align	2
	.global	dadd
	.thumb
	.thumb_func
	.type	dadd, %function
dadd:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	faddd	d0, d0, d1
	bx	lr
	.size	dadd, .-dadd
	.align	2
	.global	fadd
	.thumb
	.thumb_func
	.type	fadd, %function
fadd:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	fadds	s0, s0, s1
	bx	lr
	.size	fadd, .-fadd

無駄な転送がなくなってすっきりしました。

コンパイルオプションの詳細

gccに-v オプションをつけると内部的に実行されているコマンドとオプションを調べることができます。

$ arm-linux-gnueabi-gcc -v -S -O -o hello_float_gnueabi.s hello_float.c
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabi/4.6/lto-wrapper
Target: arm-linux-gnueabi
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.6.3-1ubuntu3' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/arm-linux-gnueabi/include/c++/4.6.3 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --enable-multilib --disable-sjlj-exceptions --with-arch=armv7-a --with-float=softfp --with-fpu=vfpv3-d16 --with-mode=thumb --disable-werror --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=arm-linux-gnueabi --program-prefix=arm-linux-gnueabi- --includedir=/usr/arm-linux-gnueabi/include --with-headers=/usr/arm-linux-gnueabi/include --with-libs=/usr/arm-linux-gnueabi/lib
Thread model: posix
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu3) 
COLLECT_GCC_OPTIONS='-v' '-S' '-O' '-o' 'hello_float_gnueabi.s' '-march=armv7-a' '-mfloat-abi=softfp' '-mfpu=vfpv3-d16' '-mthumb'
 /usr/lib/gcc/arm-linux-gnueabi/4.6/cc1 -quiet -v -imultilib . -imultiarch arm-linux-gnueabi hello_float.c -quiet -dumpbase hello_float.c -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -auxbase-strip hello_float_gnueabi.s -O -version -o hello_float_gnueabi.s -fstack-protector
GNU C (Ubuntu/Linaro 4.6.3-1ubuntu3) version 4.6.3 (arm-linux-gnueabi)
	compiled by GNU C version 4.6.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=127437
ignoring duplicate directory "/usr/lib/gcc/arm-linux-gnueabi/4.6/../../../../arm-linux-gnueabi/include"
ignoring nonexistent directory "/usr/include/arm-linux-gnueabi"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/arm-linux-gnueabi/4.6/include
 /usr/lib/gcc/arm-linux-gnueabi/4.6/include-fixed
 /usr/arm-linux-gnueabi/include
 /usr/include
End of search list.
GNU C (Ubuntu/Linaro 4.6.3-1ubuntu3) version 4.6.3 (arm-linux-gnueabi)
	compiled by GNU C version 4.6.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=127437
Compiler executable checksum: 3cfa61caeb3941938bdc401640938509
COMPILER_PATH=/usr/lib/gcc/arm-linux-gnueabi/4.6/:/usr/lib/gcc/arm-linux-gnueabi/4.6/:/usr/lib/gcc/arm-linux-gnueabi/:/usr/lib/gcc/arm-linux-gnueabi/4.6/:/usr/lib/gcc/arm-linux-gnueabi/:/usr/lib/gcc/arm-linux-gnueabi/4.6/../../../../arm-linux-gnueabi/bin/
LIBRARY_PATH=/usr/lib/gcc/arm-linux-gnueabi/4.6/:/usr/lib/gcc/arm-linux-gnueabi/4.6/../../../../arm-linux-gnueabi/lib/../lib/:/usr/lib/gcc/arm-linux-gnueabi/4.6/../../../../arm-linux-gnueabi/lib/
COLLECT_GCC_OPTIONS='-v' '-S' '-O' '-o' 'hello_float_gnueabi.s' '-march=armv7-a' '-mfloat-abi=softfp' '-mfpu=vfpv3-d16' '-mthumb'
$ arm-linux-gnueabihf-gcc -v -S -O -o hello_float_gnueabihf.s hello_float.c
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabihf-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/4.6/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.6.3-1ubuntu3' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/arm-linux-gnueabihf/include/c++/4.6.3 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --enable-multilib --disable-sjlj-exceptions --with-arch=armv7-a --with-float=hard --with-fpu=vfpv3-d16 --with-mode=thumb --disable-werror --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=arm-linux-gnueabihf --program-prefix=arm-linux-gnueabihf- --includedir=/usr/arm-linux-gnueabihf/include --with-headers=/usr/arm-linux-gnueabihf/include --with-libs=/usr/arm-linux-gnueabihf/lib
Thread model: posix
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu3) 
COLLECT_GCC_OPTIONS='-v' '-S' '-O' '-o' 'hello_float_gnueabihf.s' '-march=armv7-a' '-mfloat-abi=hard' '-mfpu=vfpv3-d16' '-mthumb'
 /usr/lib/gcc/arm-linux-gnueabihf/4.6/cc1 -quiet -v -imultilib . -imultiarch arm-linux-gnueabihf hello_float.c -quiet -dumpbase hello_float.c -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mthumb -auxbase-strip hello_float_gnueabihf.s -O -version -o hello_float_gnueabihf.s -fstack-protector
GNU C (Ubuntu/Linaro 4.6.3-1ubuntu3) version 4.6.3 (arm-linux-gnueabihf)
	compiled by GNU C version 4.6.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=127437
ignoring duplicate directory "/usr/lib/gcc/arm-linux-gnueabihf/4.6/../../../../arm-linux-gnueabihf/include"
ignoring nonexistent directory "/usr/include/arm-linux-gnueabihf"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/arm-linux-gnueabihf/4.6/include
 /usr/lib/gcc/arm-linux-gnueabihf/4.6/include-fixed
 /usr/arm-linux-gnueabihf/include
 /usr/include
End of search list.
GNU C (Ubuntu/Linaro 4.6.3-1ubuntu3) version 4.6.3 (arm-linux-gnueabihf)
	compiled by GNU C version 4.6.3, GMP version 5.0.2, MPFR version 3.1.0-p3, MPC version 0.9
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=127437
Compiler executable checksum: 203eb4988339bc30463f8e81baca2202
COMPILER_PATH=/usr/lib/gcc/arm-linux-gnueabihf/4.6/:/usr/lib/gcc/arm-linux-gnueabihf/4.6/:/usr/lib/gcc/arm-linux-gnueabihf/:/usr/lib/gcc/arm-linux-gnueabihf/4.6/:/usr/lib/gcc/arm-linux-gnueabihf/:/usr/lib/gcc/arm-linux-gnueabihf/4.6/../../../../arm-linux-gnueabihf/bin/
LIBRARY_PATH=/usr/lib/gcc/arm-linux-gnueabihf/4.6/:/usr/lib/gcc/arm-linux-gnueabihf/4.6/../../../../arm-linux-gnueabihf/lib/../lib/:/usr/lib/gcc/arm-linux-gnueabihf/4.6/../../../../arm-linux-gnueabihf/lib/
COLLECT_GCC_OPTIONS='-v' '-S' '-O' '-o' 'hello_float_gnueabihf.s' '-march=armv7-a' '-mfloat-abi=hard' '-mfpu=vfpv3-d16' '-mthumb'

デフォルトで -march=armv7-a -mthumb なので、Thumb2のコードが生成されます。fpuのタイプはvfpv3-d16だからNEON無しでも動くようになってますね。

おまけ (x86_64とx86)

$ gcc -v -S -O -o hello_float_native.s hello_float.c
	.globl	dadd
	.type	dadd, @function
dadd:
.LFB22:
	.cfi_startproc
	addsd	%xmm1, %xmm0
	ret
	.cfi_endproc
.LFE22:
	.size	dadd, .-dadd
	.globl	fadd
	.type	fadd, @function
fadd:
.LFB23:
	.cfi_startproc
	addss	%xmm1, %xmm0
	ret
	.cfi_endproc
.LFE23:
	.size	fadd, .-fadd

x86_64はFPUのレジスタ渡しですっきり。

$  sudo apt-get install g++-multilib
$  gcc -v -m32 -S -O -o hello_float_86.s hello_float.c
	.globl	dadd
	.type	dadd, @function
dadd:
.LFB22:
	.cfi_startproc
	fldl	12(%esp)
	faddl	4(%esp)
	ret
	.cfi_endproc
.LFE22:
	.size	dadd, .-dadd
	.globl	fadd
	.type	fadd, @function
fadd:
.LFB23:
	.cfi_startproc
	flds	8(%esp)
	fadds	4(%esp)
	ret
	.cfi_endproc
.LFE23:
	.size	fadd, .-fadd

x86だとスタック渡し。



トラックバックURL

コメントする

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

QRコード
QRコード