5.11 C および C++ のスタックの使用

C と C++ では、いずれもスタックが多く使用されます。

たとえば、スタックは以下の項目を格納します。
  • 関数の復帰アドレス。
  • 保持する必要があるレジスタ(ARM アーキテクチャ向けプロシージャコール標準(AAPCS)によって決定される)。たとえば、レジスタの内容がエントリからサブルーチンに保存される場合。
  • ローカル変数(ローカル配列を含む)、構造体、共用体、およびクラス(C++ の場合)。
以下のようなスタック消費量は明らかではありません。
  • ローカル整数または浮動小数点変数は多くなるとスタックメモリに配置されます(つまり、レジストリには配置されません)。
  • 構造体は通常スタックに割り当てられます。4 バイトの倍数にパディングされる sizeof(struct) に等しいスペースがスタックに予約されます。代わりに、コンパイラは構造体のレジスタへの割り当てを試みます。
  • 配列サイズがコンパイル時に確認されている場合、コンパイラはスタックにメモリを割り当てます。繰り返しになりますが、4 バイトの倍数にパディングされる sizeof(struct) に等しいスペースがスタックに予約されます。

    可変長配列のメモリはヒープ上でランタイム時に割り当てられます。
  • いくつかの最適化によって、新しい一時変数を導入し、中間結果を保持できます。最適化には、以下のものがあります。CSE 削除、ライブ範囲分割および構造体分割。コンパイラはこれらの一時変数のレジスタへの割り当てを試みます。そうでない場合、スタックに配置されます。
  • 通常、16 ビットでエンコードされた Thumb 命令のみをサポートするプロセッサ用にコンパイルされたコードは、ARM コードおよび 32 ビットでエンコードされた Thumb 命令をサポートするプロセッサ用にコンパイルされたコードと比較すると、スタックをより多く利用できます。これは、16 ビットでエンコードされた Thumb 命令では、割り当て用に使用できるレジスタは 8 個のみですが、ARM コードおよび 32 ビットでエンコードされた Thumb 命令では、14 個使用できるためです。
  • 型、サイズ、および順序によって異なりますが、AAPCS では、いくつかの関数引数はレジスタの代わりにスタックを通じて渡されることが必要です。

スタック消費量の推定方法

スタック使用は、コードに依存し、プログラムが実行時に使用するコードパスによって実行間で異なるため、推定が困難です。ただし、スタックの消費量を手動で推定することは可能です。これには、以下の方法があります。
  • --callgraph でリンクすることによって、静的コールグラフを生成します。これにより、スタックの消費量をはじめとした、すべての関数に関する情報が示されます。
  • --info=stack または --info=summarystack でリンクすることによって、すべてのグローバルシンボルのスタック消費量のリストを表示します。
  • デバッガを使用して、スタック内の最後の使用可能な場所にウォッチポイントを設定し、そのウォッチポイントがヒットされるかどうかを調べます。

    リアルタイムシステムモデル(RTSM)、DS-5 デバッガ、または RealView Debugger などのデバッグモニタの下でプログラムを実行すると、すべての命令で注目アドレスがチェックされるため、パフォーマンスが著しく低下します。DSTREAM または RealView ICE および RealView Trace を使用するとそのようなことはありません。
  • デバッガの使用、および
    1. 想定を大幅に上回るサイズのメモリ領域をスタックに割り当てます。
    2. 0xDEADDEAD などの既知の値のコピーでスタックスペースを埋めます。
    3. アプリケーションまたはその固定部分を実行します。テスト実行では、スタックスペースの可能な限り多くの部分を使用するようにして下さい。たとえば、最も深いネスト構造の関数呼び出しと、静的解析で検出されたワーストケースパスを実行するとします。必要に応じて割り込みを生成し、割り込みがスタックトレースに記録されるようにします。
    4. アプリケーションの実行が完了した後で、メモリのスタックスペースを検証して、上書きされた既知の値の数を調べます。スペースの使用済み部分にはガベージがあり、残りの部分には既知の値があります。
    5. ガベージ値の数を数えて、4 で乗算し、サイズをバイトで表します。
    計算の結果から、スタックのサイズがどの程度増大したかがバイト単位で示されます。
  • RTSM を使用して、メモリ内でスタックのすぐ下に、アクセスが許可されていないメモリ領域をマップファイルを用いて定義します。アクセスが禁止された領域にスタックがオーバーフローすると、データアボートが発生し、デバッガによってトラップされる可能性があります。

スタック消費量を減らす方法

一般に、以下の方法でプログラムのスタック要件を引き下げることができます。
  • 必要とする変数の数が少ない小さな関数を記述する。
  • 大きなローカル構造体または配列を使用しない。
  • 別のアルゴリズムなどを使用して、再帰を回避する。
  • 関数の任意のポイントで使用される変数の数を最小にする。
  • 別個の有効範囲で使用されるメモリをオーバーラップすることによって、C のブロック範囲を使用し、必要な場所でのみ変数を宣言する。
C のブロック範囲を使用することには、必要な場所でのみ変数を宣言することが含まれます。別個の有効範囲で必要とされるメモリをオーバーラップすることによってスタックの使用を最小にすることができます。

高速(ゼロウェイト状態)、オンチップ、32 ビット RAM でスタックを検索することにより、コードのパフォーマンスが最適化されます。ARM(LDMFD および STMFD)および Thumb(PUSH および POP)スタックアクセス命令は、スタックをオンまたはオフにする多くの 32 ビットのレジスタをプッシュおよびポップします。スタックが 32 ビットのメモリにある場合、各レジスタアクセスは 1 サイクルを使用します。ただし、スタックが 16 ビットのメモリにある場合、各レジスタアクセスは 2 サイクルを使用し、パフォーマンス全体を軽減します。
関連情報
『DS-5 スタートガイド』、『ARM DS-5の製品概要』、『About Fixed Virtual Platform (FVP)』
ARM DS-5:デバッガの使用
ARM DS-5 EB FVP リファレンスガイド
固定仮想プラットフォーム VE および MPS FVP リファレンスガイド
『Procedure Call Standard for the ARM Architecture』
--info=topic[,topic,...] fromelf オプション
--info=topic[,topic,...] リンカオプション
--callgraph、--no_callgraph リンカオプション
非機密扱いPDF file icon PDF 版ARM DUI0472LJ
Copyright © 2010-2015 ARM.All rights reserved.