ホーム > デベロッパ > RealViewコード生成ツールの最適化 −BREW 技術情報−

RealViewコード生成ツール( ARM RealView Compilation Tools for BREW V1.2 ) の最適化 −BREW 技術情報−

RealViewコード生成ツール( ARM RealView Compilation Tools for BREW V1.2 ) の最適化 −BREW 技術情報−

速くてコンパクトな BREW アプリのコードを生成するための、RealView コード生成ツール( ARM RealView Compilation Tools for BREW、略称 "RVCTB" ) の最適化について解説します。

なお、RealView Compilation Tools for BREW は日本国内では株式会社ソフィアシステムズ様のこちらのサイトから入手可能です。

クラス定数の例 ( リスト 1 )

▼リスト1 クラス定数の例
class my_rect {
private:
    int _x;
    int _y;
    int _width;
    int _height;
public:
    my_rect(void);
    my_rect(int x, int y, int width, int height);
};

my_rect::my_rect(void)
{
}

my_rect::my_rect(int x, int y, int width, int height)
    : _x(x), _y(y), _width(width), _height(height)
{
}

int func(my_rect const &rect)
{
    return reinterpret_cast<int>(&rect);
}

int main(void)
{
    my_rect const rect(0, 0, 100, 100);

    return func(rect);
}

リスト 1 は、矩形クラスを利用するコードです。単純ですが、無駄なコードが含まれます。

図 1 はリスト 1 のコンパイル結果です。( コンパイルオプション : RVCTB コンパイラの標準設定 )

図 1 リスト 1 のコンパイル結果
; generated by ARM C++ Compiler, ADS1.2 [Build 848]

; commandline [-O2 -S -apcs /ropi -fs "-IC:\Program Files\ARM\RVCT_BREWv1_2\INCLUDE"]
                          CODE32

                          AREA ||.text||, CODE, READONLY

                  __ct__7my_rectFv PROC ; my_rect::my_rect()
;;;13     my_rect::my_rect(void)
;;;14     {
000000  e3500000          CMP      r0,#0
000004  03a00010          MOVEQ    r0,#0x10
000008  0afffffe          BEQ      __nw__FUi
00000c  e1a0f00e          MOV      pc,lr
;;;15       return;
;;;16     }
                          ENDP

                  __ct__7my_rectFiN31 PROC ; my_rect::my_rect(int, int, int, int)
