Cコンパイラのcc1とかcc2はいつから有るのか
TinyCCのような例外的な実装を除いて、Cコンパイラはマルチパスのプログラムとして実装され、特にPCのようなメモリの制約が厳しい環境では複数のプログラムに分割される。
(いわゆるone-passのコンパイラとしてはTurbo Pascalが有る - see http://www.pcengines.ch/tp3.htm )
検索してみると、BDS-C発売時のInfoWorldの記事(80年)がヒットした。
BDS-Cはpublic domainとして提供されているので、Webで入手することができる。
ここで入手できるコンパイラはCC1 CC2の2フェーズになっている。
- CCA.ASM
; CC1: the first pass of the C compiler...
- CC2B.ASM
; cc2b.asm: ; ; Expression evaluator (code generator) ; The text pointer is always assumed to be in HL ; when evaluating expressions...
同様にCP/Mで動作するDigital Research C (CLEAR: Common Language Environment And Runtime)はプリプロセサ 0(トークナイザ?) 1(オプティマイザ?) 2(コードジェネレータ)の4フェーズとなっている。
... で、タイトルの問題の結論としては、最初から有る。UNIX哲学。
while(i<nc) { if (nc>1) printf("%s:\n", clist[i]); av[0] = "c0"; av[1] = clist[i]; av[2] = tmp1; av[3] = tmp2; av[4] = 0; if (callsys("/usr/lib/c0", av)) { // ← ★ C0 cflag++; goto loop; } av[0] = "c1"; av[1] = tmp1; av[2] = tmp2; av[3] = tmp3; av[4] = 0; if(callsys("/usr/lib/c1", av)) { // ← ★ C1 cflag++; goto loop; } av[0] = "as"; av[1] = "-"; av[2] = tmp3; av[3] = 0; callsys("/bin/as", av); // ← ★ AS t = setsuf(clist[i]); unlink(t); if(link("a.out", t) | unlink("a.out")) { printf("move failed: %s\n", t); cflag++; } loop:; i++; } nocom: if (cflag==0 & nl!=0) { i = 0; av[0] = "ld"; av[1] = "/usr/lib/crt0.o"; j = 2; while(i<nl) av[j++] = llist[i++]; av[j++] = "-lc"; av[j++] = "-l"; av[j++] = 0; callsys("/bin/ld", av); // ← ★ LD }
Unix second editionのccコマンドのソースで、既にどこかで見たような引数でどこかで見たようなコマンドを呼びだしている。(しかも、ldは-lオプションで/lib/libを補完する。)
というわけで、Cコンパイラは最初からマルチパスの複数のプログラムで構成されるシステムであった。もっとも、LLVMがMC(Machine Code)ライブラリを統合したりすることでclangは単体でコードジェネレーションまで行えるし、LTOのようなwhole program optimizationのために1ファイル入力、1ファイル出力のコンパイラというUNIX以来の伝統は崩れつつあるとも言える。