インラインアセンブリだけの関数の最適化でハマった
はじめに
Cortex-Mのコンテキストスイッチを書いています。 引数有りで、インラインアセンブリだけを実行する関数を作り、threadのディスパッチを書いていました。
このような場合、適切なconstraintを書いてインラインアセンブリを呼び出さないと、リリースビルドの最適化で意図せぬ挙動になります。
問題を起こしたdispatch関数
まず、デバッグビルドでのみ動作する実装です。
/// A dispatcher of Cortex-M. /// Parameters: /// sp: stack pointer pub extern fn dispatch(sp: *usize) void { asm volatile ( \\ ldr sp, [r0] \\ pop {r4-r12,lr} \\ mov r0,r12 \\ bx lr ); }
第一引数は、r0
に格納されるため、そのままバイナリが出力されれば、問題なく動き、スレッドが起動します。
症状
上記プログラムをリリースビルドすると、HardFaultが発生し、スレッドが正常に起動しません。
dispatch
のシンボルを強制的にexportし、break pointを作ってデバッグします。
その結果、dispatch
に到達した時点で、r0
(引数のsp)に0
が格納されていることがわかりました
(補足ですが、ここでの期待値は、RAM領域なので、0x200xxxxx
です)。
dispatch
関数のインライン化を抑制して、明示的に関数呼び出しするようにしても、症状が改善しませんでした。
調査を進めたところ、dispatch
内で明示的に引数のspを使用すると、正常に動作することがわかりました。
解決策
input constraintを使って、sp
を利用することを明示的に示します。
/// A dispatcher of Cortex-M. /// Parameters: /// sp: stack pointer pub extern fn dispatch(sp: *usize) void { // **Note** Need explicit constraint to prevent compiler from mis-optimizing. asm volatile ( "ldr sp, [%[sp_i]]":: [sp_i] "r" (sp) ); asm volatile ( \\ pop {r4-r12,lr} \\ mov r0,r12 \\ bx lr ); }
これで、リリースビルドでも、意図通り動きます。
せめて、インライン化を抑制したら、引数くらいはちゃんと渡して欲しいです…。