74

C is one of the most widely-used languages in the world. It accounts for a huge proportion of existing code and continues to be used for a vast amount of new code. It's beloved by its users, it's so widely ported that being able to run C is to many the informal definition of a platform, and is praised by its fans for being a "small" language with a relatively clean set of features.

So where are all the compilers?

On the desktop, there are (realistically) two: GCC and Clang. Thinking about it for a few seconds you'll probably remember Intel exists as well. There are a handful of others, far too obscure for the average person to name and almost universally not bothering to support a recent language version (or often even a well-defined language subset, just "a subset"). Half of the members of this list are historical footnotes; most of the rest are very specialized and still don't actually implement the full language. Very few actually seem to be open-source.

Scheme and Forth - other small languages that are beloved by their fans for it - probably have more compilers than actual users. Even something like SML has more "serious" implementations to choose between than C. Whereas the announcement of a new (unfinished) C compiler aiming at verification actually sees some pretty negative responses, and veteran implementations struggle to get enough contributors to even catch up to C99.

Why? Is implementing C so hard? It isn't C++. Do users simply have a very skewed idea about what complexity group it falls in (i.e. that it actually is closer to C++ than Scheme)?

21
  • 64
    MSVC still counts, as a C89 compiler at least. Probably more popular than Intel even.
    – Rufflewind
    Commented Feb 19, 2015 at 2:50
  • 23
    Wikipedia lists quite a few C compilers. They get very common when you find yourself in the embedded realm.
    – user40980
    Commented Feb 19, 2015 at 2:51
  • 114
    how many compilers do you need to compile your C code?
    – Bryan Chen
    Commented Feb 19, 2015 at 2:57
  • 79
    The question is based upon a false premise. Analog Devices, armcc, Bruce's C Compiler, the Bare-C Cross Compiler, the Borland compiler, the clang compiler, the Cosmic C compiler, the CodeWarrior compiler, the dokto compiler, the Ericsson compiler, and I'm not even out of the first five letters of the alphabet yet. There is an insanely large number of C compilers. The question is "why are there so few C compilers, if we don't count these several dozens as real C compilers?" You have defined away the vast majority of C compilers as not interesting, which is why there are not very many of them. Commented Feb 19, 2015 at 18:45
  • 19
    "Why" questions are bad questions for this site at the best of times, and "why not?" questions are worse. If I were to meet you at a party and ask "so, why don't you race sailboats?" I think you'd rightly find it to be an odd question. You don't need to provide a justification for NOT engaging in a technically difficult, physically risky and very expensive hobby. Writing any non-trivial piece of software is expensive, difficult and risky and therefore requires an enormous motivator. A better question would be "why are there so many C compilers?" It is surprising that there is more than one. Commented Feb 19, 2015 at 18:53

5 Answers 5

162

Today, you need a real C compiler to be an optimizing compiler, notably because C is no longer a language close to the hardware, because current processors are incredibly complex (out-of-order, pipelined, superscalar, with complex caches & TLB, hence needing instruction scheduling, etc...). Today's x86 processors are not like i386 processors of the previous century, even if both are able to run the same machine code. See the C is not a low level language (Your computer is not a fast PDP-11) paper by David Chisnall.

Few people are using naive non-optimizing C compilers like tinycc or nwcc, since they produce code which is several times slower than what optimizing compilers can give.

Coding an optimizing compiler is difficult. Notice that both GCC and Clang are optimizing some "source language-neutral" code representation (Gimple for GCC, LLVM for Clang). The complexity of a good C compiler is not in the parsing phase!

In particular, making a C++ compiler is not much harder than making a C compiler: parsing C++ and transforming it into some internal code representation is complex (because the C++ specification is complex), but is well understood, but the optimization parts are even more complex (inside GCC: the middle-end optimizations, source-language and target-processor neutral, form the majority of the compiler, with the rest being balanced between front-ends for several languages and back-ends for several processors). Hence most optimizing C compilers are also able to compile some other languages, like C++, Fortran, D, ... The C++ specific parts of GCC are about 20% of the compiler...

