あとがき

 筆者は今までに3冊のコンパイラの本を書いてきて,本書は4冊目である.本書では,筆者がコンパイラについて知っていることを出来るだけ盛り込むようにした.ここであとがきとして,自分が今までコンパイラとどのようにかかわり合ってきたかを,思い出すままに記してみたい.
 筆者は,1960年に日立製作所の中央研究所に入って,初めて計算機というものを知った.当時は,自動プログラミングの研究という研究題目のもとで,アセンブラの開発が行われていた時代である.筆者の最初の仕事も,HITAC502という制御用計算機のアセンブラの開発であった.
最初に開発したコンパイラは,HIPAC103というパラメトロン計算機のFORTRANコンパイラで,1961か1962年頃ではなかったかと思う.その頃はコンパイラについてのまとまった書物はなく,2,3の論文を参考にしただけで,手探りで開発するという感じであった.コーディングは高橋延匡氏,吉村一馬氏と筆者の3人で行ったが,まとめてのデバッグは筆者が行った.これは,日本の商用計算機としては最初のコンパイラの1つであった.FORTRANというのはIBM社の製品の名前であると思ったので,それにはHARPという名前を付けた.なお,このときソースプログラムの中に書かれている実数型の定数を計算機内部の浮動小数点表現に変換するにはどうするのが良いか考えた.これは簡単なことのように思えるかもしれないが,変換の精度や変換できる範囲,変換速度や所要メモリ容量など,考えるべきことはいろいろある.このことは,通常のコンパイラの本には書いてないが,筆者の本では,このときとそれ以降で考えたことを,いつも書くようにしている(4.4.1項参照).
 2番目は,HITAC5020のFORTRANコンパイラであった.開発にとりかかったのがいつであるか記憶が定かでないが,1965年の春にはコンパイラは一応完成していたと思う.HITAC5020は,東京大学に設置された全国大学共同利用の大型計算機センタの初代の計算機として納めたものであるが,実績のある外国機を入れるべきか,国産機を育てるべきかの大激論の末に決まったものであった.そのセンタでの主要なソフトとして使われるのがFORTRANコンパイラであったので,筆者としても頑張って良いものを作ろうと思っていろんな工夫をした.それは,(1)コンパイル速度と実行速度,(2)式の演算とレジスタ割付け,(3)論理式の部分評価,などである.以下それらの内容について述べる.
(1)コンパイル速度と実行速度
 まえがきでも書いたように,最初のFORTRANコンパイラ[Back-57][Back78,81]は素晴らしい最適化を行ったが,そのためにコンパイルに時間がかかった.また,コンパイラが出来ればもうデバッグは必要ないと考えたこともあって,デバッグに対する考慮も足りなかったように思う.そこで,コンパイルが速く,しかも,目的コードの実行速度も遅くないものを目指すことにした.
 コンパイルを速くするためには,凝った最適化は出来ない.FORTRANでの最適化はループの中での配列要素のアドレス計算を速くすることが重要であるので,そこを頑張ることにした.ところで,2次元配列Aの配列要素A(i,j)のアドレスは,10章でも述べたように
   Ai,j = A1,1 + 4(i-1) + 4d1 (j-1) = A0,0 + 4i + 4d1 j
という式で与えられる.この計算をループの中でしなくてもすむようにするためには,一般には種々の最適化の方法を組み込む必要があるが,そのようなことをせずに,掛け算の表を利用することにした.すなわち,
   4d1, 8d1, 12d1, 16d1, ...
