11 ms·
The earliest versions of the first C compiler known to exist
- Joker_vD 2y agoFunnily enough, it is emphatically not a single-pass compiler.
- dbrower 2y agoI don’t think anybody thinks or thought it was.
- int_19h 2y agoI thought it would be, given that C is designed in such a way that a single pass ought to be sufficient. Single-pass compilers were not uncommon in that era.
- Joker_vD 2y agoWas it really designed this way? I keep hearing this claim but I don't think Ritchie himself actually confirmed that? Also, notice how the functions call each other from wherever, even from different files, without need of any forward declarations, it simply works, which, as I have been repeatedly told, is not something a single-pass compiler can implement :)
- int_19h 2y agoI mean, I don't know if it was ever explicitly stated, but consider: parsing requires at most one token of lookahead - assuming that you use the lexer hack to disambiguate declarations; and in earliest versions of C without typedef, you don't even need the hack because all declarations must start with a keyword. You cannot reference any declarations - not types, not variables, not functions - until they are defined, with a special exemption for pointer types that is precisely one case where the compiler doesn't care because a pointer is a pointer. Altogether, C design permits and even encourages a naive implementation that literally does a single pass parsing and emitting highly unoptimal, but working assembly code as it goes (e.g. always use stack for locals and temporaries). There's also stuff like "register" which is incredibly useful if you're only doing a single pass and of very dubious utility otherwise. I find it hard to believe that it is all a happy coincidence. Regarding functions, it only works if the function returns int and you match the types correctly for any call that doesn't have the prototype in scope. I believe this to be one of the relict B compatibility features, BTW, since that's exactly how it also worked in B (except that int is the only type there so you only had to match the argument count correctly).
- diginova 2y agoAlso read how a compiler can be written in the same language - https://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29 https://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29 https://stackoverflow.com/a/18247926/15566831 https://stackoverflow.com/a/18247926/15566831
- aap_ 2y agoProbably one of my favorite pieces of software of all times. Learned so much from this!
- gus_massa 2y agoDo you remember any interesting anecdote you can share?
- aap_ 2y agoAnecdote probably not. But i learned how a compiler works from it and reconstructed the B compiler based on it (found here: https://github.com/aap/b https://github.com/aap/b, warning: repo is messy, will clean up more soon hopefully).
- bluetomcat 2y agoInteresting usage of "extern" and "auto". Quite different from contemporary C: tree() { extern symbol, block, csym[], ctyp, isn, peeksym, opdope[], build, error, cp[], cmst[], space, ospace, cval, ossiz, exit, errflush, cmsiz; auto op[], opst[20], pp[], prst[20], andflg, o, p, ps, os; ... Looks like "extern" is used to bring global symbols into function scope. Everything looks to be "int" by default. Some array declarations are specifying a size, others are not. Are the "sizeless" arrays meant to be used as pointers only?
- Joker_vD 2y agoYes, pretty much. To be fair, C at this point was basically BCPL with slightly different syntax (and better char/string support). The introduction of structs (and then longs) changed it forever.
- kragen 2y agoBCPL had a lot of features C didn't have at this point and still doesn't. You mean B.
- Joker_vD 2y agoCould you elaborate on those features? From the top of my head, those are: nested functions — those always were of dubious usefulness compared to the implementation difficulties needed; labels are actual constants, so computed GOTO is available — that's definitely a feature standard C still doesn't have; manifest constants — this one is Ritchie's most baffling omission in the language; multiple assignment — it's not actually parallel so merely a syntax nicety (with a footgun loaded); valof-resultis — while very nice, it's also merely a syntax nicety, "lvalue := valof (... resultis expr; ...)" is the same as "{... lvalue = expr; goto after; ... } after: ;". What else is there? Pointless distinction between the declaration syntax of functions and procedures?
- kragen 2y agoThat includes everything I was thinking of and several things I didn't know about.
- tanelpoder 2y agoThe first publicly available version of Oracle Database (v2 released in 1979) was written in assembly for PDP-11. Then Oracle rewrote v3 in C (1983) for portability across platforms. The mainframes at the time didn't have C compilers, so instead of writing a mainframe-specific database product in a different language (COBOL?), they just wrote a C compiler for mainframes too.
- chasil 2y agoUNIX was ported to the System/370 in 1980, but it ran on top of TSS, which I understand was an obscure product. "Most of the design for implementing the UNIX system for System/370 was done in 1979, and coding was completed in 1980. The first production system, an IBM 3033AP, was installed at the Bell Laboratories facility at Indian Hill in early 1981." https://web.archive.org/web/20240930232326/https://www.bell-labs.com/usr/dmr/www/otherports/ibm.pdf https://web.archive.org/web/20240930232326/https://www.bell-...
- jdougan 2y agoInteresting. Summer 84/85 (maybe 85/86) I used a port of PCC to System/360 (done, I believe, by Scott Kristjanson) on the University of British Columbia mainframes (Amdahls running MTS). I was working on mail software, so I had to deal with EBCDIC/ASCII issues, which was no fun. I sometimes wonder if that compiler has survived anywhere.
- chasil 2y agoz/OS 3.1 is certified for UNIX 95, if this list is correct: https://www.opengroup.org/openbrand/register/index2.html https://www.opengroup.org/openbrand/register/index2.html That would include a C compiler, but yours is probably on tape somewhere. Linux has been on this list, courtesy of two Chinese companies.
- dboreham 2y agoWriting something that large in assembly is pretty crazy, even in 1979!
- dark-star 2y agoMy favorite function, which some might say even made it into Windows ;-) waste() /* waste space */ { waste(waste(waste),waste(waste),waste(waste)); waste(waste(waste),waste(waste),waste(waste)); waste(waste(waste),waste(waste),waste(waste)); waste(waste(waste),waste(waste),waste(waste)); waste(waste(waste),waste(waste),waste(waste)); waste(waste(waste),waste(waste),waste(waste)); waste(waste(waste),waste(waste),waste(waste)); waste(waste(waste),waste(waste),waste(waste)); }
- retSava 2y agoBut why? Waste (compiled) binary space? Or source code space, perhaps for early employee metrics gaming purposes? And don't answer "to waste space of course" please. :)
- ajb 2y agoThere's nothing in that repo that says, but at a guess: old machines often had non-uniform ways to access memory, so it may have been to test that the compiler would still work if the binary grew over some threshold. Even today's machines often have a limit as to the offset that can be included in an instruction, so a compiler will have to use different machine instructions if a branch or load/store needs a larger offset. That would be another thing that this function might be useful to test. Actually that seems more likely. It might be instructive to compare the binary size of this function to the offset length allowed in various PDP-11 machine instructions
- bustling-noose 2y agoYes it seems like this is something to do with hardware testing. Maybe memory or registers or something that needed just X bytes etc for overflows or something. It’s really random and the only person who would know it is the one who wrote it :)
- bluetomcat 2y agoWild guess: it was a way to offset the location of the "main" function by an arbitrary amount of bytes. In the a.out binary format, this translates to an entry point which is not zero.
- ChrisMarshallNY 2y agoThis is my favorite function :): https://github.com/mortdeus/legacy-cc/blob/936e12cfc756773cb14c56a935a53220b883c429/last1120c/c10.c#L11 https://github.com/mortdeus/legacy-cc/blob/936e12cfc756773cb...
- johnisgood 2y agoWhat is the point of it?
- rasjani 2y agoWithout actually knowing, i'd guess that would generate bytecode's that could be modified later by patching the resulting binary ? I remember few buddies using similar pattern in ASM that just added n NOP's into code to allow patching and thus eliminating possible recompilation..
- ChrisMarshallNY 2y agoI suspect that’s it. There was a lot of self-modification, going on, in those days. Old machine language stuff had very limited resources, so we often modified code, or reused code space.
- agumonkey 2y agowarm up the stack ? (no idea to be honest)
- tanelpoder 2y agoThe C alternative for the hardware "halt and catch fire" instruction?
- kps 2y agoThe C alternative for the Fortran COMMON block.
- aap_ 2y ago
- FeistySkink 2y agoMissed opportunity not calling it LegaC.
- smackay 2y ago1972 is the answer to the question on the lips of everybody too busy to look at the source files.
- jeff_carr 2y agoThe first 4 commits in GO are: commit d82b11e4a46307f1f1415024f33263e819c222b8 Author: Brian Kernighan <bwk@research.att.com> Date: Fri Apr 1 02:03:04 1988 -0500 last-minute fix: convert to ANSI C R=dmr DELTA=3 (2 added, 0 deleted, 1 changed) :100644 100644 8626b30633 a689d3644e M src/pkg/debug/macho/testdata/hello.c commit 0744ac969119db8a0ad3253951d375eb77cfce9e Author: Brian Kernighan <research!bwk> Date: Fri Apr 1 02:02:04 1988 -0500 convert to Draft-Proposed ANSI C R=dmr DELTA=5 (2 added, 0 deleted, 3 changed) :100644 100644 2264d04fbe 8626b30633 M src/pkg/debug/macho/testdata/hello.c commit 0bb0b61d6a85b2a1a33dcbc418089656f2754d32 Author: Brian Kernighan <bwk> Date: Sun Jan 20 01:02:03 1974 -0400 convert to C R=dmr DELTA=6 (0 added, 3 deleted, 3 changed) :100644 000000 05c4140424 0000000000 D src/pkg/debug/macho/testdata/hello.b :000000 100644 0000000000 2264d04fbe A src/pkg/debug/macho/testdata/hello.c commit 7d7c6a97f815e9279d08cfaea7d5efb5e90695a8 Author: Brian Kernighan <bwk> Date: Tue Jul 18 19:05:45 1972 -0500 hello, world R=ken DELTA=7 (7 added, 0 deleted, 0 changed) :000000 100644 0000000000 05c4140424 A src/pkg/debug/macho/testdata/hello.b
- ModernMech 2y agoI thought the first C compiler was written in B.
- indoordin0saur 2y agoYes. I'm not an expert in compilers, but how is the first c compiler also written in C? How did they compile the compiler?
- ModernMech 2y agoThere's a thread here which talked about it: https://news.ycombinator.com/item?id=26721305 https://news.ycombinator.com/item?id=26721305
- deleted 2y ago[deleted]
- 9rx 2y agoIf we had the full change history you would see that it is written in B. New features were added and changes were iteratively made along the way, but it is the same codebase. Nowadays we'd pick some change point and call it B v2, but back then they named that point C.
- xenadu02 2y agoThat's not quite correct. See my comment here: https://news.ycombinator.com/item?id=43465698 https://news.ycombinator.com/item?id=43465698 B was bootstrapped in BCPL, then rewritten in B to be self-hosting. But the transition from B to NB (New B) to C was continuous evolution. Thompson or Richie would add a feature to the compiler, compile a new compiler, then change the compiler source to use the new feature. If you did not have a sufficiently new enough B/NB/C compiler you could not compile the compiler and there was no path maintained to deal with that. You went down the hall and asked someone else to give you the newer compiler. There also wasn't a definitive point where NB became C... they just decided it had changed enough and called it C.
- 90s_dev 2y agoThe thing I always loved about C was its simplicity, but in practice it's actually very complex with tons of nuance. Are there any low level languages like C that actually are simple, through and through? I looked into Zig and it seems to approach that simplicity, but I have reservations that I can't quite put my finger on...
- bewo001 2y agoforth would come to mind, some people have build surprising stuff with it though I find it too low-level.
- ChrisRR 2y agoIt depends what you mean by simple. C still is simple, but it doesn't include a lot of features that other languages do, and to implement them in C is not simple. C is simple for some use cases, and not for others.
- 90s_dev 2y agoThe appeal of C is that you're just operating on raw memory, with some slight conveniences like structs and arrays. That's the beauty of its simplicity. That's why casting a struct to its first argument works, why everything has an address, or why pointer arithmetic is so natural. Higher level langs like C++ and Go try to retain the usefulness of these features while abstracting away the actuality of them, which is simultaneously sad and helpful.
- colejohnson66 2y ago> That's why casting a struct to its first argument works Until WG14 makes everything you love about C "undefined behavior" in the name of performance.
- worik 2y ago> Until WG14 makes everything you love about C "undefined behavior" in the name of performance. What do you mean? I just looked up WG14 and I cannot see what you mean A link perhaps? Am I going to have to "pin" my C compiler version?
- tempodox 2y agoHighly interesting! main(argc, argv) int argv[]; This is a culture shock. Did the PDP-11 not distinguish between `char` and `int`?
- spijdar 2y agoOf course it did -- this was one of the distinguishing features (byte addressing) of the PDP-11 vs the original machine that ran UNIX, the PDP-7, after all ;-) In "ancient"/K&R C, types weren't specified with the parameters, but on the following lines afterwards. GCC would still compile code like this, if passed the -traditional flag, until ... some point in the last decade or so. Still, this style was deprecated with ANSI C/C89, so it had a good run.
- deleted 2y ago[deleted]
- jecel 2y agoHere the `int` is being used in place of `char *`, not `char`. And yes, the PDP-11 did have byte addressing while the PDP-7 on which Unix was originally created was a word addressed machine.
- tempodox 2y agoI see, now it makes sense.
- Maken 2y agoI find even more interesting that in a later version this appears: main(argc, argv) char argv[][]; Which sadly is no longer valid in C.
- ZhiqiangWang 2y agoCan't stop thinking about Ken Thompson Hack. This should be a clean one ...
- ricardo81 2y agoReminds me of the humility every programmer should have, basically we're standing on the shoulders of giants and abstraction for the most part. 80+ years of computer science. Cool kids may talk about memory safety but ultimately someone had to take care of it, either in their code or abstracted out of it.
- wang_li 2y agoBig non sequitur, but your comment triggered a peeve of mine that I find it ironic when people talk like oldsters can't understand technology.
- ricardo81 2y agoYour comment sounds like a riddle. I've programmed for 25 years but appreciate there's a lot more going on than what I know.
- wang_li 2y agoUpon my own rereading, it is unclear. My point is that the languages most of us use and the fundamental technologies in the oses we use were designed/invented by people who are in their 80s now, many of the Linux core team are 50-60.
- worik 2y ago> ...people talk like oldsters can't understand technology IMO it is young people that have trouble understanding. The same mistakes are made over and over, lessons learned long ago are ignored in the present It easier to write than read, easier to talk than listen, build new than expand the old
- bigstrat2003 2y agoThis is the way of young people in every domain, not just technology. Much like teenagers think they're the first ones to ever have sex before, young people tend to think they are the first ones to notice "hey this status quo really sucks" and try to solve it. This can be a strength, to be fair - the human mind really does tend to get stuck in a rut based on familiarity, and someone new to the domain can spot solutions that others haven't because of that. But more often, it turns into futile attempts to solve problems while forgetting the lessons of the past.
- deweywsu 2y agoAm I interpreting this repo correctly? The first C compiler was written in...C?
- schindlabua 2y agoIt would have been bootstrapped in assembly (or B/BCPL?) and then once you can compile enough C to write a C compiler you rewrite your compiler in C. I remember a Computerphile video where prof. Brailsford said something along the lines of "nobody knew who wrote the first C compiler, everybody just kinda had it and passed it around the office" which I think is funny. There's some sort of analogy to life and things emerging from the primordial soup there, if you squint hard enough.
- froh 2y agoyes. the Q you're asking is: "how was this bootstrapped?" the page that's referenced from GitHub doesn't describe that http://cm.bell-labs.co/who/dmr/primevalC.html http://cm.bell-labs.co/who/dmr/primevalC.html however there probably was a running c compiler (written in assembly) and an assembler and a linker available, hand bootstrapped from machine code, then assembler, linker, then B, NB and then C... We can't tell but that would make sense...
- xenadu02 2y agoThe first B compiler was written in BCPL on the GE 635 mainframe. Thompson wrote a B compiler in BCPL which they used to cross-compile for PDP-7. Then Thompson rewrote B in B, using the BCPL compiler to bootstrap. AFAIK this is the only clean "bootstrap" step involved in the birth of C (BCPL -> B -> self-compiled B) Then they tweaked the compiler and called it NB (New B), then eventually tweaked it enough they decided to call it C. The compiler continuously evolved by compiling new versions of itself through the B -> New B -> C transition. There was no clean cutoff to say "ah this was the first C compiler written in New B". You can see evidence of this in the "pre-struct" version of the compiler after Ritchie had added structure support but before the compiler itself actually used structs. They compiled that version of the compiler then modified the compiler source to use structs, thus all older versions of the compiler could no longer compile the compiler: https://web.archive.org/web/20140708222735/http://thechangelog.com/explore-a-piece-of-unix-history-dennis-ritchies-earliest-c-compilers/ https://web.archive.org/web/20140708222735/http://thechangel... Primeval C: https://web.archive.org/web/20140910102704/http://cm.bell-labs.com/cm/cs/who/dmr/primevalC.html https://web.archive.org/web/20140910102704/http://cm.bell-la... A modern bootstrapping compiler usually keeps around one or more "simplified" versions of the compiler's source. The simplest one either starts with C or assembly. Phase 0 is compiled or assembled then is used to compile Phase 1, which is used to compile Phase 2. (Technically if you parsed through all the backup tapes and restored the right versions of old compilers and compiler source you'd have the bootstrap chain for C but no one bothered to do that until decades later).
- ramon156 2y agoLove how unserious some of the code comments are. Makes you feel less noob for a second :')
- robertkoss 2y agoAs someone who has no touchpoints with lower languages at all, can you explain to me why those files are called c01, c02 etc.?
- higgins 2y agodisappointed this didn't link to some analysis of clay tablets
- sbassi 2y agowhich compiler is used to compile the first compiler?
- sc68cal 2y agoWith BCPL https://web.archive.org/web/20250130134200/https://www.bell-labs.com/usr/dmr/www/chist.html https://web.archive.org/web/20250130134200/https://www.bell-... See also this comment https://news.ycombinator.com/item?id=43462794 https://news.ycombinator.com/item?id=43462794
- canucker2016 2y agoCan people who have used/were around at this time (early 1970s) give a description of the typical dev environment? Also helpful: C history https://en.wikipedia.org/wiki/C_language#History https://en.wikipedia.org/wiki/C_language#History From wikipedia, early Unix was developed on PDP/11 (16-bit). signed 16-bit ints, 8-bit chars, arrays of those previous types. identifiers were limited in length? (I'm seeing 8 chars, lowercase, as the longest) octal numeric constants, was hexadecimal used? there was only a line editor available (vi was 1976) did the file system support directories at that point? no C preprocessor, no header files. (1973) no make/makefiles (1976) was there a std library used with the linker or an archive of object files that was the 'standard' library? Bourne shell wasn't around (1979), so wikipedia seems to point to the Thompson shell - https://en.wikipedia.org/wiki/Thompson_shell https://en.wikipedia.org/wiki/Thompson_shell was there a debugger or was printf the only tool?
- int_19h 2y agoI'm not sure about max identifier length in general, but identifiers exported across translation units (i.e. non-static in modern C) were limited to 6 significant chars as late as ISO C90, although I don't think there were still any compilers around at the time that actually made use of this limit.
- keyle 2y agoAside: I was playing with Think C [2] yesterday and macOS 6.0.8 (emulated with Mini vMac [1]). Boy it took a lot of code to get a window behaving back in the day... And this is a much more modern B/C; it's actually ANSI C but the API is thick. I did really enjoy the UX of macOS 6 and it's terse look, if you can call it that [3]. [1] https://www.gryphel.com/c/minivmac/start.html https://www.gryphel.com/c/minivmac/start.html [2] https://archive.org/details/think_c_5 https://archive.org/details/think_c_5 [3] https://miro.medium.com/v2/resize:fit:1024/format:webp/0*S57C0cBTJnmJCR7N.png https://miro.medium.com/v2/resize:fit:1024/format:webp/0*S57...
- brucehoult 2y agoIt's much less of your own code if you use TCL (THINK Class Library), which shipped with THINK C 4.0 (and THINK Pascal) in mid 1989. Your System 6.0.8 is from April 1991, so TCL was well established by then and the C/C++ version in THINK C 5 even used proper C++ features instead of the hand-rolled "OOP in C" (nested structs with function pointers) used by TCL in THINK C 4. I used TCL for smaller projects, mostly with THINK Pascal which was a bit more natural using Object Pascal, and helped other people use it and transition their own programs that previously used the Toolbox directly, but my more serious programs used MacApp which was released for Object Pascal in 1985, and for C++ in 1991.
- keyle 2y agoThanks for this. I was using think C 3.X last night unaware that there is a 5.0. I figured it out as I typed and googled this morning. I will have to revisit the 5.0, and pick up a digitised book.
- curtisszmania 2y ago[dead]
- andromaton 2y agoSomebody check for Trojan horses. (Ref to Ken Thompson)