Also, C (or C++) is so widely used that people expect their code to be compilable even when it does not exactly follow the official standards, which do not define precisely enough the semantics of the language (so each compiler may have its own interpretation of it). Look also into the CompCert proved C compiler, and the Frama-C static analyzer, which care about more formal semantics of C.

And optimizations are a long-tail phenomenon: implementing a few simple optimizations is easy, but they won't make a compiler competitive! You need to implement a lot of different optimizations, and to organize and combine them cleverly, to get a real-world compiler that is competitive. In other words, a real-world optimizing compiler has to be a complex piece of software. BTW, both GCC and Clang/LLVM have several internal specialized C/C++ code generators. And both are huge beasts (several millions of source lines of code, with a growth rate of several percent each year) with a large developer community (a few hundred persons, working mostly full-time, or at least half-time).

Notice that there is no (to the best of my knowledge) multi-threaded C compiler, even if some parts of a compiler could be run in parallel (e.g. intra-procedural optimization, register allocation, instruction scheduling... ). And parallel build with make -j is not always enough (especially with LTO).

Also, it is difficult to get funded on coding a C compiler from scratch, and such an effort needs to last several years. Finally, most C or C++ compilers are free software today (there is no longer a market for new proprietary compilers sold by startups) or at least are monopolistic commodities (like Microsoft Visual C++), and being a free software is nearly required for compilers (because they need contributions from many different organizations).

I'd be delighted to get funding to work on a C compiler from scratch as free software, but I am not naive enough to believe that is possible today!