;;;18     my_rect::my_rect(int x, int y, int width, int height)
;;;19       : _x(x), _y(y), _width(width), _height(height)
000010  e92d40f8          STMFD    sp!,{r3-r7,lr}
000014  e59d4018          LDR      r4,[sp,#0x18]
000018  e1a07003          MOV      r7,r3
00001c  e1a06002          MOV      r6,r2
000020  e1a05001          MOV      r5,r1
000024  e3500000          CMP      r0,#0
000028  1a000003          BNE      |L1.60|
00002c  e3a00010          MOV      r0,#0x10
000030  ebfffffe          BL       __nw__FUi
000034  e3500000          CMP      r0,#0
000038  0a000001          BEQ      |L1.68|
;;;20     {
                  |L1.60|
00003c  e580400c          STR      r4,[r0,#0xc]
000040  e88000e0          STMIA    r0,{r5-r7}
;;;21       return;
                  |L1.68|
000044  e8bd80f8          LDMFD    sp!,{r3-r7,pc}
;;;22     }
                          ENDP

                  func__FRC7my_rect PROC ; func(const my_rect&)
;;;24     int func(my_rect const& rect)
;;;25     {
000048  e1a0f00e          MOV      pc,lr
;;;26       return reinterpret_cast<int>(&rect);
;;;27     }
                          ENDP

                  main PROC
;;;29     int main(void)
;;;30     {
00004c  e92d400f          STMFD    sp!,{r0-r3,lr}
000050  e3a00000          MOV      r0,#0                 ;20
000054  e58d0000          STR      r0,[sp,#0]            ;20
000058  e58d0004          STR      r0,[sp,#4]            ;20
00005c  e3a00064          MOV      r0,#0x64              ;20
000060  e58d0008          STR      r0,[sp,#8]            ;20
000064  e58d000c          STR      r0,[sp,#0xc]          ;20
000068  e1a0000d          MOV      r0,sp                 ;26
;;;31       my_rect const rect(0, 0, 100, 100);
;;;32
;;;33       return func(rect);
00006c  e28dd010          ADD      sp,sp,#0x10
000070  e49df004          LDR      pc,[sp],#4
;;;34     }
                          ENDP

        END

C++では、クラスを定数と宣言しても コンストラクタ my_rect const rect(0, 0, 100, 100) が実行時に呼び出されます。

POD 構造体定数の例 ( リスト 2 )

▼リスト2 POD 構造体定数の例
struct pod_rect {
    int x;
    int y;
    int width;
    int height;
};

int func(pod_rect const& rect)
{
    return reinterpret_cast<int>(&rect);
}

int main(void)
{
    pod_rect const rect = {0, 0, 100, 100};

    return func(rect);
}
図 2 リスト 2 のコンパイル結果
; generated by ARM C++ Compiler, ADS1.2 [Build 848]

; commandline [-O2 -S -apcs /ropi -fs "-IC:\Program Files\ARM\RVCT_BREWv1_2\INCLUDE"]
                          CODE32

                          AREA ||.text||, CODE, READONLY

                  func__FRC8pod_rect PROC ; func(const pod_rect&)
;;;8      int func(pod_rect const& rect)
;;;9      {
000000  e1a0f00e          MOV      pc,lr
;;;10       return reinterpret_cast<int>(&rect);
;;;11     }
                          ENDP

                  main PROC
;;;13     int main(void)
;;;14     {
000004  e92d400f          STMFD    sp!,{r0-r3,lr}
;;;15       pod_rect const rect = {0, 0, 100, 100};
000008  e59f1014          LDR      r1,|L1.36|
00000c  e08f1001          ADD      r1,pc,r1
000010  e1a0000d          MOV      r0,sp
000014  e891500c          LDMIA    r1,{r2,r3,r12,lr}
000018  e880500c          STMIA    r0,{r2,r3,r12,lr}
;;;16
;;;17       return func(rect);
00001c  e28dd010          ADD      sp,sp,#0x10
000020  e49df004          LDR      pc,[sp],#4
                  |L1.36|
000024  00000010          DCD      ||.constdata$1|| + 16 - {PC}
;;;18     }
                          ENDP

                          AREA ||.constdata||, DATA, READONLY, ALIGN=2

                  ||.constdata$1||
                          DCD      0x00000000
                          DCD      0x00000000
                          DCD      0x00000064
                          DCD      0x00000064

        END

こちらの方が実行速度とサイズの面で優れています(リスト2、図2)。

C 言語互換の POD 型の初期化をすると、定数は定数領域に格納されます。
※この時、コンストラクタは定義できません。

POD 型初期化をサポートしたクラスの例 ( リスト 3 )

▼リスト3 POD 型初期化をサポートしたクラス
class my_rect {
public:
    struct atom {
        int x;
        int y;
        int width;
        int height;
        operator my_rect&(void);
        operator my_rect const&(void) const;
    };

private:
    int _x;
    int _y;
    int _width;
    int _height;

public:
    my_rect(void);
    my_rect(int x, int y, int width, int height);
};

my_rect::my_rect(void)
{
}

my_rect::my_rect(int x, int y, int width, int height)
    : _x(x), _y(y), _width(width), _height(height)
{
}

inline my_rect::atom::operator my_rect&(void)
{
    return *reinterpret_cast<my_rect*>(this);
}

inline my_rect::atom::operator my_rect const&(void) const
{
    return *reinterpret_cast<my_rect const*>(this);
}

int func(my_rect const& rect)
{
    return reinterpret_cast<int>(&rect);
}

int main(void)
{
    my_rect::atom const rect = {0, 0, 100, 100};

    return func(rect);
}

これは矩形クラスとそれに対応する POD 型構造体をセットで定義し、初期化のみ POD 型で行う方法です。

atom 構造体の型変換演算子 my_rect&() により、POD 型構造体変数 rect は my_rect クラスの変数として扱えます。

型変換演算子の内部で reinterpret_cast 演算子を使います。( クラス型と POD 構造体型のメンバ変数の配置の互換性が前提です。) クラス側にコンストラクタ、デストラクタ、仮想関数、多重継承や仮想継承が無ければ、POD 型と互換性があります。

図3◎リスト3 のコンパイル結果
; generated by ARM C++ Compiler, ADS1.2 [Build 848]

; commandline [-O2 -S -apcs /ropi -fs "-IC:\Program Files\ARM\RVCT_BREWv1_2\INCLUDE"]
                          CODE32

                          AREA ||.text||, CODE, READONLY

                  __ct__7my_rectFv PROC ; my_rect::my_rect()
;;;23     my_rect::my_rect(void)
;;;24     {
000000  e3500000          CMP      r0,#0
000004  03a00010          MOVEQ    r0,#0x10
000008  0afffffe          BEQ      __nw__FUi
00000c  e1a0f00e          MOV      pc,lr
;;;25       return;
;;;26     }
                          ENDP

                  __ct__7my_rectFiN31 PROC ; my_rect::my_rect(int, int, int, int)
;;;28     my_rect::my_rect(int x, int y, int width, int height)
;;;29       : _x(x), _y(y), _width(width), _height(height)
000010  e92d40f8          STMFD    sp!,{r3-r7,lr}
000014  e59d4018          LDR      r4,[sp,#0x18]
000018  e1a07003          MOV      r7,r3
00001c  e1a06002          MOV      r6,r2
000020  e1a05001          MOV      r5,r1
000024  e3500000          CMP      r0,#0
000028  1a000003          BNE      |L1.60|
00002c  e3a00010          MOV      r0,#0x10
000030  ebfffffe          BL       __nw__FUi
000034  e3500000          CMP      r0,#0
000038  0a000001          BEQ      |L1.68|
;;;30     {
                  |L1.60|
00003c  e580400c          STR      r4,[r0,#0xc]
000040  e88000e0          STMIA    r0,{r5-r7}
;;;31       return;
                  |L1.68|
000044  e8bd80f8          LDMFD    sp!,{r3-r7,pc}
;;;32     }
                          ENDP

                  func__FRC7my_rect PROC ; func(const my_rect&)
;;;44     int func(my_rect const& rect)
;;;45     {
000048  e1a0f00e          MOV      pc,lr
;;;46       return reinterpret_cast<int>(&rect);
;;;47     }
                          ENDP

                  main PROC
;;;49     int main(void)
;;;50     {
00004c  e92d400f          STMFD    sp!,{r0-r3,lr}
;;;51       my_rect::atom const rect = {0, 0, 100, 100};
000050  e59f1014          LDR      r1,|L1.108|
000054  e08f1001          ADD      r1,pc,r1
000058  e1a0000d          MOV      r0,sp
00005c  e891500c          LDMIA    r1,{r2,r3,r12,lr}
000060  e880500c          STMIA    r0,{r2,r3,r12,lr}
;;;52
;;;53       return func(rect);
000064  e28dd010          ADD      sp,sp,#0x10
000068  e49df004          LDR      pc,[sp],#4
                  |L1.108|
00006c  00000010          DCD      ||.constdata$1|| + 16 - {PC}
;;;54     }
                          ENDP

                          AREA ||.constdata||, DATA, READONLY, ALIGN=2

                  ||.constdata$1||
                          DCD      0x00000000
                          DCD      0x00000000
                          DCD      0x00000064
                          DCD      0x00000064

        END

main 関数の中の命令数が減っています。( ARM プロセッサは 1 命令 1 サイクルではないことに注意 )

スタティック定数を利用する例 ( リスト 4 )

コンストラクタが起動されるので、クラス定数のインスタンス変数は定数のスタティック変数とみなせません。 ( 実行時に値が変化しないスタティック変数は BREW で使えます。) POD 型初期化であれば、クラス定数のインスタンス変数を定数のスタティック変数とみなせます。( リスト 4 )

▼リスト4 スタティック定数を利用する例
int main(void)
{
    static my_rect::atom const rect = {0, 0, 100, 100};

    return func(rect);
}

図 4 はコンパイル結果です。static キーワードがあると、main 関数のコードが大幅に削減されるのが分かります。

図 4 リスト 4 のコンパイル結果 ( main の部分のみを抜粋 )
; generated by ARM C++ Compiler, ADS1.2 [Build 848]

; commandline [-O2 -S -apcs /ropi -fs "-IC:\Program Files\ARM\RVCT_BREWv1_2\INCLUDE"]
                          CODE32

                          AREA ||.text||, CODE, READONLY

                  __ct__7my_rectFv PROC ; my_rect::my_rect()
;;;23     my_rect::my_rect(void)
;;;24     {
000000  e3500000          CMP      r0,#0
000004  03a00010          MOVEQ    r0,#0x10
000008  0afffffe          BEQ      __nw__FUi
00000c  e1a0f00e          MOV      pc,lr
;;;25       return;
;;;26     }
                          ENDP

                  __ct__7my_rectFiN31 PROC ; my_rect::my_rect(int, int, int, int)
;;;28     my_rect::my_rect(int x, int y, int width, int height)
;;;29       : _x(x), _y(y), _width(width), _height(height)
000010  e92d40f8          STMFD    sp!,{r3-r7,lr}
000014  e59d4018          LDR      r4,[sp,#0x18]
000018  e1a07003          MOV      r7,r3
00001c  e1a06002          MOV      r6,r2
000020  e1a05001          MOV      r5,r1
000024  e3500000          CMP      r0,#0
000028  1a000003          BNE      |L1.60|
00002c  e3a00010          MOV      r0,#0x10
000030  ebfffffe          BL       __nw__FUi
000034  e3500000          CMP      r0,#0
000038  0a000001          BEQ      |L1.68|
;;;30     {
                  |L1.60|
00003c  e580400c          STR      r4,[r0,#0xc]
000040  e88000e0          STMIA    r0,{r5-r7}
;;;31       return;
                  |L1.68|
000044  e8bd80f8          LDMFD    sp!,{r3-r7,pc}
;;;32     }
                          ENDP

                  func__FRC7my_rect PROC ; func(const my_rect&)
;;;44     int func(my_rect const& rect)
;;;45     {
000048  e1a0f00e          MOV      pc,lr
;;;46       return reinterpret_cast<int>(&rect);
;;;47     }
                          ENDP

                  main PROC
;;;49     int main(void)
;;;50     {
00004c  e59f0004          LDR      r0,|L1.88|
000050  e08f0000          ADD      r0,pc,r0              ;41
;;;51       static my_rect::atom const rect = {0, 0, 100, 100};
;;;52
;;;53       return func(rect);
000054  e1a0f00e          MOV      pc,lr
                  |L1.88|
000058  00000000          DCD      ||.constdata$1|| - {PC}
;;;54     }
                          ENDP

                          AREA ||.constdata||, DATA, READONLY, ALIGN=2

                  ||.constdata$1||
                  ||rect@main_0||
                          DCD      0x00000000
                          DCD      0x00000000
                          DCD      0x00000064
                          DCD      0x00000064

        END

※ 以上のノウハウはクラス定数だけでなく、文字列定数にも適用できます。