2011年07月19日
ARM LinuxでのVFP(FPU)レジスタの退避、復帰
LinuxではVFP(FPU)のレジスタの退避、復帰は遅延的に行われます。
ARMのLinuxのソース上のどこでそれが行われているかを調べました。
ARMの浮動小数点ユニットはARM11ではVFPと呼ばれています。(Vector Floating Point). しかしこのVector機能は変則的でコンパイラにもサポートされていません。NEON登場後はVector演算はそちらをつかうようになってきています。Cortex-A9では浮動小数点ユニットでのVector機能は削除され、名前もFPUに改められました。
しかし、ARM LinuxのソースコードではVFP/FPU関連のソースコードは共通にvfpの名称が使われています。この記事でもVFPと呼称します。
VFPの初期化
arch/arm/vfp/vfpmodule.c: vfp_init
VFPの有無と種類を確認します。
VFPのレジスタはdouble(8バイト)が16個ですが、NEON搭載のVFPv3では32個あります。
VFPの例外ハンドラの設定、thread切り替え時のフックを設定。
thread切り替え時
arch/arm/vfp/vfpmodule.c: vfp_notifer
SMPでない場合はVFPのENビットを落とすだけで、この時点ではVFPのレジスタの保存は行いません。
SMPの場合で、そのスレッドですでにVFPを使用していた場合には、VFPのレジスタを保存します。
VFP例外処理
arch/arm/vfp/vfphw.S: vfp_support_entry
VFP関連の例外の入り口は全てここです。VFPのENビットが落ちている状態で、VFPのレジスタを使用する命令を実行したときも例外が発生してここに来ます。
まず、VFPのENビットを調べてこれが立っていなかった場合には、VFPのレジスタを以前VFPを使っていたスレッドの領域に保存します。(SMPの場合はすでに保存済みなのでスキップ。) その後に現在のスレッドのVFPの状態をVFPレジスタにロードします。この時点でVFPのENビットは立てられています。
次にそれ以外のVFP例外の処理を行います。
実際にVFPレジスタのセーブ、ロードを行う場所
マクロで定義されています。
arch/arm/include/asm/vfpmacros.h
VFPFSTMIA 全てのVFPレジスタのセーブ
VFPFLDMIA 全てのVFPレジスタのロード
VFPFLDMIAが使われているのは1箇所だけ。vfphw.Sのvfp_support_entry
VFPFSTMIAは2箇所。vfphw.Sのvfp_support_entryとvfp_save_state
vfp_save_stateはvfpmodule.cの中の3箇所で使われている。vfp_notifer, vfp_pm_suspend, vfp_sync_hwstate
SMPの場合の違う箇所
SMPでない場合にはCPUはひとつだけなので、VFPのレジスタも一組だけです。そのためVFPのレジスタの内容はメモリに退避していなくてもいつでもアクセス可能です。
SMPの場合にはCPUが複数あり、VFPはそれぞれのCPUについています。アクセス可能なのは自分のCPUのVFPレジスタだけです。SMPではスレッドが一度スリープした後に以前とは異なるCPUで実行を再開することがあります。このときにこのスレッドのVFPのレジスタの内容が以前のCPUのVFPレジスタに置いたままだと、それを持ってくるのがやっかいです。そのためにSMPではスレッド切り替えのたびにVFPレジスタをメモリ上に保存しているのだと思います。