速くてコンパクトな BREW アプリのコードを生成するための、RealView コード生成ツール( ARM RealView Compilation Tools for BREW、略称 "RVCTB" ) の最適化について解説します。
なお、RealView Compilation Tools for BREW は日本国内では株式会社ソフィアシステムズ様のこちらのサイトから入手可能です。
RealView Compilation Tools for BREW (ARM Ltd.)
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 )
BREW C++ 開発の実際 : 利用可能なスタティック変数
▼リスト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
※ 以上のノウハウはクラス定数だけでなく、文字列定数にも適用できます。