今回は、PowerPC用のソースコードをUniversal Binary化する時に注意すべき点をピックアップしてみます。最大の関門は、 PowerPCとx86 CPUのエンディアンの違いを克服することですが、それ以外にも色々な要因が存在します。
エンディアンの問題を含め、Universal Binary化でのソースコードの書き換えは、ほとんどがPowerPCとx86 CPUのアーキテクチャの違いにより生じます。以下に対象となるCPUアーキテクチャの違いをピックアップしてみました。
1.エンディアンの違い(PPCはビックエンディアンでx86はリトルエンディアン)
2.AltiVecユニットの有無の問題(x86 CPUではSSE/SSE2に切り換える必要あり)
3.ゼロで割ったときの処理が異なる(PowerPCは結果がゼロでx86はクラッシュする)
4.変数アレンジメント(PowerPCは4Byteや16Byteだがx86は1から10Byteの可変)
5.構造体とUnionのアレンジメントに違いがある
6.整数演算オーバーフロー時の代入値の違い(PowerPCは0x7fffffff,x86は0x80000000)
7.データ長の違い(x86はlong doubleが80Bits、boolはx86が1ByteでPowerPCは4Byte)
8.浮動小数点の比較に違いが生じる時がある(オーバーフローなどの扱い)
9.関数の呼び出し時の引数(引数の積み方x86ではスタックへPowerPCではレジスタへ)
10.ビットフィールドの扱いが異なる(これはコンパイラの仕様にもよる)
これら以外にもまだ幾つか存在しているかもしれませんが、ソースコードに影響が出ると考えられる要因についてはこれぐらいでしょう。一番影響が大きいのは1番ですが、画像処理の高速化などにAltiVecコードをバリバリ用いているソースコードでは、2番も大きな問題となります。逆に、AltiVecをまったく利用していなければ、2番に関しては何も気にする必要はありません。3番に関してはあってはいけないミスです。通常は、事前にゼロで割ることは避けるように処理されているべきで、そうでないと、x86 CPUはクラッシュします。どちらかと言うと潜在的に存在していたバグだと考えて対処すべきでしょう。
4番と5番も大きな問題ですが、68K CPUからPowerPCへソースコードを移植した経験のあるデベロッパは、すでに対策済みのソースコードを用いているはずですので、それほど心配はないでしょう。今のところ、筆者もこの2つの要因にひっかかりソースコードに手を入れたことはありません。数多くのアプリケーションをUniversal Binary化してきた経験上、それ以外の要因については、通常あまり気にする必要はなさそうです。ただし、今回の話は、あくまでも筆者のソースコードの「書き方」に限定されますので、各人それぞれ注意を怠らないようにしていただきたいと思います。
上記の要因に対処していないと、いかなる結果が起こるのか予想してみましょう。まず1番のエンディアンの違いが無視されていると...クラッシュする、計算結果が異なる、画像のカラー表示が異常、Unicode(複数バイト文字)テキストが正しく表示できない、バイナリファイルが正しく読み書きできない、ネットワークによるデータのコミュニケーションが正しく行われない、などなど数多くのトラブルが生じます。AltiVecコードを利用していれば、x86用コンパイル時にエラーが表示されます。また、それ以外の要因を無視した結果としては、分岐処理の異常やクラッシュなどが考えられます。
こうして調べてみると、やはり「エンディアンの違いの克服」を達成すれば、Universal Binary化の仕事は90%完了することが理解できると思います。ただし、ソースコードに一カ所でも対処し忘れの箇所が存在すると、それが大きなバグとして残りますので、細心の注意をはらい対応箇所を探し出す必要があります。特に画像関連のアプリケーションをUniversal Binary化していると感じるのですが、このエンディアンへの対処、本当にひとつひとつ「つぶす」という表現がピッタリと当てはまる根気のいる作業です。
次に、もうほとんどの皆さんは理解されていると思いますが、復習として「エンディアンの違いとは何か?」を簡単におさらいしておきます。PowerPCの場合には2Byte、4Byte、8Byteの数値は、そのバイト順にメモリに格納されますがx86の場合には逆順に格納されています。この数値のメモリへの格納方法の違いがエンディアンの違いを示し、バイト順通りに格納される方をビッグエンディアンと呼び、逆順で格納される方をリトルエンディアンと呼びます。
unsigned long value;
unsigned char *cptr;
unsigned char cc;
value=0x12345678;
cptr=(unsigned char *)&value;
cc=*cptr;
上記ソースコードでは、valueというunsigned long(4Byte)に16進の0x12345678を代入しています。そして、valueの先頭アドレスをキャストを使いポインタのcptrに代入し、その先頭アドレス1Byteのみをunsigned charのccに代入しています。処理後、何らかの方法でccの内容を表示してみると、PowerPC(ビッグエンディアン)では0x12と表示されますが、x86(リトルエンディアン)では0x78と表示されます。この表示結果から、x86ではunsigned longの数値の最終バイトから順にメモリに格納されていることが良く分かります。ちなみに...
cc=*(cptr+1);
とすると、PowerPCでは0x34と表示され、x86では0x56という表示結果になります。
ところで、ビッグエンディアン、リトルエンディアンと言う呼び名の由来はどこから来たのでしょうか?どうも、ガリバー旅行記に登場するとある王国が、卵をとんがった(小さい)方から食べる派閥と、膨らんだ(大きい)方から食べる派閥に分裂していたという逸話が発端のようです(笑)。何故ガリバー旅行記から引用されたかは謎なのですが、そんな面白い由来も「Universal Binary Programming Guidelines」で紹介されています。
次回は、PowerPCとx86 CPUのエンディアンの違いによる影響を、サンプルアプリケーションのソースコードを調べながら検証していきたいと思います。数値のバイト順がひっくり返りメモリに格納されるという摩訶不思議な体験(まあそう思うのは純正マックデベロッパだけですが...)をしてみたいと思います。
copyright 2006 Ottimo, Inc. All rights reserved
無断転載・引用禁止
Contact us: koike@ottimo.co.jp