20
  • 14
    (there is no more a market for proprietary compilers Tell that to the Visual Studio team... Commented Feb 19, 2015 at 10:15
  • 19
    Microsoft has a monopoly. I meant that small companies developing new C compilers won't sell a lot of them. Can you name a recent proprietary competitor to MSVC? Commented Feb 19, 2015 at 10:23
  • 15
    There are many proprietary compilers in the HPC world. PGCC, NAG, and ICC are the most widely used.
    – Davidmh
    Commented Feb 19, 2015 at 13:09
  • 40
    @MasonWheeler: VS is given away for free nowadays (as in beer). The non-free versions add tooling, but the C compiler in VS2013 is the same in all versions. There just isn't a market, not even for them.
    – MSalters
    Commented Feb 19, 2015 at 16:06
  • 3
    @BasileStarynkevitch: C++ actually helps in at least two ways: making more code (longer instruction sequences) available to the optimizer, and reducing false aliasing (char* aliases everything, std::string virtually nothing). The optimizer may be shared, but it works on different inputs.
    – MSalters
    Commented Feb 20, 2015 at 12:12
73

I would like to contest your underlying assumption that there are only a small number of C implementations.

I don't even know C, I don't use C, I am not a member of the C community, and yet, even I know far more than the few compilers you mentioned.

First and foremost, there is the compiler which probably completely dwarfs both GCC and Clang on the desktop: Microsoft Visual C. Despite the inroads that both OSX and Linux have been making on the desktop, and the marketshare that iOS and Android have "stolen" away from former traditional desktop users, Windows is still the dominant desktop OS, and the majority of Windows desktop C programs are probably compiled using Microsoft tools.

Traditionally, every OS vendor and every chip vendor had their own compilers. Microsoft, as an OS vendor, has Microsoft Visual C. IBM, as both an OS vendor and a chip vendor, has XLC (which is the default system compiler for AIX, and the compiler with which both AIX and i/OS are compiled). Intel has their own compiler. Sun/Oracle have their own compiler in Sun Studio.

Then, there are the high-performance compiler vendors like PathScale and The Portland Group, whose compilers (and OpenMP libraries) are used for numbercrunching.

Digital Mars is also still in business. I believe Walter Bright has the unique distinction of being the only person on the planet who managed to create a production-quality C++ compiler (mostly) by himself.

Last but not least we have all the proprietary compilers for embedded microcontrollers. IIRC, there are more microcontrollers sold every year than desktop, mobile, server, workstation, and mainframe CPUs have been sold in the entire history of computing combined. So, those are definitely not niche products.

An honorary mention goes out to TruffleC, a C interpreter(!) running on the JVM(!) written using the Truffle AST interpreter framework that is only 7% slower than GCC and Clang (whichever is fastest on any given particular benchmark) across the Computer Languages Benchmark Game, and faster than both on microbenchmarks. Using TruffleC, the Truffle team was able to get their version of JRuby+Truffle to execute Ruby C extensions faster than the actual C Ruby implementation!

So, these are 6 implementations in addition to the ones you listed which I can name off the top of my head, without even knowing anything about C.

14
  • 7
    MSVC is the big C++ compiler, but for C it's hard to use and permanently stuck in C89; microcontroller compilers are usually target-specific, stuck in C89, and quirky; TruffleC doesn't appear to be available yet (but is interesting, thanks). Pathscale and Digital Mars seem more like the kind of counterexamples I was looking for though. Commented Feb 19, 2015 at 8:05
  • 8
    @Mario my meaning isn't that C89 is broken, but C89 is not the up-to-date form of the language; and that does mean fewer compilers that are up-to-date exist. Commented Feb 19, 2015 at 8:42
  • 8
    @Leushenko MSVC isn't permanently stuck in C89. There have been some discussions and more C99 features should be added. For starters, most of the C99 library is supported as of MSVC 2015 and a few language features too (mainly the things needed for C++11 though).
    – Morwenn
    Commented Feb 19, 2015 at 9:28
  • 6
    @Morwenn: Microsoft's policy appears to be that C99 solves no problems that C++ had not already solved, and that if you're doing system programming you should be using the C-like subset of C++ (anything that doesn't require the runtime or where you can't control where the compiler is going to put things - important if you need to ensure that code or data isn't paged out from states where paging is disabled). The only features from C99 will be things required in later C++ specs, and those which are no-brainers to implement. Commented Feb 20, 2015 at 10:25
  • 2
    Update: MSVC supports C11 and C17. By default, it's C89 plus extensions, some of which are in C99. Commented Feb 21, 2021 at 16:16
8

How many compilers do you need?

If they have different feature sets, you create a portability problem. If they're commoditised you choose either the "default" (GCC, Clang or VS). If you care about the last 5% performance you have a benchmark-off.

If you're doing programming language work recreationally or for research purposes, it's likely to be in a more modern language. Hence the proliferation of toy compilers for Scheme and ML. Although OCaml seems to be getting some traction for non-toy non-academic uses.

Note this varies a lot by language. Java has essentially the Sun/Oracle toolchain and the GNU one. Python has various compilers none of which are really respected compared to the standard interpreter. Rust and Go have exactly one implementation each. C# has Microsoft and Mono.

4
  • 1
    It's obvious that there are more interesting reasons to develop an ML compiler... I just thought that the C community being probably three orders of magnitude bigger would balance that effect out. But you might be right, 1000 * 0 is still 0. Commented Feb 19, 2015 at 10:46
  • Creating a new compiler is often linked with fragmentation of the community (either caused by or causing). For example, the egcs vs gcc maintainer split. Also, C source compatibility tends to be below 100%.
    – pjc50
    Commented Feb 19, 2015 at 11:28
  • @pjc50: The way the standard is written effectively subdivides C into a number of disjoint dialects based upon things like the basic type of int, and will require different compilers to interpret the same source code in very different ways.
    – supercat
    Commented Feb 19, 2015 at 20:09
  • 5
    I believe, Go has two implementations (the 6g/8g/… toolchain and gccgo). There also used to be a very interesting proprietary commercial implementation called erGo, which was a) a native Windows implementation of Go at a time when neither gccgo nor the original Go compiler worked very well on Windows, b) a company betting on Go, long before it even became 1.0, and c) the first implementation of Go written in Go (gccgo and 6g/8g are both written in C). Both the project and the company vanished, however, before they even got out of closed beta. Commented Feb 20, 2015 at 0:22
