これらの重要なセクションにはいくつかの潜在的な問題があります。これらすべてに警告と解決策がありますが、要約としては:
- 最適化またはその他のランダムな理由により、コンパイラがこれらのマクロ間でコードを移動することを妨げるものはありません。
- それらは、コンパイラーがインラインアセンブリーがそのままにすることを期待しているプロセッサー状態の一部を保存および復元します(他に指示がない限り)。
- シーケンスの途中で割り込みが発生し、読み取り時と書き込み時の状態が変わるのを妨げるものは何もありません。
まず、コンパイラのメモリバリアが必要です。GCCはこれらをclobberとして実装します。基本的に、これはコンパイラに「いいえ、メモリアクセスの結果に影響を与える可能性があるため、このインラインアセンブリ間でメモリアクセスを移動することはできません」と伝える方法です。具体的には、開始マクロと終了マクロの両方で、両方"memory"
と"cc"
clobber が必要です。これらは、コンパイラがメモリアクセスを持っている可能性があることを知っているため、インラインアセンブリに対して他のこと(関数呼び出しなど)が並べ替えられるのを防ぎます。クローバーを使用したインラインアセンブリ全体の条件コードレジスタでGCC for ARMのホールド状態を見てきました"memory"
ので、間違いなく"cc"
クローバーが必要です。
第二に、これらの重要なセクションは、割り込みが有効になっているかどうかだけでなく、はるかに多くを保存および復元します。具体的には、CPSR(現在のプログラムステータスレジスタ)の大部分を保存および復元しています(リンクはCortex-R4用です。A9の素敵な図が見つからなかったためですが、同じである必要があります)。状態の各部分を実際に変更できる微妙な制限がありますが、ここでは必要以上です。
とりわけ、これには条件コード(cmp
後続の条件付き命令が結果に作用できるように命令の結果が保存される場所)が含まれます。コンパイラーはこれによって間違いなく混乱するでしょう。これは"cc"
、前述のようにclobberを使用して簡単に解決できます。ただし、これによりコードが毎回失敗するため、問題が発生しているようには見えません。しかし、ややこしい時限爆弾のように、ランダムな他のコードを変更すると、コンパイラーが少し違うことをする可能性があります。
これは、Thumb条件付き実行の実装に使用されるITビットの保存/復元も試みます。Thumbコードを実行したことがない場合、これは重要ではありません。私はGCCのインラインアセンブリがITビットをどのように扱うかを理解していませんが、それはそうしないと結論付けています。つまり、コンパイラはインラインアセンブリをITブロックに入れてはならず、アセンブリが常にITブロックの外側で終了することを期待しています。GCCがこれらの仮定に違反するコードを生成するのを見たことはありませんし、かなり最適化されたかなり複雑なインラインアセンブリを行ったので、それらが成り立つと確信しています。これは、おそらく実際にITビットを変更しようとはしないことを意味します。この場合、すべてが正常です。これらのビットを変更しようとすることは、「アーキテクチャ上予測不能」として分類されます。、すべての種類の悪いことをすることができますが、おそらく何もしません。
保存/復元されるビットの最後のカテゴリ(実際に割り込みを無効にするものを除く)はモードビットです。これらはおそらく変更されないため、おそらく重要ではありませんが、意図的にモードを変更するコードがある場合、これらの割り込みセクションは問題を引き起こす可能性があります。特権モードとユーザーモードを切り替えることが、これを行う唯一のケースです。
第三には、間CPSRの他の部分変更からの割り込みを防止することは何もありませんMRS
し、MSR
中をARM_INT_LOCK
。そのような変更は上書きされる可能性があります。ほとんどの合理的なシステムでは、非同期割り込みは、割り込み対象のコード(CPSRを含む)の状態を変更しません。そうした場合、コードが何をするのかを推論するのが非常に難しくなります。ただし、それは可能です(FIQ無効ビットを変更することは私には最もありそうです)。
私が指摘したすべての潜在的な問題に対処する方法でこれらを実装する方法は次のとおりです。
#define ARM_INT_KEY_TYPE unsigned int
#define ARM_INT_LOCK(key_) \
asm volatile(\
"mrs %[key], cpsr\n\t"\
"ands %[key], %[key], #0xC0\n\t"\
"cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
"tst %[key], #0x40\n\t"\
"beq 0f\n\t"\
"cpsie f\n\t"\
"0: tst %[key], #0x80\n\t"\
"beq 1f\n\t"\
"cpsie i\n\t"
"1:\n\t" :: [key]"r" (key_) : "memory", "cc")
-mcpu=cortex-a9
少なくともいくつかのGCCバージョン(私のものなど)がデフォルトで、cpsie
およびをサポートしない古いARM CPUにデフォルト設定されているため、必ずコンパイルしてくださいcpsid
。
私は使用ands
だけの代わりand
にARM_INT_LOCK
、これはThumbコードで使用されている場合、それは16ビット命令ですので。"cc"
クロバーは、とにかく必要なので、厳密にパフォーマンス/コードサイズの利点です。
0
および1
は、参照用のローカルラベルです。
これらは、バージョンと同じように使用できるはずです。これARM_INT_LOCK
は元のものと同じくらい高速/小型です。残念ながら、ARM_INT_UNLOCK
ほとんど指示がない限りどこでも安全に行う方法を思いつきませんでした。
システムでIRQおよびFIQを無効にするタイミングに制約がある場合、これを簡略化できます。彼らは常に無効になって一緒にいる場合たとえば、あなたは一つに組み合わせることができcbz
+ cpsie if
このように:
#define ARM_INT_UNLOCK(key_) asm volatile (\
"cbz %[key], 0f\n\t"\
"cpsie if\n\t"\
"0:\n\t" :: [key]"r" (key_) : "memory", "cc")
あるいは、FIQをまったく気にしない場合は、FIQを完全に有効化/無効化するだけの場合と同様です。
あなたが他のこと何も知らない場合は、これまで、あなたはまた、使用することは、両方を除いて、あなたの元のコードと非常によく似て何かを続けることができ、ロックとロック解除の間CPSR内の他の状態ビットのいずれかを変更"memory"
し、"cc"
切り詰めの両方でARM_INT_LOCK
とARM_INT_UNLOCK