第7回の宿題 ── 最初のアセンブラは、何が組んだのか
第7回で「最初の Unix は PDP-7 のアセンブリで書かれた」と見て、宿題を残しました ── アセンブリを機械語にするアセンブラが要るのに、その最初のアセンブラは何が組んだのか? PDP-7 には、最初アセンブラすらありません。鶏がいないのです。答えは意外なほど物理的でした。Thompson は別の機械を使ったのです。すでに立派に動いていた大型機 GE-635(OS は GECOS)で PDP-7 向けの機械語を作り、それを紙テープに刻んで PDP-7 まで持って行った。この「よその機械を借りる」やり方を、クロスアセンブルと呼びます。
第7回の自己ホスティングの輪 ── アセンブラが自分自身を組む ── は、いったん回り始めれば強い。でも回す前は、PDP-7 にアセンブラが1本も無い。「アセンブリを機械語にするにはアセンブラが要るのに、そのアセンブラを機械語にするアセンブラが無い」。第0回の鶏と卵、そのものです。Thompson の解は、この機械の中で解こうとしないことでした。
ふつう「アセンブラで組む」と言うと、同じ機械の上で、その機械向けの機械語を作る(ネイティブ)。でも、アセンブラを動かす機械(host)と、機械語を向ける機械(target)は、別々でかまわない。GE-635 の上で動くアセンブラ(GEMAP のマクロ)が、PDP-7 向けの機械語を吐く ── これがクロスです。下の図で、ネイティブとクロスを切り替えて、なぜ最初はクロスしかなかったかを見てください。
クロスでできた PDP-7 向けの機械語は、GE-635 の中にあります。これを PDP-7 へ移さねばならない。ネットワークなどない時代です。どうしたか ── 紙テープに穴を開けて出力し、そのテープを手に持って PDP-7 まで歩いて運び、読み取り機に通した。第6回で「O-code を新マシンへ運ぶ」と言った、あの「運ぶ」の正体が、この穴あきテープの徒歩搬送です。下の図で、その一連を動かしてみてください。
クロスの発想はこうです ── 「target で target を作らなくていい。動く host を1台借りればいい」。鶏がいない新機種でも、よその鶏から卵をもらえる。これは第3回の「既存の cc を借りて gcc を着火」、第6回の「O-code を運ぶ」、そして現代のクロスコンパイル(新しい CPU や組込み機器向けを、手元の PC で作る)と、まったく同じ骨格です。半世紀、ずっと使われている“新機種の産声のあげ方”。
クロスで PDP-7 に最小限の道具が載り、カーネル・エディタ・シェル・そしてアセンブラが揃うと、Unix は自分自身の上でプログラムを書きテストできるようになります(self-supporting)。Thompson は仕上げに、PDP-7 ネイティブのアセンブラを書いて、GE-635 を使うのを完全にやめました。第7回の図で点滅していた「最初の as は何が組んだ?」の答えが、ここで埋まります ── 最初の as は、GE-635 のクロスアセンブラが組んだ。輪は、よその機械から着火されたのです。
では「その GE-635 のツールチェーンは何が作った?」── また下段送りに見えます。でも今回は少し違う。GE-635 もかつては新品で、さらに古い機械や、もっと原始的な手段から立ち上げられた。すべての“既にある機械”は、かつて新機種だった。クロスは「1つ前に立ち上がった機械」を借りる連鎖で、これは有限です。さかのぼると、いつか ── 借りる host が1台も無かった、いちばん最初の機械に着く。そこにはクロスの魔法が効きません。
クロスの連鎖をさかのぼると、必ず「借りる相手がいなかった最初の機械」に着きます。世界で初めてのそのコンピュータには、アセンブルしてくれる別の機械も、読み込ませる完成済みの紙テープもありません。ではその機械には、どうやっていちばん最初の命令を入れたのか? ── 次回、ついに底です。答えは、人間の手。
(1) クロスアセンブルは鶏と卵を解決しません。「この機械の中で解く」のをやめ、別の(既に動く)機械へ問題を移すだけです。ただしその連鎖は有限で、さかのぼれば最初の機械(第9回)に底を打ちます。無限後退ではありません。
(2) GE-635/GECOS/GEMAP、紙テープ搬送、self-supporting へ移る時期などの細部は、資料により表現に幅があります(骨格=「GECOS 上のクロスアセンブラ→紙テープ→PDP-7→やがて自前アセンブラで自立」は、Ritchie 自身の回想などで一致)。(3) 図はワークフローを模式化したもので、実機の外観・操作を厳密に再現したものではありません。
第7回の宿題「最初のアセンブラは何が組んだか」の答えは、別の機械だった。生まれる前の PDP-7 にはアセンブラが無く、自分の中では始められない。そこで Thompson は、すでに動く大型機 GE-635(GECOS)の上でアセンブラ(GEMAP のマクロ)を動かし、PDP-7 向けの機械語を作った ── host≠target のクロスアセンブル。できた機械語は紙テープに穴で刻み、手で運んで PDP-7 に読ませた。第6回「O-code を運ぶ」の、泥くさい実物である。
やがて PDP-7 にカーネル・エディタ・シェル・アセンブラが揃い、Unix は自分の上で開発できる(self-supporting)ようになり、Thompson はPDP-7 ネイティブのアセンブラを書いて GECOS を卒業した。クロスの急所は「target で target を作らなくていい、動く host を借りればいい」── 第3回の既存 cc、現代のクロスコンパイルと同じ骨格だ。ただしこれは鶏と卵を別の(既にある)機械へ移すだけ。連鎖は有限で、さかのぼれば借りる相手がいない最初の機械に着く。── 次回、ついに底。最初の1バイトは、人間の手で入れられた。
印刷 / PDF 化:⌘+P(Windows は Ctrl+P)。画面では図1のボタンでネイティブ/クロスを切り替え、図2の「▶」で紙テープ搬送のワークフローを再生できます。「答えを見る」で解答が開きます。