という掛け算の結果の数値の列(実際には,HITAC5020は語アドレスのマシンであるので,バイトアドレスを語アドレスに変換するための4倍は必要ない)をコンパイル時に1次元配列(掛け算のテーブルと呼んでいた)として作っておくことと,HITAC5020 が持っていたアドレス計算のための特別の命令MNI(Modify Next Instruction)を生かすことによって,上記の式のiやjの値がレジスタに入ってさえいれば,A(i,j)の値をロードしたり,足し算したりするのは2命令ですむようにした.
 その結果,最適化としては,ループの制御変数にレジスタを割付けるということをするだけで,行列の掛け算の最内側のループの命令が8ステップですむようになった.この値は,10.1.2項の数字を見てもトップに近いものである.これで,コンパイルも速く,実行も速く,という当初の目標を達成することが出来た.
 ところで,このコンパイラでとった方式は,その後のFORTRANコンパイラでは使えないことになった.その後作られたFORTRANの規格にあわないことになったからである.テーブルを使う方式では,配列要素のアドレスは,その配列の先頭番地とその配列の掛け算のテーブルから計算できる.その2つが配列を表現するとも考えられ,サブルーチンのパラメタとして配列が渡されるときは,その2つを渡すことにした.パラメタとして配列を渡すということは,その配列の次元や各次元の長さも渡すことであると筆者は考えたからである.しかし,当時作られていた多くのコンパイラでは,パラメタとして配列を渡すときはその先頭番地だけを渡すように作られていた.それがANSI FORTRAN規格を制定するときに明文化され,それがそのままISO FORTRAN規格,JIS FORTRAN規格となってしまったからである.この FORTRANは,1966年にANSI規格となったので,FORTRAN 66と呼ばれている.
(2)式の演算とレジスタ割付け
 それまでの計算機では演算レジスタが1つしかなかったのに対して,HITAC5020は16個のレジスタを持っていた.そこで,式の演算でこの複数個のレジスタを有効に生かす方法を考えようと思ったが,なかなか思いつかなかった.式の演算のコード生成部のコーディングをはじめる少し前にはっと思いついて,そのアルゴリズムで目的コードを生成することが出来た.9.2.2項で述べた方法はそのアルゴリズムをRISCマシン向きに書いたものである(RISCマシンでは,引き算や割算が非可換演算であることは問題にならないから,その分アルゴリズムは単純になっている).このコンパイラ開発が一段落した後で,そのアルゴリズムをCACMに発表した[Naka67]ところ,いくつかの論文で引用された.その中のSethi(セティ)氏とUllman(アルマン)氏の論文[Seth2-70]は,筆者の論文をもとにして,一部不十分であったところをおぎない,かつそのアルゴリズムがある意味で最適の結果を与えることを証明するものであった.ところで,それから何年か経って,同じようなアルゴリズムをすでに1959年にErshov氏が発表している[Ersh58]ことが見つかった.今では,このアルゴリズムはSethiとUllmanのアルゴリズムと呼ばれることが多い.
