15

私はこの非常に単純なコードを持っています:

#include <stdio.h>
#include <math.h>
int main()
{
    long v = 35;
    double app = (double)v;
    app /= 100;
    app = log10(app);
    printf("Calculated log10 %lf\n", app);
    return 0;
}

このコードは x86 では完全に機能しますが、結果が 0.00000 になるアームでは機能しません。いくつかのアイデア?

他の情報:

オペレーティング システム: Linux 3.2.27

ct-ng で arm ツールチェーンをビルドします: arm-unknown-linux-gnueabi-

libc バージョン 2.13

の出力gcc -v:

組み込みの仕様を使用します。COLLECT_GCC=arm-unknown-linux-gnueabi-gcc COLLECT_LTO_WRAPPER=/opt/x-tools/arm-unknown-linux-gnueabi/libexec/gcc/arm-unknown-linux-gnueabi/4.5.1/lto-wrapper ターゲット: arm -unknown-linux-gnueabi 構成: /home/mirko/misc/rasppi-ct-ng-files/.build/src/gcc-4.5.1/configure --build=x86_64-build_unknown-linux-gnu --host =x86_64-build_unknown-linux-gnu --target=arm-unknown-linux-gnueabi --prefix=/opt/x-tools/arm-unknown-linux-gnueabi --with-sysroot=/opt/x-tools/ arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi//sys-root --enable-languages=c --disable-multilib --with-pkgversion=crosstool-NG-1.9.3 --enable-__cxa_atexit --disable-libmudflap --disable-libgomp --disable-libssp --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --with-gmp=/home /mirko/misc/rasppi-ct-ng-files/.

4

1 に答える 1

10

ARM Linux ディストリビューションでの浮動小数点のサポートは簡単ではありません。そのため、オペレーティング システムとハードウェアであるシステムに一致するツールチェーンを使用し、適切なコンパイル スイッチを使用する必要があります。

まず、「関数を呼び出すときに引数を渡す方法」に関する ARM の呼び出し規則を理解する必要があります。. ARM は RISC アーキテクチャであるため、レジスタでのみ動作します。メモリを直接操作する命令はありません。メモリ内の値を変更する必要がある場合は、最初にそれをレジスタにロードし、変更してからメモリに保存する必要があります。

関数を呼び出すときに引数を渡す必要がある場合は、引数をスタック (メモリ) に置くことができますが、ARM はレジスタでしか動作しないため、関数が最初に行うことはおそらくそれらをレジスタにロードすることです。この無駄を避けるために、ARM 呼び出し規約はレジスタを使用して引数を渡します。ただし、ARM には限られた数のレジスタがあるため、呼び出し規約では、最初の 4 つの引数に最初の 4 つ (r0-r3) のレジスタのみを使用するように指示されており、残りは渡されるためにスタックを使用する必要があります。

2 つ目は、初期の ARM コアには浮動小数点がサポートされておらず、操作がソフトウェアで実装されていたことです。(これは、gcc の を介してまだサポートされているものです-mfloat-abi=soft。)

次のスニペットを使用して、これが何を意味するかを簡単に示すことができます。

float pi2(float a) {
    return a * 3.14f;
}

-c -O3 -mfloat-abi=softこれを viaとobdumpingでコンパイルすると、

00000000 <pi2>:
   0:   f24f 51c3   movw    r1, #62915  ; 0xf5c3
   4:   b508        push    {r3, lr}
   6:   f2c4 0148   movt    r1, #16456  ; 0x4048
   a:   f7ff fffe   bl  0 <__aeabi_fmul>
   e:   bd08        pop {r3, pc}

ご覧のとおり (実際には表示されません:))pi2でパラメータを取得しr0、入力pi constantr1て使用__aeabi_fmulし、それらを乗算して結果を で返しr0ます。も同じ呼び出し規約を使用しているため__aeabi_fmul、詳細r0は表示されません。すべての関数は、データを入力r1して に委譲するために行い__aeabi_fmulます。

フローティング ハードウェア サポートが ARM に追加されたとき (これもアーキテクチャ スタイルのため)、独自のレジスタ セット(s0、s1、...)が付属していました。

同じスニペットをコンパイルし-c -O3 -mfloat-abi=softfpてダンプすると、

00000000 <pi2>:
   0:   eddf 7a04   vldr    s15, [pc, #16]  ; 14 <pi2+0x14>
   4:   ee07 0a10   vmov    s14, r0
   8:   ee27 7a27   vmul.f32    s14, s14, s15
   c:   ee17 0a10   vmov    r0, s14
  10:   4770        bx  lr
  12:   bf00        nop
  14:   4048f5c3    .word   0x4048f5c3

ご覧のとおり、コンパイラは への呼び出しを作成しません__aeabi_fmulが、代わりに、 にvmul.f32ある引数を に移動しr0s14に入力3.14した後に命令を作成しs15ます。乗算命令の後、呼び出し規約により、この関数の呼び出し元はそれを期待するため、s14利用可能な結果を​​元に戻します。r0

サードパーティから提供されたライブラリと考えるpi2と、soft 実装と softfp 実装の両方が同じことを行い、それらを交換可能に使用できることが理解できます。システムがそれらを提供する場合、アプリがハードウェア浮動小数点をサポートするシステムで実行されるかどうかは気にしません。これは、古いソフトウェアを新しいハードウェアで実行し続けるのに非常に役立ちました。

ただし、互換性を維持しながら、このアプローチでは、ARM レジスタと FP レジスタ間で値を移動するオーバーヘッドが発生します。これは明らかにパフォーマンスに影響し、 によって呼び出される新しい呼び出し規約によって対処さhardgccます。この新しい規則は、関数に浮動小数点引数がある場合、通常の浮動小数点レジスタとインターリーブされた浮動小数点レジスタを利用できること、および浮動小数点レジスタで浮動小数点値を返すことができることを示していますs0

スニペットをコンパイルし-c -O3 -mfloat-abi=hardてダンプすると、次のようになります。

00000000 <pi2>:
   0:   eddf 7a02   vldr    s15, [pc, #8]   ; c <pi2+0xc>
   4:   ee20 0a27   vmul.f32    s0, s0, s15
   8:   4770        bx  lr
   a:   bf00        nop
   c:   4048f5c3    .word   0x4048f5c3

レジスタが移動していないことがわかります。への引数pi2が渡されs0、コンパイラが作成したコードを入力3.14し、必要な結果を取得するためにs15使用します。vmul.f32 s0, s0, s15s0

この新しい規則の大きな問題は、コンパイラによって生成されたコードを改善すると、互換性が完全に失われてしまうことです。hard規則soft/softfpに従って構築されたアプリケーションが、ハード向けに構築されたライブラリで動作することを期待することはできません。

呼び出し規則の詳細については、ARM の Web サイトを確認してください。

于 2012-12-20T15:13:10.583 に答える