W.Deeの2006年5月の日記

kikyou.info»日記

誠に勝手ながら、kikyou.infoは2017年12月いっぱいをもって閉鎖します。ながらくありがとうございました。

最新月 : 2008年10月
2003年 [             3    4    5    6    7    8    9   10   11   12  ] 月
2004年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2005年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2006年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2007年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2008年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2009年 [   1    2    3    4    5    6    7    8    9   10   11       ] 月
前月の日記  次月の日記

2006年5月29日

燃えるギャルゲー

ギャルゲーが萌えるのは当然なのでやっぱり燃えるギャルゲーを作ってみたい。既出ネタをおそれずに書けば

  • × 放課後の校門で待ち合わせ
  • ○ 放課後の校門で待ち伏せ
  • × お兄ちゃんと一線を越える
  • ○ お兄ちゃんと一戦を交える
  • × 額に肉と書く
  • ○ 首を刃物で掻く
  • × 下駄箱にラブレター
  • ○ 下駄箱に果たし状
  • × お休みのキス
  • ○ お休みのドス
  • × 萌え
  • ○ 燃え

愛を必死に奪い取る。

  • 2006-05-29 15:03 京 秋人 : 青春ですね。
  • 2006-05-29 15:06 SB : 起きて!起きて!ねぇ○○ちゃん起きて!>>起きろ! 起きろ! 起きろ! マスかきやめ! パンツ上げ!
  • 2006-05-29 16:47 通りすがりのひと : 兄妹で敵味方に分かれて壮絶なバトル(でも内実は兄妹喧嘩)というゲームはありそうな気がするけど思いだせんな(何。
  • 2006-05-31 00:08 W.Dee : デートを決闘かなんかと勘違いしてる愛すべき連中の繰り広げる生きるか死ぬかのラブストーリー! ………… とか考えてました

2006年5月26日

KAGEX

ごうさんのところで KAGEX なるものが公開されています (←サンプルもこのリンク先で公開されています)。

なんというかごうさんの所で公開されているサンプルを見るのが速いと思うのですが、演出で使える様々なレイヤ効果が簡単に扱えたりします。

それと上記の「ワールド拡張機能」にあるように、環境情報を適切に設定してあれば (ここで「みく」はキャラクタ名)

[みく おじぎ]

[みく 制服 通常更新]

のように劇的な記述量の削減ができます。しかも可読性も失われてないです。このタグだけをみても何をやっているのかの想像がつくでしょう。

ボイスの再生も【名前】を行頭に書いて

【みく】「ねえ、おきてよー。遅刻だよー!!!」

のように記述すればキャラクタ「みく」のボイスが自動的に再生されます。改行が自動的にクリック待ちになっていたりします。

ある程度特定の用途に限定されてはいますが、可読性と機能性を失わずに記述量を削減できているすばらしい拡張だと思います。

リポジトリ上の最新のソースから構築された吉里吉里コアでないとKAGEXが正常に動作しないので、週末に吉里吉里2のメンテナンスリリースを行おうと思います。

ちなみにボイス機能のサンプルに使えるような10文程度の適当なボイス募集中です。

  • 2006-06-01 14:44 たにみちNON : よろしければ実際にエロゲで使ってるデータを提供できますけども。声優さんにも許可とれると思うし。
  • 2006-06-01 15:35 W.Dee : おお、ありがとうございます。今回はサンプルとして配布するので最低でも「KAG/KAGEX のサンプルとして配布してよい」という許諾をいただければ、と思いますがよろしいでしょうか?
  • 2006-06-01 15:44 W.Dee : あああ、すみません、ちょっと(^^; お待ちください (確認中)
  • 2006-06-01 16:03 W.Dee : もうひと方、提供してくださると(先に) おっしゃってる方がいて、こちらのほうの確認をとっております。こちらのほうで話がまとまるようならば、その際はせっかくですがこちらを使わせて頂きます。   (あと適当なボイスと本文では書いてますが10文程度の「会話文」が好ましいです、説明が足りなくて申し訳ありません)
  • 2006-06-01 21:02 たにみちNON : 社名と声優さんの名前が載ってれば配布は問題ないです。もういっそ新規で収録するか(ぉぃこら ショチョーの許可はでたので声優さんから許可が出しだいデータ送りますです。
  • 2006-06-01 21:05 たにみちNON : そのとき別の方ので決まってたらそれはそれでナシの方面でOKつうこって。えぇ
  • 2006-06-02 12:44 W.Dee : こんにちは。ありがとうございます。せっかくのお申し出で大変ありがたく思いますが、↑の「もうひと方、提供してくださると(先に) おっしゃってる方が」を使わせて頂きます。もしまたの機会がありましたらよろしくお願い致します。
  • 2006-06-09 23:46 たーゆー : こんばんはです。掲示板で質問していいのか分からなかったのでこちらに。今のバージョン(2.27-dev.20060527)でKAGEXは正常に動作するのでしょうか?
  • 2006-06-10 00:07 W.Dee : 動くと思います
  • 2006-06-10 01:44 たーゆー : お返事ありがとうございます。kag3のtemplateの変わりにKAGEXのtemplateを使うことにやっと気づきました(汗 ただkag3から引っ張ってきただけかと思ってました) world.ksだけ呼び出して起動しないなーってずっとやってたもので(苦笑) 解決しましたー。

2006年5月21日

LLVMのパフォーマンス

LLVMと他のコンパイラの比較を行ってるページ、というかgccのSPEC2000ベンチマークのページですが、見つけたのでリンクを張っておきます。

http://people.redhat.com/dnovillo/spec2000.i686/gcc/individual-run-ratio.html

他のコンパイラと張り合う?ぐらいの能力はあるんですね。

SSA 形式への変換実装中

ASTからSSA形式への変換 をノロノロと実装中。

ふと try ... catch/finally のサポートがすこし厄介な事がわかって(もっと前に気付けよという話も)、コードを整理中。

2006年5月15日

kikyou.infoのHDDが壊れてなかった

壊れていると思っていた HDD ですが、別コンピュータにつないで badblocks で調べてもエラーが見つからない。書き込みを伴うチェックを行うようなオプションを badblocks につけてもエラーが検出されない。

けれども、その HDD を RAID1エンクロージャ に装着すると再構築中にエラーになる (というか前回出ていた読み出し側 HDD は今度はエラーにならない)。

なんどか繰り返しているうちになぜか再構築が成功。

ARAID-99 1000Lがイカれましたかね。

ううむ。

2006年5月13日

萌えるゲーム制作 吉里吉里/KAGで作る美少女ゲーム

萌えるゲーム制作 吉里吉里/KAGで作る美少女ゲームという本がインプレスより発売されるそうです。

2006/06/01発売、CD-ROM1枚付属、\2,793(本体 \2,660+税)

  • 2006-06-02 11:11 ほのか : これってどうなんでしょう。買った方の感想をぜひ聞かせてください。
  • 2006-06-02 12:49 W.Dee : 僕に関して言えば、手元に無いのでなんとも(汗) 本を(出版社様から)送ってくださるみたいな話はそういえば聞いてないんですが、一応ツール作者としてもっておくべきなんかな
  • 2006-06-17 01:14 W.Dee : 手元に来ました。さっと読んでみましたが「(吉里吉里/KAGで作れるようなジャンルの)ゲームを作りたいが何かが足りなくて次の一歩を踏み出せない」といった人には良いきっかけになるのではないでしょうか。「何か」ってのは吉里吉里/KAGのようなゲームエンジンだったり絵だったり音楽だったり。なんにせよ何かを形にしないと始まりませんから。あと、なれてきたら吉里吉里本体についてくるリファレンスなどを併用したがよいかもしれません。

2006年5月12日

kikyou.infoのHDDが壊れてた

壊れたディスクの代替ディスクを買ってきて RAID 再構築を行っていたら、最後のほうで 元ディスク (先ほどまでまだ壊れてないと思っていた方) から読み出しエラーになりました .... orz

とりあえず、その元ディスクも、危ない箇所にさわらなきゃ動くという期待の元、その元ディスクのみで今は運用しています。

どうしよう。読み出せなかった場所は最後のほうだし、そこには何もデータが無い可能性は高いのですが。。。

ミラーデバイス(ARAID-99 1000L)に任せずに自分でブロックデバイスレベルでコピーするか、RAIDブロックデバイスレベルのコピーじゃなくてファイルシステムレベルのコピーを行うか。どちらにしろ結構長期間 kikyou.infoを止めないとならないなぁ。

ちなみに ARAID-99 1000L ですが、液晶表示では、正確にどこで読み出せなくなったかがサッパリわかりません。これもこまったもの。シリアル端子の Linux からの使用法わかんないし (あるんかな)

それとやっぱり Western Digital だからかな?? (身の回りではそういい評判を聞かないのでした)。そしてその代替ディスクも同じ型番の Western Digital 製というオチつき。だって同じ型番がいいじゃん、RAID-1なんだし。

明日の早朝あたりに緊急メンテナンスするかもしれません。

2006年5月11日

AST→SSA形式

SSA 形式 がなかなかおもしろそうなので、実装してみることに。

とりあえず Risse の、スクリプトコードからバイトコードまでの変換は、 (lex + parse) → AST → SSA form → (最適化) → byte code という流れを考えています。

(最適化) の部分は無いか、あったとしても相当限定された物になりそうです。それでも TJS2 の頃よりは良質なコードを吐けそうですし、変数がプログラム中の任意の時点でとりうる「型」の推測もさほど難しくなさそうです。

lex, parse からバイトコード生成まで on the fly で処理していた TJS2 に比べると明らかにコンパイル処理は重たい物になるでしょうけど、様子をみながらコンパイルそのものも最適化を進めていきたいと考えています。

バイトコードは VM (仮想マシン) 用の物ですが、TJS2 と同じく仮想レジスタマシンにする予定です。

あとコンパイラの基礎についてちゃんと勉強してから実装、、、と行きたいところなのですが、自分は考えるより手が動いてしまう方なので中途半端なものができあがる予定。

参考

2006年5月9日

kikyou.infoのHDDが壊れた

RAID1エンクロージャのステータスを見る限り、片方のHDDが壊れたようです。

またですか。

ちなみに前回はこの日に壊れて、3年間保証に釣られてこれを買ってきました

今度は10ヶ月でお釈迦。

早速変えてこようとおもいます………。

2006年5月8日

LLVM所感

ここ数日 LLVM を弄ってみた感想です。C++例外はどう処理されるのか、Risse で使おうとしている Boehm GC との相性はどうなのか、コルーチン大丈夫なんか、など、まだまだ検証したい項目がありますが、それもまた今度の機会に、ということで。

  • プラットフォーム非依存のバイナリコードを直接実行できる
  • ほぼ完全な C/C++ コンパイラがある(gccベース)
  • 簡単に独自のコンパイラや JIT 付きのインタプリタを作れるのがよい
  • リンカやアーカイバ、アセンブラ、正確なGCライブラリなど、周辺機能も充実
  • ほとんどすべてがC++から再利用可能なコンポーネントとして提供されている
  • アクティブに開発が続けられている
  • オープンソース、マルチプラットフォームである
  • ライセンスが扱いやすい
  • (MinGWでは)ひたすらビルドが難しい
  • 非JIT動作のバイトコードインタプリタは破壊的に遅い
  • バイナリサイズがでかい
  • まだまだバイナリコードの最適化が甘いかな
  • 日本語資料が皆無に等しい

「簡単に独自のコンパイラや JIT 付きのインタプリタを作れる」というのは、自分はまだやったわけではないのですが、付属の Stacker という Forth ライクな言語は LLVM についてなにも知らなかった人がたったの4日間で実装したらしいですから簡単なのでしょう。付属の他のサンプルをみていてもそう難しそうには見えません。もしかしたら独自の言語を作るのが流行るかも……?

「バイナリサイズがでかい」というのは、たとえば lli (バイトコードインタプリタ/JITコンパイラ) がデバッグ情報なしで 5,955,584 バイトもあります。ダイエットすれば小さくなるのでしょうけど。

「(MinGWでは)ひたすらビルドが難しい」というのもかなりの不安要因です。

これらを鑑みて、肝心な Risse ではどうするか、ですが、Risse は最初は LLVM を使用することを考えずに、簡単な最適化機構とVM を独自に積む方向で考えたいと思います。ただ、LLVM は後に JIT コンパイラを載せたくなったときの最有力の候補ですので、様子を見続けたいと思います。

2006年5月6日

LLVMをいじる(8)

Risse での実際の用途に近い状態でのLLVM自体の最適化性能を調べるために、以下のようなプログラムを作成しました。

プログラム (C++)

何をやっているかというと、Variant というクラスがあって、整数型の integer というメンバと実数型の real というメンバ、それとこのインスタンスが整数を表しているのか、実数を表しているのかを表す type というメンバを持っています。いわゆるバリアントです。

最後に test という関数があって、0~100000 の整数の合計を計算して返しています。main 関数ではそれの戻り値を表示しています。

さて、これをコンパイルして逆アセンブルしてみると ....

$ llvm-gcc -o vtest test.cpp
gccld: warning: Cannot find library 'stdc++'
$ llvm-dis -f ./vtest.exe.bc
$ cat vtest.exe.ll
; ModuleID = './vtest.exe.bc'
target endian = little
target pointersize = 32
target triple = "mingw32"
deplibs = [ "stdc++", "c", "crtend" ]
%.str_1 = internal constant [5 x sbyte] c"%ld\0A\00"            ; <[5 x sbyte]*> [#uses=1]

implementation   ; Functions:

declare int %printf(sbyte*, ...)

int %main() {
entry:
        %tmp.0 = tail call int (sbyte*, ...)* %printf( sbyte* getelementptr ([5 x sbyte]* %.str_1, int 0, int 0), long 4999950000 )        ; <int> [#uses=0]
        ret int 0
}

………。

ということで、いきなりすでに結果の値が埋め込まれてしまっています。Variant クラスの関数も全部なくて、main 関数しかなく、計算をするコードすらありません。

これはイケてるかも。

test関数のループ内に 4 を毎回加算するような以下のコードを書いても

integer_t test()
{
    Variant i;
    Variant sum = (integer_t)0;
    Variant limit = (integer_t)100000;
    for(i = (integer_t)0; i < limit; i.Increment())
    {
        sum += i;
        sum += (integer_t)4;
    }

    return sum.AsInteger();
}

コンパイル結果は

%tmp.0 = tail call int (sbyte*, ...)* %printf( sbyte* getelementptr ([5 x sbyte]* %.str_1, int 0, int 0), long 5000350000 )        ; <int> [#uses=0]

のように計算済み結果となってしまいます。

以下のようなコードでやっと事前の計算をあきらめてくれました。

integer_t test()
{
    Variant i;
    Variant sum = (integer_t)0;
    Variant limit = (integer_t)100000;
    for(i = (integer_t)0; i < limit; i.Increment())
    {
        sum += i;
        sum += sum;
    }

    return sum.AsInteger();
}

逆コンパイル結果は以下のようになりました (main 関数内のみ示す)。

int %main() {
entry:
    br label %shortcirc_next.0.i49.i

shortcirc_next.0.i49.i:        ; preds = %no_exit.i, %entry
    %indvar.i = phi ulong [ 0, %entry ], [ %indvar.next.i, %no_exit.i ]        ; <ulong> [#uses=3]
    %sum.0.5.i = phi long [ 0, %entry ], [ %tmp.18.i.i, %no_exit.i ]        ; <long> [#uses=2]
    %exitcond.i = seteq ulong %indvar.i, 100000        ; <bool> [#uses=1]
    br bool %exitcond.i, label %_Z4testv.exit, label %no_exit.i

no_exit.i:        ; preds = %shortcirc_next.0.i49.i
    %i.0.0.i = cast ulong %indvar.i to long        ; <long> [#uses=1]
    %tmp.18.i28.i = add long %i.0.0.i, %sum.0.5.i        ; <long> [#uses=1]
    %tmp.18.i.i = shl long %tmp.18.i28.i, ubyte 1        ; <long> [#uses=1]
    %indvar.next.i = add ulong %indvar.i, 1        ; <ulong> [#uses=1]
    br label %shortcirc_next.0.i49.i

_Z4testv.exit:        ; preds = %shortcirc_next.0.i49.i
    %tmp.0 = tail call int (sbyte*, ...)* %printf( sbyte* getelementptr ([5 x sbyte]* %.str_1, int 0, int 0), long %sum.0.5.i )        ; <int> [#uses=0]
    ret int 0
}

いずれにしろ、Variant 内に書かれているような実数演算に関するコードはいっさい出てきていません。整数の計算しか書いてないですし、実際に整数の計算しかしていないからです。

実は、これぐらいの最適化をしてくれることを期待していたのです。

Risse はタイプ・ルーズな言語なので、変数などへのアクセスはすべて型が何であるかを実行時に毎回チェックしています。

たとえば TJS2 で以下のようなコードを書くと、

function make_sum(array)
{
  var sum;
  for(var i = 0; i < array.count; i++) sum += array[i];
  return sum;
}

現状の実装では i の型を毎回チェックしてしまいます。

しかし、i は整数にしかなり得ないのは、インライン展開や定数伝播(constant propagation)解析や死亡コード除去(dead code elimination)などの詳細な解析をおこなえば判明するので、これにより最適化の効果が望めるところです。

Risse は動的な言語ですが、この手の最適化を妨げるほどの自由度は与えない方針です。たとえば 整数クラス Integer の += 演算子などは、とりあえずはオーバーライドや置き換えが可能にはしないと思います。

ちなみに、プログラム中の Variant::integer と Variant::real を union にすると LLVM ではこの手の最適化はいっさいダメになってしまいます。実際の Risse 内部のバリアントはすべての型の値は一つの union で共有されているので、この手の最適化を有効にするには union による共有を (LLVM用コード内では)あきらめるか、あるいはそもそも変数の型のトラッキングを Risse 側で解析してやる必要がありそうです。前者は Risse 内部のバリアント型との変換を書かなければならないし、このような最適化が聞かなかった場合に変数がストレージを食い過ぎるので、後者を採用するしかないかも、、、ってそれじゃ今回見てきたような LLVM の最適化の恩恵をあまり得られないじゃん。

どうしようかな。まさか、むしろ、いまさら、Risseをタイプルーズじゃなくするとか?

LLVMをいじる(7)

簡単なベンチマークを。

ベンチマーク用プログラムにはこちらのプログラムを使用し、655,360桁の円周率を計算するのに必要な時間を計測しました (I/Oの時間を計測する目的ではないので、ソースを修正して出力が行われないようになっています)。

まず、gcc。

$ gcc -v
Reading specs from c:/k3/MinGW/bin/../lib/gcc/mingw32/3.4.5/specs
Configured with: ../gcc-3.4.5/configure --with-gcc --with-gnu-ld 
--with-gnu-as --host=mingw32 --target=mingw32 --prefix=/mingw --
enable-threads --disable-nls --enable-languages=c,c++,f77,ada,ob
jc,java --disable-win32-registry --disable-shared --enable-sjlj-e
xceptions --enable-libgcj --disable-java-awt --without-x --enable
-java-gc=boehm --disable-libgcj-debug --enable-interpreter --enab
le-hash-synchronization --enable-libstdcxx-debug
Thread model: win32
gcc version 3.4.5 (mingw special)
$ gcc -O3 -mtune=athlon64 -o pi-gcc-native pi_fftca.c fftsgx.c
$ time ./pi-gcc-native.exe
real    0m3.171s
user    0m0.015s
sys     0m0.015s

結果は 3.171秒。

つぎ、LLVM。

$ llvm-gcc -o pi pi_fftca.c fftsgx.c
$ time lli ./pi.exe.bc
real    0m4.438s
user    0m0.015s
sys     0m0.015s

結果は 4.438秒。

ちなみに JIT ではなくてインタプリタで動作させた時間を計ろうとしたところ

$ lli --force-interpreter ./pi.exe.bc

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

………あらら。実行できませんでした。

llc で LLVM がどの様なコードを実行しているのか調べてみると、どうやら SSE2 に実数演算を行わせている模様。

gcc でも同様のオプションをつけてやってみると

$ gcc -O3 -mtune=athlon64 -march=athlon64 -msse2 -mfpmath=sse -o pi-gcc-native  pi_fftca.c fftsgx.c
$ time ./pi-gcc-native.exe
real    0m3.063s
user    0m0.015s
sys     0m0.015s

………差が開いてるし。

逆に LLVM で SSE のような拡張命令を使わせないと

$ time lli --mcpu=generic ./pi.exe.bc
real    0m6.187s
user    0m0.015s
sys     0m0.015s

これは遅い。

いろいろ試したのですがおおむね LLVM のほうが2~3割遅いという結果に。まだ一つの例しか見てないのでこれで優劣を決める訳にはいかないのですが、まだまだ最適化が甘いのかもしれません。どうもアセンブリ言語の出力をみていると、レジスタの割り当てに無駄が多く、ロードとストアが多い気がします。

2006年5月5日

LLVMをいじる(6)

(ここの日記は新しい項目ほど上に来ています。「LLVMをいじる(番号)」の(番号)の順に読んでください)

さて、ここでやっと LLVM が生半可に使えるようになってるはずです。

テストプログラムを作ってみます。

$ cat hello.c
#include <stdio.h>

int main(void) 
{
        printf("hello world!\n");
        return 0;
}
$ llvm-gcc hello.c -o hello
$ ls -l
total 4
-rw-r--r--    1 Taka     Administ       84 May  5 23:04 hello.c
-rwxr-xr-x    1 Taka     Administ     5632 May  5 22:23 hello.exe
-rw-r--r--    1 Taka     Administ      177 May  5 23:04 hello.exe.bc

hello.exe と hello.exe.bc ができているのがわかります。hello.exe は hello.exe.bc を実行するためだけのスタブです。実際のプログラムは hello.exe.bc のほうです。実行してみます。

$ ./hello.exe
hello world!

おー。

スタブではなくてインタプリタを起動して実行することもできます。

$ lli hello.exe.bc
hello world!

hello.exe.bc の逆アセンブルをしてみます。

$ llvm-dis hello.exe.bc

hello.exe.llというファイルができます。内容を表示してみます。

$ cat hello.exe.ll
; ModuleID = 'hello.exe.bc'
target endian = little
target pointersize = 32
target triple = "mingw32"
deplibs = [ "c", "crtend" ]
%.str_1 = internal constant [14 x sbyte] c"hello world!\0A\00"          ; <[14 x sbyte]*> [#uses=1]

implementation   ; Functions:

declare int %printf(sbyte*, ...)

int %main() {
entry:
        %tmp.0 = tail call int (sbyte*, ...)* %printf( sbyte* getelementptr ([14 x sbyte]* %.str_1, int 0, int 0) )             ; <int> [#uses=0]
        ret int 0
}

これは LLVM 用のアセンブリ言語ですね。

ネイティブコードに変換してみます。

$ llc ./hello.exe.bc

hello.exe.s ができます。内容を表示してみます。

$ cat hello.exe.s
        .text
        .align  16
        .globl  _main
_main:
        subl $12, %esp
        fnstcw 10(%esp)
        movb $2, 11(%esp)
        fldcw 10(%esp)
        movl $_.str_1, (%esp)
        call _printf
        xorl %eax, %eax
        addl $12, %esp
        ret
        .data
        .align  4
_.str_1:                                # .str_1
        .asciz  "hello world!\n"

ネイティブコードといってもネイティブなアセンブリ言語で記述された出力が出てくるだけなので、これを使うにはアセンブルとリンクをしないとなりません。どうも gas 用のアセンブリ言語なので gcc が使えるようです。

$ gcc hello.exe.s -o hello-native.exe
$ ./hello-native.exe
hello world!

簡単なサンプルですが、一通り動くことがわかりました。

LLVMをいじる(5)

なんというかだんだん疲れてきました。Cygwin はまだ UNIX に近いからいいとしても、たぶん MinGW はおろか Windows プラットフォームによる動作はまだまだ完全にサポートされてないんかなーと。

世の中の PC 用 OS って、シェアを考えると大体 Windows, Mac, Linux, *BSD, Solaris?? だと思っているのですが、メジャーな中では最後のフロンティア(謎)だった Mac OS が X になって BSD ベースになってしまい、ほとんどが UNIX ベースになりました。Windows を除いて。

もちろん UNIX 系 OS の間にも細かい差異があって完全に同じとは言えないのですが、Windows は Cygwin のようなエミュレーションレイヤを使わない限りは UNIX 系とはかけ離れた存在なので、オープンソース系のマルチプラットフォームなアプリではよく Windows だけが例外的な存在になり、サポートするのが困難な状態になってるソフトウェアを見かけます。Mac OS X も特に GUI 周りは例外的ではありますが、まだまだ Windows よりはましといった状態。

ただ、普及シェアからいうと Windows 対応は致し方ない所ですね。

LLVM もその例に漏れないのかなと。

さて、もう一度 LLVM のビルド方法を、gcc フロントエンドも含めて再度整理します。

パスを通しておきます。LLVM のバイナリと LLVM の gcc フロントエンド用のパスです。

$ export PATH="$PATH:/projects/llvm/bin:/projects/llvm-gcc/bin"

LLVM の展開とツールのビルド。前回の内容に近いですが、パッチの内容が更新されています。

$ export CPPFLAGS="-DLLVM_ON_WIN32"
$ mkdir -p /projects/llvm/source
$ mkdir -p /projects/llvm/source/build
$ cd /projects/llvm/source
$ wget http://llvm.org/releases/1.7/llvm-1.7.tar.gz
$ tar zxvf llvm-1.7.tar.gz 

展開したファイルに対して以下の修正を行います (パッチ)

  • llvm/lib/System/Win32/Path.inc の CopyFile の定義を修正 (sys:: が余分)
  • llvm/lib/Target/X86/X86JITInfo.cpp の 先頭に #define __CYGWIN__ を挿入
  • llvm/Makefile.rules の JIT_LIBS の JIT_LIBS := LLVMInterpreter LLVMJIT LLVMCodeGen LLVMExecutionEngine を JIT_LIBS := LLVMInterpreter LLVMJIT LLVMCodeGen.a LLVMExecutionEngine に変更
  • llvm/lib/CodeGen の Makefile の LIBRARYNAME の行の下に BUILD_ARCHIVE = 1 を追加
  • llvm/tools/llc の Makefile の USEDLIBS の LLVMCodeGen を LLVMCodeGen.a に変更
  • llvm/lib/System/Win32/Path.inc の if (GetLastError() != ERROR_NOT_FOUND) を if (GetLastError() != ERROR_FILE_NOT_FOUND && GetLastError() != ERROR_PATH_NOT_FOUND) に変更 (ファイルがない、というエラーコードは ERROR_NOT_FOUND ではないのでこれはバグっぽい気がしますが)
  • llvm/lib/System/Win32/Path.inc の bool Path::renamePathOnDisk(const Path& newName) の最初に DeleteFile(newName.c_str()); を書き加える (UNIX系で標準的なrenameの動作と違い、Windowsでは名前を変更しようとした際、新しいファイル名のファイルがすでに存在するとエラーになるので)
  • llvm/lib/Bytecode/Archive/Archive.cpp の path("<invalid>") を path("(invalid)") に変更 (こうしないと <invalid> が無効なパス名なので問答無用で例外を吐いて落ちる)
$ cd /projects/llvm/source/build
$ ../llvm/configure --prefix=/projects/llvm --enable-targets=x86
$ make tools-only TOOLLINKOPTSB+=-limagehlp TOOLLINKOPTSB+=-lpsapi \
 LDFLAGS+=-Wl,--no-keep-memory -r

これで LLVM のツール群が作成されます。これをインストール。

$ make install TOOLLINKOPTSB+=-limagehlp TOOLLINKOPTSB+=-lpsapi \
 LDFLAGS+=-Wl,--no-keep-memory -r

エラーが出るかもしれませんが人類最強なので無視。

/projects/llvm/bin にバイナリがインストールされますが、拡張子である .exe がありません。拡張子がないと exec が実行に失敗するようなので 拡張子をつけてやります。

$ cd /projects/llvm/bin
$ for d in *; do mv $d $d.exe; done

次に LLVM の gcc フロントエンドをビルドします。

$ mkdir -p /projects/llvm-gcc/source
$ cd /projects/llvm-gcc/source
$ wget http://llvm.org/releases/1.7/cfrontend-1.7.source.tar.gz
$ tar zxvf cfrontend-1.7.source.tar.gz

展開したファイルに対して以下の修正を行います (パッチ)

  • src/gcc/Makefile.in の INCLUDES = -I. -I$(@D) -I$(srcdir) -I$(srcdir)/$(@D) \ を INCLUDES = -I. -I./$(@D) -I$(srcdir) -I$(srcdir)/$(@D) -I/mingw/include \ に変更する ( $(@D) がなぜか空文字列になるのでそれを回避するのと、/mingw/include がインクルードパスになくてコンパイルに失敗するのを回避)
  • src/gcc/Makefile.in の NATIVE_SYSTEM_HEADER_DIR = /usr/include を NATIVE_SYSTEM_HEADER_DIR = /mingw/include に変更 (なんで決め打ちなんだろう)
  • src/gcc/config/i386/mingw32.h の #define LIB_SPEC "%{pg:-lgmon} %{mwindows:-lgdi32 -lcomdlg32} -luser32 -lkernel32 -ladvapi32 -lshell32" を #define LIB_SPEC "" に変更 (LLVM オブジェクトに対して gdi32 や user32 をリンクしてもしょうがない)
$ cd /projects/llvm-gcc/source/
$ mkdir build
$ cd build
$ ../cfrontend/src/configure   --prefix=/projects/llvm-gcc  \
  --disable-threads --disable-nls --disable-shared   \
  --enable-languages=c,c++ --program-prefix=llvm-   \
  --srcdir=../cfrontend/src mingw32
$ make CFLAGS=-O LIBCFLAGS+=-g LIBCFLAGS+=-O2 LIBCXXFLAGS+=-g \
  LIBCXXFLAGS+=-O2 LIBCXXFLAGS+=-fno-implicit-templates all

libstdc++-v3 の configure は失敗します。たぶん失敗しちゃいけないんじゃないかと思いますが ... これも無視加減でとりあえずインストールします。

$ make install

MinGW のヘッダファイルなどが参照されないのでコピーします。もしかしたらこれは llvm-gcc の configure 時に --includedir=/mingw/include とすることで回避できるかもしれません。

$ mkdir -p /projects/llvm-gcc/include/c++/3.4-llvm
$ mkdir -p /projects/llvm-gcc/mingw32/include
$ cp -r /mingw/include/c++/3.4.5/* /projects/llvm-gcc/include/c++/3.4-llvm
$ cp -r /mingw/include/* /projects/llvm-gcc/mingw32/include

もう一度 llvm のディレクトリに戻って LLVM をビルドします。

$ cd /projects/llvm/source/build
$ ../llvm/configure --prefix=/projects/llvm --enable-targets=x86
$ make TOOLLINKOPTSB+=-limagehlp TOOLLINKOPTSB+=-lpsapi LDFLAGS+=-Wl,--no-keep-memory -r

examples ディレクトリ内のサンプルプログラム ParallelJIT は、pthread がインストールされてないとコンパイルエラーになるようです。pthread-win32 を mingw 用にインストールすればコンパイルが通るかもしれませんが、今回は無視。

インストールします。

$ make install TOOLLINKOPTSB+=-limagehlp TOOLLINKOPTSB+=-lpsapi LDFLAGS+=-Wl,--no-keep-memory -r

途中でエラーになるかもしれませんが無視します。

2006年5月4日

LLVMをいじる(4)

LLVMのコンパイル方法を整理。

$ export CPPFLAGS="-DLLVM_ON_WIN32"
$ mkdir -p /projects/llvm/source
$ mkdir -p /projects/llvm/source/build
$ cd /projects/llvm/source
$ wget http://llvm.org/releases/1.7/llvm-1.7.tar.gz
$ tar zxvf llvm-1.7.tar.gz 

展開したファイルに対して以下の修正を行う (パッチ)

  • llvm\lib\System\Win32\Path.inc の CopyFile の定義を修正 (sys:: が余分)
  • llvm\lib\Target\X86\X86JITInfo.cpp の 先頭に #define __CYGWIN__ を挿入
  • llvm\Makefile.rules の JIT_LIBS の JIT_LIBS := LLVMInterpreter LLVMJIT LLVMCodeGen LLVMExecutionEngine を JIT_LIBS := LLVMInterpreter LLVMJIT LLVMCodeGen.a LLVMExecutionEngine に変更
  • llvm\lib\CodeGen の Makefile の LIBRARYNAME の行の下に BUILD_ARCHIVE = 1 を追加
  • llvm\tools\llc の Makefile の USEDLIBS の LLVMCodeGen を LLVMCodeGen.a に変更
$ cd /projects/llvm/source/build
$ ../llvm/configure --prefix=/projects/llvm --enable-targets=x86
$ make tools-only TOOLLINKOPTSB+=-limagehlp TOOLLINKOPTSB+=-lpsapi \
 LDFLAGS+=-Wl,--no-keep-memory -r

でこれで全部 llvm がビルドできたわけではなくて、これから llvm 用 C/C++ コンパイラをビルドしないとなりません。

LLVMをいじる(3)

前回の「リンカが処理する内容に直接影響を与えそうな最適化オプションを個別にオフにすることでリンクが通るようになりました」は気のせいだったみたいです。すみません。見誤っていました。

リンカをデバッガ上で動かして観察していると LLVMCodeGen.o という大きなオブジェクトファイルをリンクしている途中でエラーが発生しています。

こんな大きなオブジェクトファイル、どこからできたのかなと観察してみると、Makefile中で複数のオブジェクトファイル *.o をまとめて一つのオブジェクトファイル .o にする Relink と呼ばれているというルール (たぶん ld -r か ld -i をやってる) があってこれで生成されています。

そういえば現状のRisaでもサブディレクトリごとに *.o をまとめて一つの .o にまとめて、最後にそれらをリンクするという手法を用いていています。こうしないと最終リンク時にコマンドラインに並ぶオブジェクトファイルの数がとんでもないことになります。まあ、たぶんアーカイブファイルでもいいんでしょうけど。

で、LLVM、どうやらこの過程で ld がおかしなオブジェクトファイルを吐いている様子です。

Relink ではなくて、普通の アーカイブ .a を作成するように Makefile を変えたらビルドが通るようになりました。

こうやって作られたオブジェクトファイルやアーカイブファイルが、ビルドトップディレクトリ下の Release/lib にあるのですが、なぜオブジェクトファイルやアーカイブファイルを分けているのでしょうかね。あとで理由を見てみよう。

いろいろいじってる内にビルドが通るようになったのでいろいろと確認したいことがありますから、あとでもう一度手順を整理してみたいと思います。

2006年5月3日

LLVMをいじる(2)

どうもコンパイラの最適化を無しにするとリンカが asesrtion fail を起こさないようです。でも最適化無しだと 10 倍近く遅いよーみたいな警告がビルドの最後に出るのでやっぱり最適化はしたい。

結局、-O2 最適化を行いつつ、リンカが処理する内容に直接影響を与えそうな最適化オプションを個別にオフにすることでリンクが通るようになりました。

あと、 gcc 4.1 は使い慣れないので元の 3.4.5 に戻しました (こっちはこっちで落ち着いたらいずれ使うつもり)。

configure 以降は

$ ../llvm/configure --prefix=/projects/llvm --enable-targets=x86
$ make tools-only TOOLLINKOPTSB+=-limagehlp TOOLLINKOPTSB+=-lpsapi \
 LDFLAGS+=-Wl,--no-keep-memory -r \
 CFLAGS+="-fkeep-inline-functions -fno-merge-constants -O2" \
 CXXFLAGS+="-fkeep-inline-functions -fno-merge-constants -O2"

にしました (llvm に関してはどうやら CFLAGS と CXXFLAGS は make 時にこうやって渡さないとダメ)。

ちなみに LLVM のデフォルトの最適化レベルである -O3 だと gcc 3.4.5 が internal compile error するので -O2 にしてあります。

が、今度は opt.exe のリンク中に

undefined reference to `llvm::SDNode::hasNUsesOfValue(unsigned int, unsigned int) const

などという表示が。またあとで見ます ...

2006年5月2日

LLVMをいじる

最適化をするに当たって、いずれ JIT コンパイラとして使おうと思っていた The LLVM Compiler Infrastructure を先にさわっておこうと思っています。

The LLVM (Low Level Virtual Machine) Compiler Infrastructure はコンパイラを実装するに当たって必要な機能のうち、最適化、コード生成と実行に関わる部分がライブラリになっています。最適化とコード生成はコンパイル時、リンク時、実行時、オフライン時 (いったんコンパイルが終わった物を最適化された状態にするために再コンパイルするような用途ですかね) にできるとなっています。実行時にネイティブコードへの変換を行ってその場で実行する JIT (Just In Time) コンパイルも可能らしいです。ドキュメントを読んでるとプロファイルフィードバックされた動的な最適化もできそうなことが書いてありますが、まだ全部読んでいません。

LLVM を使ってできることはいろいろありそうで、たとえば C/C++ コンパイラがついてくるので、プラットフォーム非依存のコンパイル済みのバイナリ形式のプラグインをつくって、それを実行時にネイティブコードに変換したり、Risse スクリプトを LLVM 用 VM 形式で保存したり、そのままネイティブコードに変換して実行できたりするんじゃないかと考えています。

LLVM での最適化が相当望めるようならば、Risse で実装する最適化は最小限の物にして、LLVM にあとの最適化をやらせるという方法もあるので、LLVM がどれほど「つかえる」のかを見極めておきたいというのがあります。

とりあえずはコンパイル。MinGW でコンパイルが通らない。

c:\k3\mingw\bin\..\lib\gcc\mingw32\4.1.0\..\..\..\..\mingw32\bin\ld.exe:
BFD 2.16.92 20060416 assertion fail ../../binutils-2.16.92/bfd/cofflink.c:1928 

などと出る。binutils 2.16.91 でダメだったので binutils 2.16.92 をコンパイルして使ってみているのだけれどもだめ。

gcc 3.4.5 が悪いのかと思い、gcc 4.1 をコンパイルしてそれを使ってみてもだめ (状況が同じ) 。

もうちょっとよくドキュメントとか同じようにこまってる人がいないかとか、みてみますかね。

ちなみに binutils 2.16.92 のビルド方法

$ mkdir -p /projects/binutils/source
$ cd /projects/binutils/source
$ wget ftp://sourceware.org/pub/binutils/snapshots/binutils-2.16.92.tar.bz2
$ tar jxvf binutils-2.16.92.tar.bz2
$ mkdir -p /projects/binutils/source/build
$ cd /projects/binutils/source/build/
$ ../binutils-2.16.92/configure --host=mingw32  --build=mingw32 --target=mingw32 \
  --prefix=/mingw --with-gcc --with-gnu-as --with-gnu-ld --disable-shared --disable-nls
$ make  CFLAGS="-O2 -mtune=i686 -D__USE_CRTIMP -fno-exceptions" LDFLAGS=-s
$ make install

それと、GCC 4.1 ビルド方法 (C/C++のみ)

$ mkdir -p /projects/gcc-4.1/source
$ mkdir -p /projects/gcc-4.1/source/build
$ cd /projects/gcc-4.1/source
$ wget ftp://tron.um.u-tokyo.ac.jp/pub/GNU/gcc/gcc-4.1.0/gcc-4.1.0.tar.bz2
$ tar jxvf gcc-4.1.0.tar.bz2
$ cd mkdir -p /projects/gcc-4.1/source/build
$ CC=/mingw/bin/gcc LD=/mingw/bin/ld AS=/mingw/bin/as  ../gcc-4.1.0/configure \
  --with-gcc --with-gnu-ld --with-gnu-as --host=mingw32 --target=mingw32 \
  --prefix=/mingw --enable-threads --disable-nls --enable-languages=c,c++ \
  --disable-win32-registry --disable-shared --enable-sjlj-exceptions--without-x \
  --enable-hash-synchronization --enable-libstdcxx-debug
$ make
$ make install

GCC はなんかもっと正当なビルド方法があったような気がしますが思い出せません。

LLVM ビルド方法 (ここを参考に)

$ mkdir -p /projects/llvm/source
$ mkdir -p /projects/llvm/source/build
$ cd /projects/llvm/source
$ wget http://llvm.org/releases/1.7/llvm-1.7.tar.gz
$ tar zxvf llvm-1.7.tar.gz 
$ cd /projects/llvm/source/build
$ ../llvm/configure --prefix=/projects/llvm --enable-targets=x86
$ make tools-only TOOLLINKOPTSB+=-limagehlp TOOLLINKOPTSB+=-lpsapi \
 LDFLAGS+=-Wl,--no-keep-memory -r

ただし、途中でエラーになると思うので以下の2点を修正

  • llvm\lib\System\Win32\Path.inc の CopyFile の定義を修正 (sys:: が余分)
  • llvm\lib\Target\X86\X86JITInfo.cpp の 先頭に #define __CYGWIN__ を挿入

この LLVM のビルドの途中で、上記のリンカの assertion fail になります。

2006年5月1日

Risse 進捗(2)

5月か ...

Risse は AST 生成が一通り終わったのでオプティマイザ(最適化するとこ)をどうにか実装するフェーズへ。

もともとが動的な言語なのであまり最適化による効果は期待できないものの、興味のある分野なのと、(スクリプト内で多用することの是非はともかく) goto を実装するうえでのフロー解析をしてみたいのです。

しばらくの間は文献をあさる期間が続くだろうなぁ。

ちなみに TJS2 は最適化という最適化はしてません。強いて言うなら論理圧縮と定数たたみ込みぐらい。