UPDATE : 05/25/2004 1:47 pm
AUTHOR : Yoshiharu Oba ( obajus@kk.iij4u.or.jp )
For English is here
更新履歴:doubleのjitコードが不正であったのを解決した。これによりmake checkでのFAIL数が18->8に激減
最初に、この作業を始めるにあたって、たくさんの情報を得ることができた下記のサイトの方々にお礼を申し上げます。
Targetは、MIPS系CPUを積んだLinuxマシンです。手元にあるのは、MontaVista系のLinux 2.4.17の
Linux PlayStation2 2.4.17_mvl21 #2 Mon Jul 21 15:35:41 JST 2003 mips unknown
と
Linux 2.4.18_mvl30-pb1500 #13 2003年 10月 3日 金曜日 10:26:25 JST mips unknown
の2台です。下のPCはAMD製 Au1500 というMIPS R3900 CPUです。
1) jit3モードで動かすこと
kaffeは、2つのモードを持っています。
- インタープリターモード
- JIT3コンパイラーモード
MIPS上に移植した物は、インタープリターモードでの動作を検証していますが、やはりJavaの実行速度は非常に遅いのです。実行速度で有利なJITコンパイラーモードで動くように修正します。
JIT3モードでコンパイルすると、下記の不具合が露見します。
- セマフォ処理内部のASM記述でコンパイルエラー
- Java上でのExceptionを、signalとして受け取れない
- nativeコード内で発生したExceptionをtraceできない
- もろもろのコンパイルエラー
2) PS2LINUX特有の問題
一番大きな問題として、FPUとしてdoubleをサポートしていません。PS2LINUXではソフトウェアEmulationを用意しています。しかしKaffeのJITコンパイラーでは、double用の機械語を吐くので、Illeagle instructionエラーが発生します。
そこでdouble用の機械語を吐かないように抑止し、doubleの演算はソフトウェアで行わせます。
PS2の浮動小数点演算は、MIPS正規のものに比べて簡素化されています(doubleの非サポート、非数(NAN)の非サポート等)、単精度は、従来通りの機械語を吐きますが精度が落ちます。よってmake checkでの結果が一部異なります。
libffiを使用すると、o32-sysdepCallMethodに置き換わってどうさするので、こちらに当てたパッチは意味が無くなります。
NATIVE Cコードは、ソフトウェアemulationのコードを吐くので、この辺りのレジスターの使い方をJAVAとの間で合わせます。このあたりは、o32-sysdepCallMethodあたりに書いてあります。
1) JIT3 モードで動かすには
./config/mips/common.h
COMPARE_AND_EXCHANGE(A,O,N) macroは、実はMIPSII以外のマシン語ニーモニックを使用しているのでエラーが出ます。kaffe-1.1.2の相当のファイルでは、修正されているので、そちらを参考に書き換えます。
同時にmipsのCPUを定義している、sgidef.hをincludeします。
./config/mips/jit.h
./config/mips/linux/md.h
./config/mips/mips.c
./kaffe/kaffevm/systems/unix-jthreads/signal.c一部のnativeコード( interfaceからのexception )では、スタックの使い方が、何故か?違います。
#fp( r30)を使わないで、sp( r29 )を使っています。
このために、InterfaceからのExceptionをキャッチできずに終了してしまうのを防ぎます。それと、signalを受け取る引数をlinuxに合わせます。
2) PS2LINUX特有の問題(Double対応)
./config/mips/jit3-icode.h
doubleを扱う機械語をすべてundefにして、これらのjitコードを抑制します。
./config/mips/jit3-mips.def
関数の引数において、doubleはFPU0ではなく、汎用レジスター$A0〜$A3に割り当てられます。
./kaffe/kaffevm/jit3/icode.c
jitコードで扱えないdoubleを、ソフトウェアで処理します。
こちらは、kaffe-1.1.2で実装されているコードを持ってきます。作者、Dylan Schellに大変感謝しています。
未だ作業中の内容は
./config/mips/jit3-mips.def
./config/mips/jit3-icode.h
この2本が、コンパイルした結果次第で埋め込まれる関数群ですが、PS2LINUXでは(正確にはEE)でサポートしていない命令が使用されています。( mov.d , add.d ... )
さらに、どうやってdoubleを扱えばいいのか、、、、、謎は多いです。PS2でサポートしていない命令として有名なのが、LLSC問題ですが、こちらは単純な方法で回避してあります。--> ./config/mips/common.h
できあがったパッチはこちらです。PS2用となってますが汎用mipsでも問題ありません。
kaffe-1.0.7 for MIPS LINUX JIT supported patch kaffe-1.0.7-for-ps2.patch
kaffe-1.0.7に必要な動作環境は、kaffe-1.0.6と同様なのでOld & Newsで詳細に説明されています。
kaffe-1.0.7のソースが展開できたところでパッチを当てます。
JIT3コンパイラーを活かすためのconfigure設定は、つぎのとおりです。
--with-engine=jit3 をconfigureに加えます。
./configure --with-engine=jit3 --with-libffi --with-libraries=/usr/local/libffi/lib --with-includes=/usr/local/libffi/include
kaffeでは、セルフテストするコマンドがあります。
make check
特にmake checkで、javaの色々なクラスを呼び出したセルフテストを行ってくれます。全てをクリアーすることが望ましいのですが、
Au-1500では、2個失敗
PlayStation2にいたっては、8個も失敗します。
これは、今後の課題とします
JITコンパイラーを動かす為には、officialのdocumentから、portingが大変役に立ちました
残る8個のFAILだが、本当に問題となるのは
DoubleCvt
DoubleNeg
DoublePrint
DoubleCompの4つ、
ソース上でfloatとしてある場合でも誤差を減らす為にコンパイラーは通常doubleとしてレジスターに割り当てて最終的にfloatに丸めるコードを吐きます。ところが、今回PS2のdoubleを非サポートとしたので、floatのままレジスターに割り当てられるので、doubleをサポートした結果とは大きな誤差を含んでしまいました。これは、もう、しようがないかと(^_^;、、諦めて、、、kaffeのPS2への移植を終わりにします。
libffiにもセルフテストがあって、make test と実行します。上記で公開されているパッチだけでは、このセルフテストで失敗します。
PS2LINUXでのdouble扱いがちょっと違う事が原因なのです。
double型の引数は、$a0 〜$a3に割り当てます。
パッチはこちらです。
libffi-1.20 for PS2LINUX ( PlayStation2 LINUX ) libffi-1.20-mips-ps2linux.patch