帰って来たプログラミング言語「DT」ゴールデンウィークを利用して久々にろくでもないものができたので晒しておく。「ごっこじゃないよ、兄ちゃん!」「才能の無駄遣いじゃなくて、無駄そのものだよ、お兄ちゃん。前のと何が違うの?そもそもの発端についてはプログラミング言語「DT」という記事を、実際の言語仕様についてはREADMEでも読んでもらうとして、何で再実装したのかという話。3年前に作ったのは「Whitespaceのトークンを置換した処理系をインタプリタとしてRubyで実装」したものなんだけど、何を思ったかコンパイルしてみたいと思い立ってしまい、んでそれ自体はLLVMが良く分かんなかったとか、llvmruby自体がプロジェクトとして大分アレな感じになってしまっていたとか、そういう事情で挫折してたんだけど、時は流れ、なんか見返してみたら普通にLLVMアセンブリ読み書きできるようになってたし、ruby-llvmがちゃんと動くようになってるみたいだし、ということで再実装してみることにした。そんで作り直す過程で「Whitespaceの置換ってのもなんだか面白くないな」と思ってVMのアーキテクチャと言語仕様を見直すことにした(ぱっと見だと全然分かんないけど、旧DTと新DTの間に互換性は無い)。そのせいで実際にはチューリング完全じゃなくなってると思うし、そもそもちゃんと出来てるのかどうか怪しいものだけど、とりあえず「Hi!」って出力するのとフィボナッチ数を出すのは動いたので良しとする。当初の目標だった「DTコードをネイティブのバイナリにまでコンパイルして高速に実行」ってのも実現して、DTインタプリタで250msくらいかかるfib.dtをコンパイルすると4msくらいで結果が出るようになった。似たようなRubyのコードだと大体100msくらいだったので素敵に速い。コンパイル自体はLLVMとコンパイラに任せているので大して威張れるあれでもないのだけども。高速に動いたからなんだという話なのだけども。ぶっちゃけDTよりLLVMアセンブリの方が遥かに記述力高いし書きやすい。当たり前のように構造体とか関数ポインタとか使えるのな。最初Cで書いてたVMを途中でLLVMアセンブリ直接書くようにしたけど、大して変わらない。DTで書くくらいならLLVMアセンブリ書いた方がいいです。LLVMの無駄遣いタグを付けていただきたい。DTの存在意義については疑問を差し挟む余地もなく皆無だと言っていいけど、その過程でレジスタマシンについて色々調べて、はー、こんぴゅーたーってこうやってうごいてるのかー、と勉強になったのでそれはそれで良かった。こんなの考えられるよなぁ。CPUとかコンパイラとか作ってる人達、本当に俺と同じ種類の生き物なのだろうか。Tweet2009年03月16日LLVMでwhile懲りずにesotericでDTのコンパイラを作るべくしこしことコードを書き続けているのだけど、コンパイラ(というかesotericのASTからLLVMへのブリッジ)は概ね出来たので、今は各言語のパーサを直しつつ標準ライブラリをLLVMアセンブラで書いてる。別に標準ライブラリはCで書いてllvm-gccとかでコンパイルしておけばいいっちゃいいんだけど、なんとなく、出来るだけLLVMとllvmruby以外には依存しない方針で行こうと思ったため。Cで書いたのをコンパイルするととしれっとターゲット指定されてたり外部のライブラリの関数呼んだりしてるので、ちゃんと使い方わかんないとどうなるか良く分からなかったので。LLVMアセンブラむずい...で、すぐにそれを後悔することになったわけですが。これがまぁ、今までRubyとかPerlとかばっかり書いてた身にはLLVMアセンブラはなかなか難しい。いやもちろんいわゆる「アセンブラ」よりはずっと書き易いし、それどころかDTのコード書くよりも遥かに楽(なんだろうこの本末転倒っぷりは)なんだけども、そうは言っても、ねぇ。Cだって読めはするけどあんまりまともに書いたことないし、それでアセンブラっつってもね。以下のような関数を書こうとしてハマる。stack_push(node->value);}スタックの上からn番目の値をコピーして、スタックの一番上に詰む処理ですね。ちなみにESONODEは値と次の要素へのポインタを持つスタック要素の構造体、stack_topはスタックの一番上に乗ってるESONODE、stack_pushは見て分かるようにintの値を取ってそれをスタックの一番上に乗せる関数。ここで「whileってどう書くんだろう...」と10分くらい途方に暮れる。LLVMアセンブラにはループ構文は無い。してても分かるわけもないので、llvm-gccで一旦上記のソースをコンパイルしてからllvm-disでバイトコードをLLVMアセンブラに戻して見てみることにする。それを参考に書いたのが次のコード。void}...むぅ。スタックトップを取ってきて%node_ptrがそれへのポインタになるようにする。あと、引数%nのポインタも作ってる。なんでこんなことしてるのかなーと思ったけど、ローカルの変数それ自体は一度定義したら不変なのね。ポインタとstoreとloadを使って、「変数に再代入」しているのではなくて「ポインタが差し示す先を変え」ているってことなのかな。これでCのコードでnodeやiを操作してるのと同等のことをしてる。最初の準備を終えたら次にラベルloop_condにジャンプしてる。そこでポインタ%n_ptrが差し示す先の値が0より大きいかを見て、0より大きければloop_bodyへ、0以下だったらreturnにジャンプ。loop_bodyでは%node_ptrの差し示す値が次のノードになるように変えるのと、%n_ptrが差し示す値がその時点での値から1引いたものになるように変える。loop_condへ。%n_ptrの値が0になるまではloop_condとloop_bobyを行ったり来たりして、0になった時点でreturnにジャンプだ。whileと同じ動作になる。頭の中のスパゲッティ構造体の使い方がCよりややこしいとか変数の扱いがちょっと違うとかってのもハマったけど、それはまぁ一度見てしまえばなんとなく分かる。それよりかジャンプ構文ヤバい。50」とかでプログラミングしてたとか信じ難いなぁ。そりゃスパゲッティも絡まるよ。ループ構文考えた人は偉大だなぁと思ったし、Rubyのイテレータとか神々しくさえ見えました。ぶっちゃけ、LLVMアセンブラを勉強するよりllvm-gccの使い方を覚えた方が手っ取り早いと思う。ちなみにllvmrubyで書くともっと楽ではあるんだけど、可読性はむしろ下がる印象。Rubyのライブラリ(Cで書かれてるRuby本体のコードの方)に依存するともう少し楽なのかもしれないけど、どっちにしろあんまりRubyっぽくないコードを書かなきゃいけなくなるし、最終的に生成されるコードの上でどういう遷移をしてるのかが分かり辛い。LLVMアセンブラ書くのが楽しくなってきちゃったのでこのまま進むことにするけどね。頭の訓練だと思ってやることにする。Tweet2009年03月08日LLVMを勉強中ここ数日、LLVMについて少し勉強している。何その無駄に敷居高いお仕事!誰得!でもなんか面白そう!というやりとりがあって、じゃあ本当に誰が得するのかわからないけど面白そうだからLLVMをバックエンドで使ってesotericがサポートしてるコードからバイナリを生成するコンパイラ作ろうぜ、という流れになったから。実にLLVMの無駄遣いですね。この記事ブクマするときは「LLVMの無駄遣い」ってタグを付けるといいですよ。OS向けにコンパイルする必要があった。最近は追ってないので今どうなってるかは知らない)程度だったので、実のところLLVMが何をするものなのかちゃんと分かってなくて、どうすんべーとまず調べ直すところから始める。Languageとは特に関係がないようだ(お約束)。