C言語とアセンブラの結合
IAR C/C++ Compiler forArm は、低レベルリソースにアクセスするためのいくつかの方法を提供します:
アセンブラだけで記述したモジュール
組み込み関数(Cの代替関数)
インラインアセンブラ
単純なインラインアセンブラが使用される傾向があります。ただし、どの方法を使用するかは慎重に選択する必要があります。
組み込み関数
コンパイラは、アセンブラ言語を必要とせずに低レベルのプロセッサ処理に直接アクセスできる定義済関数をいくつか提供しています。 これらの関数を、組み込み関数と呼びます。組み込み関数は、時間が重要なルーチンなどに便利です。
組み込み関数は、通常の関数呼び出しと変わらないように見えますが、実際にはコンパイラが認識する組み込み関数です。組み込み関数は、単一の命令か短い命令シーケンスとして、インラインコードにコンパイルされます。
使用可能な組み込み関数の詳細は、組み込み関数を参照してください。
Cとアセンブラモジュールの結合
アプリケーションの一部をアセンブラで記述し、C/C++モジュールと混在させることができます。
これは関数呼び出しとリターンの命令シーケンスでオーバヘッドになる原因になります。またコンパイラはいくつかのレジスタをスクラッチレジスタとみなします。多くの場合、外部命令によるオーバヘッドは、最適化により削 除されます。
重要な利点は、コンパイラが生成したものとアセンブラで書いたものの間で明確に定義されたインタフェースを持つことができることです。インラインアセンブラを使用する場合、インラインアセンブラの行がコンパイラによって生成されたコードを干渉しないという保証はありません。
アプリケーションで、一部をアセンブラ言語、一部を C/C++で記述する場合、いくつかの疑問点に遭遇します。
C から呼び出せるようにアセンブラコードを記述する方法は?
アセンブラコードが自分のパラメータの場所を探す方法と、呼び出し元へ値を返す方法は?
Cで記述した関数をアセンブラコードから呼び出す方法は?
アセンブラ言語で記述したコードから、Cのグローバル変数にアクセスする方法は?
アセンブラコードをデバッグする際に、デバッガでコールスタックが表示されない理由は?
最初の疑問については、Cからのアセンブラルーチンの呼び出しで説明します。次の2つの疑問については、呼び出し規約で説明します。
最後の疑問については、アセンブラコードをデバッガで実行する際、コールスタックを表示できるというのが答えです。ただし、デバッガではコールフレームについての情報が必要になります。この情報は、アセンブラソースファイルでコメントとして記述する必要があります。詳細については、呼出しフレーム情報を参照してください。
C/C++とアセンブラモジュールを混在させる望ましい方法は、Cからのアセンブラルーチンの呼び出しとC++からのアセンブラルーチンの呼び出しでそれぞれ説明しています。
インラインアセンブラ
インラインアセンブラを使用して、アセンブラ命令をC/C++ 関数に直接挿入できます。通常これは以下の必要がある場合に便利です。
Cでアクセスできないハードウェアリソースにアクセスする(つまり、SFRの定義がなかったり、適切な組み込み関数がない場合)。
速度が重要なコードを C で記述すると遅くなりすぎるため、手動でシーケンス記述する。
時間が重要なコードを C で記述するとタイミングが適切でなくなるため、手動でシーケンス記述する。。
インラインアセンブラの文は、引数が入力可能(入力オペランド)でリターン値があり(出力オペランド)、Cシンボルのリードやライトが可能(オペランドを介して)という点で、C関数に似ています。インラインアセンブラ文は、上書きされるリソース (つまり、レジスタやメモリ内のオーバライドされた値)を宣言することもできます。
制限
通常のアセンブラ言語でできることの多くが、インラインアセンブラでも可能です。違う点は以下のとおりです。
アライメントは制御できません。つまり、
DC32などのディレクティブの位置が誤ってアライメントされることがあります。32ビットモードの許可されたレジスタの別名は、
SP(R13)、LR(R14)、およびPC(R15)のみです。64ビットモードの許可されたレジスタの別名は、
IP0(X16)、IP1(X17)、FP(X29)、およびLR(X30)のみです。一般的に、アセンブラディレクティブはエラーを発生させるか、何の意味も持ちません。ただし、データ定義ディレクティブは予期したとおりに機能します。
使用されているリソース(レジスタ、メモリなど)で、Cコンパイラでも使用されているものは、オペランドまたは上書きされるリソースとして宣言する必要があります。
インラインアセンブラの文がコンパイラによって最適化されすぎないようにするには、
volatileとして宣言してください。Cシンボルにアクセスしたり、定数式を使用するにはオペランドを使用する必要があります。
オペランドの式間の依存関係によって、エラーが発生することがあります。
擬似命令
LDR Rd, =exprは、インラインアセンブラでは使用できません。
インラインアセンブラに関するリスク
オペランドや上書きされるリソースがなければ、インラインアセンブラの文には周囲のCソースコードとのインタフェースがありません。このため、インラインアセンブラコードが脆弱になります。また、将来コンパイラを更新した場合に保守面で問題が生じる可能性もあります。オペランドや上書きされるリソースのないインラインアセンブラの使用には、いくつかの制約もあります。
コンパイラによる最適化では、インライン文の効果を無視します。これらはまったく最適化されません。
実影響のないアセンブラ文の関数のインラインは、実行されません。
インラインアセンブラの文は
volatileとなり、メモリが破壊されることを暗黙的に指定しません。 つまり、コンパイラはアセンブラ文を削除しません。単純にプログラムフローの指定位置に挿入されます。挿入による前後のコードへの影響や副作用は考慮されません。たとえば、レジスタやメモリ位置が変更される場合は、それ以降のコードが正常に動作するには、インラインアセンブラ命令のシーケンス内での復元が必要になることがあります。
警告
次の例(Armモードの場合)は、オペランドおよびクラバーなしでasmキーワードを使用する場合のリスクを示します。
int Add(int term1, int term2)
{
asm("adds r0,r0,r1");
return term1;
}この例の場合:
関数
Addは、値がレジスタで渡されて戻されると想定しています。これは、関数がインライン化されている場合等は、常にそうはならない可能性がある方法です。Adds命令のsは、条件フラグが更新されることを示します。これはccクラバーオペランド を使用して指定します。それ以外の場合、コンパイラは条件フラグが変更されていないとみ なします。
したがって、オペランドや上書きされるリソースを使用しないインラインアセンブラは、極力避けるようにしてください。コンパイラは、それらにリマークを発行します。
上書きされるメモリの使い方の例
int StoreExclusive(unsigned long * location, unsigned long value)
{
int failed;
asm("strex %0,%2,[%1]"
: "=&r"(failed)
: "r"(location), "r"(value)
: "memory");
/* Note: 'strex' requires Armv6 (Arm) or Armv6T2 (THUMB) */
return failed;
}