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だとスタック渡し。