7

C/C++ is unique amongst compiled languages in that it has 3 major implementations of a common specification.

Going by the rule of dismissing anything that's not used much, every other compiled language has 0 to 1.

And I think javascript is the only reason you need to specify 'compiled'.

6
  • 2
    The label "C" is applied to a number of different languages; some define the code uint16_t a=48000u; unsigned uint32_t b=(a*a)/2; as assigning to b the value 8192. Some define it as assigning 1152000000. Most nowadays regard it as Undefined Behavior, and likely to store 3299483648 but make no promise in that regard.
    – supercat
    Commented Feb 19, 2015 at 20:05
  • 1
    @supercat: Ah, a good weird one with overflows and integer promotion rules. It hinges on using 2 or 2u apparently.
    – Zan Lynx
    Commented Feb 19, 2015 at 23:46
  • 1
    @ZanLynx: I don't think there are any cases where 2 versus 2u legitimately matters; the only case I know where it might matter involves Undefined Behavior with both 2 and 2u.
    – supercat
    Commented Feb 19, 2015 at 23:54
  • 3
    @supercat: how would you get undefined behavior from /2u ? Unsigned overflow is defined (as modulo 2^N for implementation-defined N) but division can't even overflow.
    – MSalters
    Commented Feb 20, 2015 at 12:17
  • 2
    The Undefined Behavior would come from the multiplication of values which would get promoted to signed int, but whose product would not fit in that type. Coercing that result to unsigned int would likely change the interpretation of the resulting value, but would not negate the Undefined Behavior from the preceding calculation.
    – supercat
    Commented Feb 20, 2015 at 16:34
5

So what is your target language?

SML compilers are often targeting C or something like LLVM (or as seen in your link, the JVM or JavaScript).

If you're compiling C, it's not because you're going to the JVM. You're going to something worse than C. Far worse. And then you get to duplicate that minor hell a bunch of times for all your target platforms.

And sure, C isn't C++, but I'd say that it's closer to C++ than Scheme. It does have its own subset of undefined behavior evilness (I'm looking at you size of built in types). And if you screw up that minutiae (or do it "correctly" but unexpectedly) then you have decades of existing code on vital systems that will tell you how terrible you are. If you screw up an SML compiler, it just won't work - and someone might notice. Someday.

7
  • SML/NJ and PolyML are both compiling to machine code... Commented Feb 19, 2015 at 6:31
  • 2
    How is int size " Undefined Behavior" ? And why would be UB be a burden on compiler vendors anyway? The only real burden for compiler writers is that int widths are implementation defined, not unspecified, so you have to document what you did.
    – MSalters
    Commented Feb 19, 2015 at 16:11
  • @MSalters In reality, compiler writers for an established platform have the burden of matching what others that went before them did. Sometimes this is documented and standardized, sometimes not. It's easy to find what size an int is, but harder to find what is done with register values and where arguments are stored when calling a function (which may change depending on the argument types and return type of the function), struct layout rules, etc.
    – Random832
    Commented Feb 19, 2015 at 18:07
  • @MSalters Most people expect int to be 32 or 64 bits but it can be as small as 16 bits. It's not hard at all to produce a number outside the range of [−32767, +32767] and int overflow is UB. There's also char/short getting promoted to int or unsigned int depending on whether int can represent every value of the original type, which can further trigger a conversion from int to unsigned int if the operands had different types and got converted differently, plus potentially another conversion when you assign the result to a variable.
    – Doval
    Commented Feb 19, 2015 at 18:36
  • @MSalters There's enough leeway in the size of the standard types and enough implicit conversions that I'd bet that for just about any non-trivial C program there's a choice of legal integer sizes that will cause it to do the wrong thing or cause undefined behavior.
    – Doval
    Commented Feb 19, 2015 at 18:37