Loment
日本語

システム · 進行中

Loment VM

プログラムを走らせる第二の方法。バイナリは、それを要求に応じて採用し、 採用するかどうかをバイナリがすでに携えているものだけを読んで決める実行者に手渡せる。 速い道ではなく、プログラムを書きやすくする方法でもない。リポジトリには動く インタプリタが存在する —— パッケージはまだそれを同梱しておらず、この設定は コンパイラが出すものを変えない。

存在するもの、存在しないもの

リポジトリは今、動くインタプリタ層、T0 から T3 を携えている。リンカが 読むのと同じ中間形式を読む。T0 はその形式を直接実行する。T1 は出力を一バイトも変えずに フィードバックプロファイルを記録する。T2 は実行ループの手前で命令をひとつずつデコードし、 コーパス上で T0 の 6.1×、出力はバイト単位で同一。コンパクトな 直列化は同じユニットのテキスト形式の 28.5%。T3 はガード付きの特化と 戻り道を加える。

存在しないのは、そのどれもがパッケージの中にないことである。インタプリタは リポジトリの tools/lvm.py 一族で、Python で書かれている。リリースが同梱する バイナリの中にはなく、インストール済みツールチェインでこの方法でプログラムを走らせることは できない。設定も成果物を変えない: 今日 choose vm と no_vm の出力は バイト単位で同一である。

プロジェクトが以前にも抱えた借りである。runtime 設定は、 何もしないまましばらくの間成果物に宣言され、後にその主張に見合うところまで引き上げられた。 これはその形の再来である: エンジンはある、出荷はない。このページはどちらがどちらかを、 読者に見つけさせるのではなく、自分で言う。

何ではないか

三つの読み方が誤っている。いずれも読者が持ち込みがちなものである。これらを設計より先に 置く。誤って言われると、これはすべてをやろうとしてそのどれもまともにやらないものに なってしまうからだ。

速い道ではない

逆である。Loment のネイティブな道はすでに、独立性と引き換えに速度を手放しており、 そもそも速くない: docs/212 の配列ベンチマークで C の -O2 に対して 10.7 倍遅れている —— 記録された測定値であり、常時の約束ではない。VM はその上に載る層である。 この線の軸は性能ではない。逃げ道はまったく別の線にある。

Write once, run anywhere ではない

その仕事はとうの昔にコンパイル時に与えられた: ひとつの IR、三つの出力 —— Linux ELF、Windows PE、あるいは freestanding オブジェクト。バイトコード形式が、 それよりポータビリティに近いわけではない。そしてこの主張は自らのテストに落ちる: もしポータビリティが要点なら、判据は「ターゲットをひとつ足すのに新しいバックエンドを 要さない」ことになるはずだが、ターゲットは二つある。

プログラムを速く書く方法ではない

それは別の一群のものであり、これが触れるのはそのどれでもない: 他の六つの表層構文、 フォーマッタ、言語サーバー、コンパイル時の構文定義、コーディングエージェント向けに 書かれたガイド。LVM が到来するのは、プログラムが書かれた後である。

三つの部分からなる設計

バイトコードは新しい発明ではない

それはコンパイラがすでに出力する中間表現の直列化である。同梱のバック エンドはスタックマシン —— 値ごとにスタックの一スロット、引数はスタック上、集約は隠れた 結果ポインタを経由 —— なので、その部分集合の完全な実行モデルはすでに存在し、 すでに実行されている。欠けているのは、それを実行する段である。LVM は同じ形の 第四の読み手となるだろう: 新しいフロントエンドも、新しい中間表現も、 新しい型システムもない。

第五の設定

choose にはすでに四つあった —— プログラムが使ってよい層、メモリをどう回収するか、 バイナリが runtime を積んでよいか、自分自身の外に届いてよいか。vm が第五であり、 このプログラムはどの実行モデル向けか? に答える。書かなければ no_vm になり、それはこの設定が存在する前にはすべてがそうだったものである。 0.1.4.6 からはリリースビルドもこれを受け入れる。今日それで買えるのは宣言であって、異なる成果物ではない。

その値はあるかどうかを述べ、決してどれかを述べない。それは runtime 設定がすでに従っている規則である: 中に何が座るかは何年もかけて 変わるし、今日の段を符号化した名前は、明日のものを加えるのに改名を強いられることに なる。

ゲート、そしてそれが開いた理由

0.1.4.6 まで、リリースビルドは choose vm をコンパイル時に 拒否した。開発チャネルだけが受け入れた。規則は乱暴だった —— 使えるエンジンが存在する まで、リリースではそれへの呼び出しはすべて違法 —— それにより、インストール済みの ツールチェインは決して果たせない約束を受け入れることがない。果たされない約束は 拒否より悪い。

0.1.4.6 はそれを開く。拒否の前提が真でなくなったからである: 最初の使えるエンジンが 来た。設定は今、両方のチャネルで宣言であり、訂正は削除ではなく記録される —— かつて 拒否を要求した検査は今、受容を要求する。だから「このゲートは存在した、そしてこれが 開いたときだ」が可視のまま残る。