(3)論理式の部分評価
もう1つ最適化の工夫をしたのは,論理式の部分評価である.それは,論理式を評価していて,途中で,式全体の真偽が判明したところで,それ以後の評価を省略するようなコードを生成するアルゴリズムである.そのアルゴリズムが出来たと思って,それをコンパイラに組み込んだのであるが,そのアルゴリズムには誤りがあった.そのアルゴリズムが難しかったのは,ソースプログラムを構文解析すると同時にそのようなコードを生成しようとしたからであった.その後何年か経って,構文解析した結果の構文木からコード生成するとすれば分かりやすいアルゴリズムが考えられることと,そのアルゴリズムから構文解析と同時にコードを生成するアルゴリズムも導き出せることがわかった[中田75].以前に書いたコンパイラの本[中田81]ではその両方のアルゴリズムを比較的詳しく書いたが,最近では構文解析と同時にコード生成をし,かつ,このような最適化をすることは少ないのではないかと思って,本書では構文木からそのようなコードを生成するアルゴリズムだけを説明した(9.3.1項参照).
 このコンパイラの開発はアセンブリ言語で行われ,でき上がったコンパイラの大きさは約30K語(すなわち機械語のステップ数で約3万ステップ,バイト数では120KB)であった.当時としては大きなシステムであり,チームでの開発が必要であった.後に構造化プログラミング(structured programming)と関連して,ソフトウェアの開発はチーフプログラマチームで行うのがよいという議論があったが,このコンパイラの開発はまさにその体制で行われた.筆者がチーフプログラマ(主任プログラマ)であり,全体の設計を行って,構成が決まったところから分担を決めていった.ただし,大事なところは自分で最後までコーディング/デバッグを行った.開発人員は多いときで15人くらいであり,バックアッププログラマ(副主任プログラマ)は高須昭輔氏であった.
 ただし,筆者がチーフプログラマを務めたのはこれが最初で最後である.この後は,開発に当たっては主として管理をするようになってしまった.日本で優れたソフトウェアが生まれないのはこのような体制にもその一つの理由があるのではなかろうか.多くの会社では,一仕事をすれば後は管理者となって行くのが普通である.しかし,管理によって優れたソフトウェアが生まれるわけではない.
 管理について言えば,筆者はこの仕事では嶋田正三氏という管理者に恵まれていた.この仕事が最初の予定から遅れていることを管理者として依頼元から強く非難されても,そのことを筆者に伝えることを一切されなかったし,ここで人員の増強が必要であるという筆者の要請にはいつも応えていただいた.結局は納入予定に遅れずに完成することが出来た.
 その次に行なった仕事は,HITAC5020のメモリの上限であった64K語を128K語に拡張したマシンのためのコンパイラの基本設計,HITAC5020に仮想記憶のための機能を付加したマシンで会話型システムを実現するにあたってのシステム記述言語(PL/IWと名付けた)の開発,会話型言語の開発,などである.会話型言語としては,最初は高級電卓型のものを作ったが,それの拡張版として,プログラム片をデータとして扱うことが出来て,実行中にそれを予定開始時間順のリストに並べていきながらその順に実行するという仕掛けを入れた.それを使って仮想記憶の性能に関するシミュレーションなども行った(これは管理者としてでなく筆者が一人で作った).
 それらが一段落したところで,最初のコンパイラの本(コンパイラの技法,竹内書店,1971年)を書いた.これは比較的よく売れた本であったが,しばらくして出版社がつぶれてしまった.
 その次の仕事で思い出されるのは,LR構文解析器生成系の開発である.米国の技術論文のリスト(NTISというような名前であったと思う)からDeRemer氏のLR構文解析に関する博士論文[DeRe69]を見つけて読んでみて,これは実用になると思った.それまでは,LR構文解析は効率が悪いと思われていたようであったが,この論文では,種々の最適化をほどこすことにより,演算子順位関数による式の構文解析(5.4.3項参照)と同程度の効率で式の構文解析が出来ることが示されていたからである.
 そこで,その方式によるLR構文解析器生成系を開発することにした.ただし,管理者としてである.1970年代の初めであったと思う.ちょうどそれを開発しているときに4KB FORTRANのコンテストが行われた.これは,メモリが4KBしかないマシンで動作するFORTRANコンパイラを開発するというコンテストである.LR構文解析器はLR構文解析表の形で実現されるから,小さく出来るはずであると考え,開発中のLR構文解析器生成系でFORTRANの構文解析器を生成し,それを組み込んだコンパイラで応募するという方針をたてた.残念ながら,そのコンパイラはコンテストの締め切りには間に合わなかったが,この方式の実用性を示すことは出来たと思う.その結果を論文として発表したのが1974年2月である[小島3-74]
 その後,UNIXが広く世の中で使われるようになってきて,その中にあるyaccというLR構文解析器生成系もよく使われるようになった.yaccの参考文献としてあげられるのは1975年の[John75]であるから,我々の仕事が決して遅れていたわけではない.しかし,UNIXやyaccは,自分の使いたいものを作り(使う人の身になって作り),周りの人にも使ってもらって改良を重ねていったから,優れたソフトウェアになって多くの人に使ってもらえるものになったと言えるであろう.我々はそのような努力はしなかった.
 なお,筆者が最初にLR構文解析器生成系が実用になると思ったのは,最適化によって効率のよい構文解析器が生成できると思ったからである.そこで,以前の本[中田81]ではその最適化の方法を述べた.しかし,LR構文解析器が一般に使われるようになると,効率のことはあまり問題にされなくなってきたし,計算機が速くなって,実際にもあまり問題にならなくなってきたので,本書ではそれは省略した.
 1970年代の後半あたりから,日立はベクトル計算機を開発するようになった.そのような計算機のコンパイラにも使えるアルゴリズムの論文もすでにいくつかあったように思うが,筆者には分かりにくいものであったので,筆者なりに分かりやすいアルゴリズムを考えた[中田77][中田81].ベクトル化や並列化に関してはその後多くの研究がされている(12.6節12.7節12.8節参照).
 1979年に日立製作所から筑波大学へ移ってしばらくしたころ,スタック型のレジスタを持った浮動小数点演算のチップを使ったマシンのコンパイラの設計について日立から相談を受けたことがある.スタック型のレジスタを式の演算で有効に使うアルゴリズムが考えられていないわけではなかったが[Brun2-75],筆者にはわかりにくいものであったので,自分で改めて考えて[中田81b]それを提案した.それについては,9.2.2項で少しだけ触れた.
 筑波大学では佐々政孝氏と一緒に学生の指導をするようになって,属性文法とそれを使ったコンパイラ生成系を考えるようになった.というより,佐々氏にいろいろ教えてもらったと言うほうが正確である.本書の属性文法の最初の例(6.3.1項)も佐々氏の資料から拝借したものである.
 その後は,正規表現とオートマトンに関する拡張を試みたり(4.4節参照),正規右辺文法のLR構文解析法を考えたり(5.3.7項参照),ストリーム型のデータが扱える言語とそのコンパイラを考えたり,正規右辺文法に属性評価規則を付加した正規右辺属性文法やそれによるコンパイラ生成系を考えたりした(6.3.5項参照).
 1991年頃から,筑波大学の物理学系,電子情報工学系,構造工学系の教官を中心としてCP-PACSプロジェクトが始まった.CP-PACSはComputational Physics by Parallel Array Computer Systemの頭文字をとって付けられた名前であり,計算物理学での計算をすることを主たる応用とする超並列計算機を開発するプロジェクトで,世界最高の性能のマシンの開発を目標とするものであった.筆者は,そのマシンのソフトウェアシステムを考えるということで参加したが,主としてコンパイラに関して協力した.実際の開発は日立製作所が担当した.
 超並列機で目標の高性能を実現するためには,n台のノードマシンを並べたシステムで,1台のマシンのn倍に近い性能を出す必要があるが,その前に,まずノードマシンを性能の高いものとする必要がある.しかし,そのノードマシンとして使う予定のマシン(ワークステーション用のマシン)では,計算物理学の計算のような大規模な配列演算に関してキャッシュが有効に働かず,目標とする性能が出ないことが分かった.そこで中澤喜三郎氏をリーダとするアーキテクチャのグループから,スライドウィンドウ(12.4.3項参照)が提案された.中澤氏から「このアーキテクチャだとコンパイラが大変でしょうね」と言われたとき,筆者は,考えてみましょうと答えた.また,コンパイラの立場からそのアーキテクチャに対する注文として,スライド数を任意に指定できるなどの一般化を提案した.コンパイラのアルゴリズムとしては,筆者の同僚の山下義行氏がそのアーキテクチャに対するソフトウェアパイプライニングのアルゴリズムを考え出した.実は,我々は不勉強で,すでにそのような基本的なアルゴリズムが存在することを知らずに,独自に考え出してしまった.もちろんその基本的な考え方だけでなくスライドウィンドウ特有のアルゴリズムも考える必要があったし,さらに,多重ループのソフトウェアパイプライニングも考えた(12.4.3項参照).また,ループ中にif文がある場合の最適なソフトウェアパイプライニングの方法も考えた(12.4.4項参照).ただし,この最後のものについては,実際のコンパイラで実現されるところまでは行かなかった.
 1996年にこのマシンが完成したとき,LINPACKというベンチマークで,368.2ギガFLOPS(floating point operation per second)という世界1の性能を達成することが出来た.ただし,数ヶ月後には,アメリカのエネルギー省の,CP-PACSよりずっと大規模なプロジェクトASCIで開発されたマシンに抜かれてしまった.
 筆者は大学へ移って以来ずっとコンパイラの講義を行なってきた.最初は最適化も含めて筆者の知っていることを全部講義した.それを本にしたのが2冊目のコンパイラの本[中田81]である.
 その後は,最適化の分野での研究が広がっていること,最適化までを学部の1学期の講義で行なうのは困難であることなどから,学部での講義では最適化の話をせず,最適化については大学院で講義するようになった.また,コンパイラを理解するには,簡単な言語でもいいからその言語のコンパイラのプログラムを完全に理解するのがいいと思って,Wirth氏のPL/0コンパイラ[Wirt76]のプログラムを解説してきた.しかし,それがPascal言語で書かれていることによるプログラム構造と,関数機能がないことなどが気になっていたので,ある時から,PL/0言語に関数を入れたPL/0'言語に変更し,そのコンパイラとしてはC言語で新たに書いたものを講義するようになった.そのPL/0'コンパイラを例題として,それを理解するのに必要なものを中心にして本にまとめたのが3冊目のコンパイラの本[中田95]である.
 なお,講義では最初,まず講義ノートを自分のノートに書いて,それを黒板に書きながら講義したが,本が出版されてからは,その中の図をOHPシートにとったもので講義をするようになった.そのうちに,構文解析の講義では計算機の画面で構文解析の動きを説明したくなった.筆者はパソコンとしてはマッキントッシュを使っていたが,マッキントッシュにHyperCardが出現したとき,HyperCardでGUIが使えることとプログラム言語HyperTalkが使えることを利用して,それを実現することにした.最初は構文解析の動きを見せるものだけを作ったが,その後,コンパイラの講義のすべての内容をHyperCardで表現するようにした.
 これを始めたのは,10年前の1989年くらいである.その当時はプロジェクタの性能が悪くて,薄暗い映像しか出なかったが,それでも学生には好評であった.ただし,試験をしてみると成績は必ずしも良くはなかった.映像は,分かったような気にさせるのにはよいが,学生の頭の中に定着させるためには,自分で考える時間を持たなければならない.そこで,出来るだけ演習問題を並行してやってもらうようにした.プロジェクタの性能は年々向上し,最近では明るい映像が見られるようになってきた.
 本書は,4冊目のコンパイラの本として,コンパイラに関して,これまで筆者が講義をしてきたり,自分で考えたり,調査してきたりしてきたものを出来るだけ盛り込むことにしたが,そのために,さらに追加の文献調査をしたり,ものを実際に作ってみることを行なった.
 たとえば,オブジェクト指向言語のコンパイラについても書くことが出来るように,簡単なオブジェクト指向言語のコンパイラを書いてみた.ちょうどJavaが出現したころで,その言語が気に入ったことと,ウェブのブラウザに表示する機能があることから,まず最初はjava0と名付けた簡単な言語のコンパイラを作成し,次には,そのコンパイラの動きをブラウザに表示するアプレットとした.追加の調査をしたものとしては,オブジェクト指向言語の実現法や,最適化に関するいくつかの項目などがある.
 しかし,「まえがき」でも述べたように,まだまだ不足してる項目があるし,とりあげてある項目の中でも十分記述されていないものもある.たとえば,オブジェクト指向言語での最適化,最適化のための解析技術としての記号解析(symbolic analysis),タスク並列化,などが不足している.また,とりあげた多くの最適化項目に関しては,今後も新しい方式が研究されていくものと思われる.それらについては,今後,機会があれば追補改訂をしていきたいと思っている.