ホーム > デベロッパ > BREW で浮動小数点演算を使用する方法 −BREW 技術情報−

BREW での浮動小数点演算と数学関数 −BREW 技術情報−

BREW での浮動小数点演算と数学関数 −BREW 技術情報−

問題

BREW では、標準的な浮動小数点演算が使えません。浮動小数点演算は BREW ヘルパ関数に置き換えなくてはいけません。

また、double 型だけのサポートで、float 型は使えません。数学関数もほとんどサポートされていません。

原因

BREW 用に SoftFP 演算を用いたコンパイルをすると、ARM コンパイラは浮動小数点演算をすべてコンパイラ組込み関数に置き換えます。

組込み関数は内部で様々なライブラリをリンクし、そのなかにはスタティック変数を使います。しかし、BREW ではスタティック変数は使えません。

また、BREW アプリには main() 関数がないので、標準ライブラリの初期化コードが自動的に呼び出されません。

目標

次のような浮動小数点演算コードが記述できるようにする。

float a = 1.0;
float b = 2.0;
float c;
int i;

c = sin(a) + cos(b);
i = c * 100.05;

解決策

ARM コンパイラは、浮動小数点演算をARM コンパイラ付属の rt_fp.h に定義される ARM コンパイラ組込み関数に置き換えます。

/*
 * Single-precision arithmetic routines.
 */
extern __softfp float _fadd(float, float);
extern __softfp float _fsub(float, float);
extern __softfp float _fmul(float, float);
extern __softfp float _fdiv(float, float);
...etc.

上記組込み関数を使うために前提となる _fp_init() は、アプリ起動時に呼び出される AEEMod_Load() で呼び出されるようにします。

/*
 * Call this before using any fplib routines, if you're trying to
 * use fplib on the bare metal.
 */
extern void _fp_init(void);

_fp_init() を呼び出すように変更したアプリをコンパイルしリンクするとさまざまな組込み関数がリンクされます。 その中の以下の 3 つの関数が、スタティック変数や SWI 命令を使うため、 BREW アプリは浮動小数点演算が使えません。

<rt_misc.h>
/*
 * Redefine this to replace the library's entire signal handling
 * mechanism in the most efficient possible way. The default
 * implementation of this is what calls __raise (above).
 */
void __rt_raise(int /*sig*/, int /*type*/);

<errno.h>
extern __pure volatile int *__rt_errno_addr(void);

<rt_fp.h>
/*
 * This returns a pointer to the FP status word, when it's stored
 * in memory.
 */
extern unsigned *__rt_fp_status_addr(void);

この問題の解決策には、C 言語による方法とアセンブラのよる方法があります。( C 言語を使う方法が推奨されます。 )

上記関数のうちの 2 つは、組込み関数が内部で使う変数プールのアドレスを返すことを要求します。変数プールを何処に配置するかで実装の方法が異なります。

C 言語による __rt_raise 関数のリターゲット

C 言語の場合、変数プールを AEEApplet 構造体の一部に確保して、そのアドレスを返却するように関数を実装します。

関数が呼ばれたとき、AEEApplet 構造体のアドレスを取得するために、GETAPPINSTANCE() ヘルパ関数を呼び出します。

#if defined __arm
#include <rt_misc.h >
#include <errno.h >
#include <rt_fp.h >
#endif

#if defined __arm
#pragma import(__use_no_semihosting_swi)
#endif

typedef struct SoftFPApplet {
    AEEApplet aee;
#if defined __arm
    signed int volatile _errno;
    unsigned int _fpstatus;
#endif
} SoftFPApplet;

#if defined __arm
void __rt_raise(signed int signal, signed int type)
{
    return;
}

signed int volatile* __rt_errno_addr(void)
{
    SoftFPApplet* ap = (SoftFPApplet*)GETAPPINSTANCE();

    return(&ap->_errno);
}

unsigned int* __rt_fp_status_addr(void)
{
    SoftFPApplet* ap = (SoftFPApplet*)GETAPPINSTANCE();

    return(&ap->_fpstatus);
}
#endif

int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * po,void ** ppObj)
{
    *ppObj = NULL;

    if (ClsId == AEECLSID_SOFTFP) {
        if (AEEApplet_New(sizeof(SoftFPApplet), ClsId, pIShell, po, (IApplet**)ppObj,
            (AEEHANDLER)SoftFP_HandleEvent, NULL) == TRUE) {

#if defined __arm
            _fp_init();
#endif

            // Add your code here .....

            return(AEE_SUCCESS);
        }
    }
    return(EFAILED);
}
アセンブラによる __rt_raise 関数のリターゲット

アセンブラの場合、変数プールはアプリモジュールの一部をスタティック変数領域として使います。 このとき、BREW アプリが ROPI ( Read Only Position Independent ) であることを無視します。 そのため、実際にはリードオンリーメモリなのに書き込まれるかもしれません。 従って、この方法は携帯電話によっては使えない可能性があります。

    AREA ||i.__rt_raise||, CODE, READONLY

    EXPORT __rt_raise

__rt_raise
    BX          lr


    AREA ||i.__rt_errno_addr||, CODE, READONLY

    EXPORT __rt_errno_addr

__rt_errno_addr
    LDR         r0, table_errno_addr
    ADD         r0, pc, r0
    BX          lr
table_errno_addr
    DCD         errno_addr - {PC}


    AREA ||i.__rt_fp_status_addr||, CODE, READONLY

    EXPORT __rt_fp_status_addr

__rt_fp_status_addr
    LDR         r0, table_status_addr
    ADD         r0, pc, r0
    BX          lr
table_status_addr
    DCD         status_addr - {PC}


    AREA ||.constdata||, DATA, READONLY

errno_addr
    DCD         0x00000000
status_addr
    DCD         0x00000000

    END

以上のテクニックで、BREW で標準的な浮動小数点演算が利用可能になります。

SophiaFramework では、デフォルトで標準的な浮動小数点演算が利用可能です。