ゲートはリポジトリが持っていなかった概念を必要とした: このコンパイラはリリースビルドか、 開発のものか? 設計はそれをひとつの定数に収める。片側にひとつの値、 パッケージ時に反転する。一行なのは意図的である —— パッケージされた言語ガイドは変更 なしにディストリビューションへコピーされるので、「パッケージ内のコピーはリポジトリ内の コピーである」が検査可能なまま残り、一行はその規則を曲げる最小の方法だからだ。代価も 一緒に述べられる: 出荷されるコンパイラの種はもはやリポジトリのものとバイト単位で等しく ないので、判定基準は同一から再現可能に等しく、その差はちょうどその一つの 定数へと下がる。

はしご

VM は一つのものとしてではなく、段として読む。それぞれの段は到達可能でなければならず、 反証可能でなければならない —— 「JIT を書く」は段ではない。何もそれを反証できないからだ。 部品表はこれを大規模にやった二つのシステムから取られている。実装はプロジェクト自身のものに なり、それがそうであり続けるかは未決である。

T0 —— バイトコードと直接インタプリタ

床。リポジトリに構築済み。判定基準: この方法で走らせた プログラムは、原生成物とバイト単位で同じ出力と同じ終了コードを生む。

T1 —— フィードバック

型、分岐、呼び出し地点のプロファイル。リポジトリに構築済み。 判定基準: プロファイルが成果物の中に入り、第三者が読める —— そしてそれを切ると成果物はバイト単位で変わらない。

T2 —— ベースラインコンパイラ

最適化せずに解釈のコストを取り除く。リポジトリに構築済み —— コーパス上で T0 の 6.1×。判定基準: 測定可能に T0 より速く、意味論的 に同一。

T3 —— 戻り道のある最適化コンパイラ

間違っていたと分かった推測は、放棄できなければならない。リポジトリに構築済み。 対象は推測を背負う唯一の最適化。これがこの段で唯一の厳しい判定基準: 推測が放棄された後、プログラムの結果は原生成物と一致する。

T4 —— 世代別かつ並行の回収

他の段の延長ではない唯一の段。回収の設計は、これがここでは全くできないと以前は言って いた: バックエンドはポインタをローカルと引数スロットにしか置かず、それが通常の ライトバリアを排除する。その理由はもう成り立たない —— ポインタを整数に する方法が現れ、世代別の半分は回収の側にカードテーブルと ライトバリアを伴って着地した。まだ先にあるのは、それをVM の内部で行うことで あり、事前に行うのとは別の問題である。

リポジトリは T0 から T3 を携えている。パッケージはそのどれも携えていない。 インタプリタを構築することと、それを配布物に載せることは二つの別の段階であり、終わっている のは最初の一つだけである。上の段は計画の形であり、予定表ではない。

何のためのものか

まず冷水を: 大規模プロジェクトが最もよく望む二つのこと —— より速くビルドする、より速く 走らせる —— はいずれも範囲外である。ビルド時間はコンパイラの内側の問題であり、 実行時間はこの線にとって間違った軸である。

しかし「大規模」は、欠陥ではなく構造的な問題をすでに一つ露呈させており、これを癒やせる のはこれだけである。出力されるシンボルは平坦である:モジュールが百個 あれば、関数名は手で大域的に一意にしなければならず、同じ小さな名前をそれぞれ定義する 二つのモジュールは拒否される。これは「ひとつの静的リンク」というモデルの帰結である —— 大規模プロジェクトの古典的なスケーラビリティの壁が、言語自身の構造によって、利用者の 面前にじかに押し出されたものだ。リポジトリはすでに、過大なプロジェクトのコーパスを 判据として走らせているので、これは推測ではなく測定である。

実行時にモジュールを採用する

ネイティブの成果物にモジュールを足すことは、プログラム全体を再リンクすることを 意味する。ここでは、ひとつの単位をロードすることを意味する。

再リンクせずにひとつを差し替える

モジュールを変え、それを再ロードし、プログラムの残りはそのままにしておく。

自分が書いていないコードを走らせる

第三者の拡張、あるいは機械が書いたもの。プロジェクト自身の考えに、初めて 仕事が生まれるのはここである。リンク時には、ポート表、ユニットが届いてよいと 宣言する範囲、持っていないものの記録、そして回収の数値は監査である —— 後から 読むものだ。ロード時にはそれらはゲートになる —— 何かが走る前に、それで 決めるものだ。

これがこのページの拠って立つ唯一の文であり、それは反証可能である: 拡張モジュールを足すことは、プログラム全体を再リンクすることを意味しない —— そしてそれがロードされる前に、本来届くべきでないものに届きうるかどうかを知ることが できる。

主張しないこと

設計記録はこの一覧を保っているが、それは記録の中ではなく、このページに載るべきもので ある。

新しいということ

その中に新しい煉瓦はひとつもない。成果物に携えられ、ロード時に判定される実行の証拠は 1990 年代からある; 証明できないものをロードすることを拒むのは、サンドボックス化された runtime がそう動くやり方である; ひとつの入力に複数の実行モデルというのは、最大の モバイル runtime がやっていることである。置き方が違うかもしれないのは継ぎ目であって、 部品ではない。

その二つを誰もやっていないということ

全体を通しての言い回しは検索で見つからないであり、それはやられていない とは同じではない。この主張を殺すはずの二つの検索は、未解決の判据として書き留められて おり、網羅的に走らせたわけではないという正直な注記が添えられている。

役に立つということ

その箱は今日、空である。そして上の第三の役割は、誰も測っていない数字に依っている —— あの過大なコーパスのどれだけが、本当に遅れてロードされるべきコードなのか。その数字が ほぼゼロなら、その役割は美しい話にすぎない。