良いコードのを生成させる方法
このページには、コンパイラが効率的なコードを生成するためのヒントが含まれています。
最適化を容易にするソースコードの記述
以下に、コンパイラでのアプリケーション最適化を改善できるプログラミングテクニックを示します。
静的/グローバル変数よりもローカル変数(自動変数とパラメータ)を使用することをお勧めします。. これは、呼び出し先関数がローカル以外の変数を変更する可能性などをオプティマイザが想定する必要があるためです。ローカル変数の使用期間が終了すると、占有されていたメモリを再利用できます。グローバルに宣言した変数は、プログラムの実行中はデータメモリを占有します。
&演算子を使用してローカル変数のアドレスを取ることは避けてください。これは、主に2つの理由で非効率です。まず、変数はメモリに配置する必要があり、プロセッサのレジスタに配置できません。そのため、コードのサイズが大きくなり、速度が低下します。次に、ローカル変数が関数呼び出しの影響を受けないとオプティマイザが想定できなくなります。グローバル変数(非静的)よりも、モジュール内でローカルな変数(staticとして宣言された変数)を使用することをお勧めします。また、頻繁にアクセスされる静的変数のアドレスを取得することは避けてください。
コンパイラによる関数のインライン化が可能です(関数インライン化を参照)。インライン化の効果を最大限にするには、複数のモジュールから呼び出される小さい関数の定義を、実装ファイルではなくヘッダファイルに配置することをお薦めします。または、複数ファイルコンパイルを使用します。詳細については、複数ファイルのコンパイルユニットを参照してください。
オペランドやclobberedリソースの指定がないインラインアセンブラは使用しないようにしてください。代わりに、可能であればSFRや組み込み関数を使用します。そうでなければ、オペランドやclobberedリソースの指定があるインラインアセンブラを使用するか、アセンブラ言語で別のモジュールを記述してください。詳細については、C言語とアセンブラの結合を参照してください。
スタックエリアとRAMメモリの節約
関数プロトタイプ
2つのスタイルのいずれかを使用して、関数の宣言と定義を行うことができます。
プロトタイプ
カーニハン&リッチーC (K&R C)
どちらのスタイルも有効なCですが、できる限りプロトタイプスタイルを使用して、関数を定義するコンパイルユニットおよびそれを使用するすべてのコンパイルユニットの両方に含まれるヘッダのpublic関数ごとに、プロトタイプ宣言を指定するようお勧めします。
コンパイラは、K&Rスタイルを使用して宣言された関数に引き渡されるパラメータに対してはチェックを実行しません。プロトタイプ宣言を使用すると、コードがより効率的になることがあります。これは、これらの関数に型変換が必要ないためです。
すべての関数定義が適切なプロトタイプスタイルを使用し、すべてのpublic関数が定義される前に宣言されていることをコンパイラで義務づけるには、[プロジェクト]>[オプション]>[C/C++コンパイラ]>[言語 1]>[プロトタイプの強制]コンパイラオプション(--require_prototypes)を使用します。
プロトタイプスタイル
プロトタイプ関数の宣言では、各パラメータの型を指定する必要があります。
int Test(char, int); /* Declaration */
int Test(char ch, int i) /* Definition */
{
return i + ch;
}カーニハン&リッチースタイル
カーニハン&リッチースタイル(標準C以前)では、プロトタイプ化された関数を宣言することはできません。その代わりに、空のパラメータリストを関数宣言で使用します。また、定義の記述が異なります。
以下に例を示します。
int Test(); /* Declaration */
int Test(ch, i) /* Definition */
char ch;
int i;
{
return i + ch;
}整数型とビット反転
場合によっては、整数型とその変換の規則が、混乱を招く動作の原因となることがあります。異なるサイズの型や論理演算(特にビット否定)が関係する代入文や条件文(評価式)に注意する必要があります。この場合、types型には定数型も含まれます。
場合によっては、警告が表示されることがあります (たとえば、定数条件や無意味な比較など)。また、予想とは異なる結果が表示されることもあります。コンパイラが定数の条件文のインスタンスを特定するために最適化を使用する場合などは、より高水準の最適化でのみ、コンパイラがワーニングを生成することがあります。以下の例では、8ビット文字、32ビット整数、2の補数を想定しています。
void F1(unsigned char c1)
{
if (c1 == ~0x80)
;
}この場合、評価結果は常にfalseになります。右辺の、0x80は0x00000080、~0x00000080は0xFFFFFF7Fになります。左側では、c1は範囲0–255にある8 ビットの符号なし文字で、0xFFFFFF7Fと同等になることはありません。また、負の値にもならないため、汎整数拡張された値の上位24ビットが設定されることはありません。
同時にアクセスされる変数の保護
非同期でアクセスされる変数(割り込みルーチンからアクセスされる変数、独立したスレッドで実行しているコードからアクセスされる変数など)は、適切にマークして保護する必要があります。例外は、常に読み込み専用の変数のみです。
変数を適切にマークするには、volatileキーワードを使用します。このキーワードは、変数が他のスレッドから変更される可能性があることをコンパイラに示します。すると、コンパイラでは、変数の最適化を回避(レジスタ内の変数を追跡するなど)し、変数へのライトを遅延させず、ソースコードで指定された回数だけ変数にアクセスするように注意します。
volatile型修飾子およびvolatileオプションのアクセス規則について詳しくは、オブジェクトのvolatile宣言を参照してください。
特殊機能レジスタへのアクセス
IAR製品のインストール内容には、Armデバイス用の専用ヘッダファイルがいくつか付属しています。ヘッダファイルは、iodevice.hという形式で命名され、プロセッサ固有の特殊機能レジスタ(SFR)を定義します。
注記
各ヘッダファイルには、コンパイラが使用するセクションが1つ、アセンブラが使用するセクションが1つ含まれています。
ビットフィールド付きのSFRが、ヘッダファイルで定義されています。以下にioks32c5000a.hの例を示します。
__no_init volatile union
{
unsigned short mwctl2;
struct
{
unsigned short edr : 1;
unsigned short edw : 1;
unsigned short lee : 2;
unsigned short lemd : 2;
unsigned short lepl : 2;
} mwctl2bit;
} @ 0x1000;
/* By including the appropriate include file in your code,
* it is possible to access either the whole register or any
* individual bit (or bitfields) from C code as follows.
*/
void Test()
{
/* Whole register access */
mwctl2 = 0x1234;
/* Bitfield accesses */
mwctl2bit.edw = 1;
mwctl2bit.lepl = 3;
}
他のArm デバイス用に新しいヘッダファイルを作成する場合には、ヘッダファイルをテンプレートとして使用することもできます。
Cおよびアセンブラオブジェクト間での値の受渡し
以下の例は、Cソースコードでアセンブラをインライン化して、特殊な目的のレジスタから値を設定および取得する方法を示しています。
static unsigned long get_APSR( void )
{
unsigned long value;
asm volatile( "MRS %0, APSR" : "=r"(value) );
return value;
}
static void set_APSR( unsigned long value)
{
asm volatile( "MSR APSR, %0" :: "r"(value) );
}汎用レジスタは、特殊な目的のレジスタAPSRの値の取得および設定に使用されます。他の特殊な目的のレジスタおよび特殊な命令へのアクセスにも同じ方法を使用できます。
インラインアセンブラの詳細については、インラインアセンブラを参照してください。
非初期化変数
通常は、アプリケーション起動時に、ランタイム環境がすべてのグローバル変数と静的変数を初期化します。
コンパイラは、__no_init型修飾子により、初期化されない変数の宣言をサポートしています。このような変数は、キーワードとして指定することや、#pragma object_attributeディレクティブを使用して指定することができます。コンパイラは、このような変数を個別のセクションに配置します。
__no_initを使用した場合、constキーワードは、リードオンリーメモリにオブジェクトが格納されるのではなく、オブジェクトがリードオンリーであることを意味します。__no_initオブジェクトに初期値を指定することはできません。
__no_initキーワードを使用して宣言した変数は、大きな入力バッファとして使用することや、アプリケーション終了後も内容を保持する特殊なRAMにマッピングすることができます。
詳細については、__no_initを参照してください。
注記
このキーワードを使用するには、言語拡張を有効にする必要があります(-eを参照)。詳細については、object_attributeを参照してください。