Dlaczego jest tak mało kompilatorów C?

72

C jest jednym z najczęściej używanych języków na świecie. Stanowi ogromną część istniejącego kodu i jest nadal używany do ogromnej ilości nowego kodu. Jest uwielbiany przez użytkowników, jest tak szeroko rozpowszechniony, że możliwość uruchamiania C jest dla wielu nieformalną definicją platformy i jest chwalony przez fanów za to, że jest „małym” językiem ze stosunkowo czystym zestawem funkcji.

Gdzie są więc wszystkie kompilatory?

Na pulpicie są (realistycznie) dwa : GCC i Clang. Myśląc o tym przez kilka sekund, zapewne pamiętasz, że Intel również istnieje. Istnieje kilka innych, zbyt niejasnych, aby przeciętny człowiek mógł je nazwać i prawie na ogół nie zawracają sobie głowy obsługą najnowszej wersji językowej (lub często nawet dobrze zdefiniowanego podzbioru językowego, tylko „podzbiór”). Połowa członków tej listy to przypisy historyczne; większość pozostałych jest bardzo wyspecjalizowanych i wciąż nie implementuje pełnego języka. Bardzo niewiele z nich wydaje się być oprogramowaniem typu open source.

Scheme i Forth - inne małe języki, które uwielbiają ich fani - prawdopodobnie mają więcej kompilatorów niż faktyczni użytkownicy. Nawet coś takiego jak SML ma bardziej „poważne” implementacje do wyboru niż C. Podczas gdy zapowiedź nowego (niedokończonego) kompilatora C mającego na celu weryfikację faktycznie dostrzega dość negatywne reakcje, a weterani implementacji walczą o to, aby uzyskać wystarczającą liczbę współpracowników, aby nawet nadrobić zaległości C99

Dlaczego? Czy wdrożenie C jest takie trudne? To nie jest C ++. Czy użytkownicy mają po prostu bardzo wypaczone wyobrażenie o tym, w jakiej grupie złożoności się znajduje (tzn. Że w rzeczywistości jest bliżej C ++ niż schematu)?


źródło
61
MSVC wciąż się liczy, przynajmniej jako kompilator C89. Prawdopodobnie nawet bardziej popularny niż Intel.
Rufflewind
22
Wikipedia wymienia sporo kompilatorów C. Stają się bardzo powszechne, gdy znajdziesz się we wbudowanej sferze.
113
ile kompilatorów potrzebujesz do skompilowania kodu C?
Bryan Chen
76
Pytanie opiera się na fałszywej przesłance. Urządzenia analogowe, armcc, kompilator C Bruce'a, kompilator Bare-C Cross, kompilator Borland, kompilator clang, kompilator Cosmic C, kompilator CodeWarrior, kompilator dokto, kompilator Ericsson i nawet nie jestem poza pierwsze pięć liter alfabetu. Istnieje niesamowicie duża liczba kompilatorów C. Pytanie brzmi: „dlaczego jest tak mało kompilatorów C, skoro nie liczymy tych kilkudziesięciu jako prawdziwych kompilatorów C?” Zdecydowaną większość kompilatorów C zdefiniowałeś jako nieciekawych, dlatego nie ma ich zbyt wiele.
Eric Lippert,
19
Pytania „dlaczego” są najlepszymi pytaniami dla tej witryny w najlepszym przypadku, a „dlaczego nie?” pytania są gorsze. Gdybym miał cię spotkać na imprezie i zapytać „dlaczego nie ścigasz się w żaglówkach?” Myślę, że słusznie uznałbyś to za dziwne pytanie. Nie musisz uzasadniać NIE angażowania się w trudne technicznie, ryzykowne fizycznie i bardzo drogie hobby. Pisanie dowolnego nietrywialnego oprogramowania jest drogie, trudne i ryzykowne, dlatego wymaga ogromnego motywatora. Lepszym pytaniem byłoby „dlaczego jest tak wiele kompilatorów C?” Zaskakujące jest to, że jest ich więcej niż jeden.
Eric Lippert,

Odpowiedzi:

153

Dzisiaj trzeba prawdziwego kompilatora C być kompilator optymalizujący , zwłaszcza dlatego, że C nie jest językiem blisko sprzętu, ponieważ obecne procesory są niezwykle złożone ( out-of-order , potokowych , Superskalarna , ze złożonymi skrytek i TLB , dlatego wymaga planowania instrukcji itp.). Dzisiejsze procesory x86 nie przypominają procesorów i386 z poprzedniego wieku, nawet jeśli oba są w stanie uruchomić ten sam kod maszynowy. Zobacz, że C nie jest językiem niskiego poziomu (Twój komputer nie jest szybkim PDP-11), autorstwa Davida Chisnalla.

Niewiele osób używa naiwnych, nieoptymalizujących kompilatorów C, takich jak tinycc lub nwcc , ponieważ produkują kod, który jest kilka razy wolniejszy niż to, co mogą dać kompilatory optymalizujące.

Kodowanie optymalizującego kompilatora jest trudne. Zauważ, że zarówno GCC, jak i Clang optymalizują pewną reprezentację kodu „neutralną dla języka źródłowego” (Gimple dla GCC, LLVM dla Clang). Złożoność dobrego kompilatora C nie znajduje się w fazie analizy!

W szczególności tworzenie kompilatora C ++ nie jest dużo trudniejsze niż tworzenie kompilatora C: parsowanie C ++ i przekształcanie go w wewnętrzną reprezentację kodu jest złożone (ponieważ specyfikacja C ++ jest złożona), ale jest dobrze zrozumiana, ale części optymalizacyjne są jeszcze bardziej złożone (wewnątrz GCC: optymalizacje klasy środkowej, język neutralny i procesor docelowy są neutralne, stanowią większość kompilatora, a reszta jest zrównoważona między frontonami dla kilku języków i backendami dla kilku procesorów). Dlatego większość optymalizujących kompilatorów C jest w stanie kompilować niektóre inne języki, takie jak C ++, Fortran, D, ... Specyficzne części GCC dla C ++ stanowią około 20% kompilatora ...

Ponadto C (lub C ++) jest tak szeroko stosowane, że ludzie oczekują, że ich kod będzie podlegał kompilacji, nawet jeśli nie jest on zgodny z oficjalnymi standardami, które nie określają wystarczająco dokładnie semantyki języka (więc każdy kompilator może mieć własną interpretację tego). Spójrz także na sprawdzony kompilator CompCert C i analizator statyczny Frama-C , które dbają o bardziej formalną semantykę C.

Optymalizacje to zjawisko długofalowe : wdrożenie kilku prostych optymalizacji jest łatwe, ale nie uczynią kompilatora konkurencyjnym! Musisz wdrożyć wiele różnych optymalizacji oraz sprytnie je zorganizować i połączyć, aby uzyskać konkurencyjny kompilator w świecie rzeczywistym. Innymi słowy, kompilator optymalizujący w świecie rzeczywistym musi być złożonym oprogramowaniem. BTW, zarówno GCC, jak i Clang / LLVM, mają kilka wewnętrznych specjalistycznych generatorów kodu C / C ++. Oba są ogromnymi bestiami (kilka milionów linii kodu źródłowego, z roczną stopą wzrostu o kilka procent rocznie) z dużą społecznością programistów (kilkaset osób, pracujących głównie w pełnym wymiarze godzin lub przynajmniej w niepełnym wymiarze godzin).

Zauważ, że nie ma (według mojej najlepszej wiedzy) wielowątkowego kompilatora C, nawet jeśli niektóre części kompilatora mogłyby być uruchomione równolegle (np. Optymalizacja między procedurami, alokacja rejestru, planowanie instrukcji ...). A kompilacja równoległa z make -jnie zawsze wystarcza (zwłaszcza z LTO ).

Ponadto trudno jest uzyskać fundusze na kodowanie kompilatora C od zera, a taki wysiłek musi trwać kilka lat. Wreszcie, większość kompilatorów C lub C ++ jest obecnie wolnym oprogramowaniem (nie ma już rynku na nowe kompilatory zastrzeżone sprzedawane przez start-upy) lub przynajmniej są to monopolistyczne towary (takie jak Microsoft Visual C ++ ), a bycie wolnym oprogramowaniem jest prawie wymagane dla kompilatorów ( ponieważ potrzebują wkładu wielu różnych organizacji).

Byłbym zachwycony, gdybym otrzymał fundusze na pracę nad kompilatorem C od zera jako wolne oprogramowanie, ale nie jestem wystarczająco naiwny, aby wierzyć, że jest to możliwe dzisiaj!

Basile Starynkevitch
źródło
14
(there is no more a market for proprietary compilersPowiedz to zespołowi Visual Studio ...
Mason Wheeler,
18
Microsoft ma monopol. Miałem na myśli, że małe firmy opracowujące nowe kompilatory C nie będą sprzedawać dużo z nich. Czy możesz wymienić niedawnego konkurenta firmy MSVC?
Basile Starynkevitch,
12
W świecie HPC istnieje wiele zastrzeżonych kompilatorów. Najczęściej stosowane są PGCC, NAG i ICC.
Davidmh
37
@MasonWheeler: VS jest obecnie rozdawany za darmo (jak w przypadku piwa). Wersje niewolne dodają oprzyrządowanie, ale kompilator C w VS2013 jest taki sam we wszystkich wersjach. Po prostu nie ma rynku, nawet dla nich.
MSalters
3
Ale zarówno GCC, jak i LLVM działają na znacznie niższych reprezentacjach i optymalizują również kod C ++ i C (i Ada i Fortran, dla GCC). Przeciwnie, powiedziałbym, że C ++ wymaga większej optymalizacji (zwłaszcza przy kompilowaniu kodu przy użyciu jego STL) niż C!
Basile Starynkevitch,
70

Chciałbym zakwestionować twoje podstawowe założenie, że istnieje tylko niewielka liczba implementacji C.

Nawet nie znam C, nie używam C, nie jestem członkiem społeczności C, a jednak nawet wiem o wiele więcej niż kilka kompilatorów, o których wspomniałeś.

Przede wszystkim jest kompilator, który prawdopodobnie całkowicie przyćmiewa zarówno GCC, jak i Clanga na komputerze: Microsoft Visual C. Pomimo napadów, które zarówno OSX, jak i Linux robią na komputerze, i udział w rynku, że „ukradli” iOS i Android z dala od dawnych tradycyjnych komputerów stacjonarnych, Windows jest wciąż dominującym pulpit OS, a większość programów Windows Desktop C są prawdopodobnie skompilowany przy użyciu narzędzi Microsoft.

Tradycyjnie każdy sprzedawca systemu operacyjnego i każdy sprzedawca układów miał swoje własne kompilatory. Microsoft, jako dostawca systemu operacyjnego, ma Microsoft Visual C. IBM, zarówno jako dostawca systemu operacyjnego, jak i dostawca chipów, ma XLC (który jest domyślnym kompilatorem systemowym dla systemu AIX i kompilatorem, z którym kompilowane są zarówno systemy AIX, jak i i / OS) . Intel ma własny kompilator. Sun / Oracle ma własny kompilator w Sun Studio.

Są też wydajni dostawcy kompilatorów, tacy jak PathScale i The Portland Group, których kompilatory (i biblioteki OpenMP) są używane do łamania liczb.

Digital Mars nadal działa. Uważam, że Walter Bright wyróżnia się tym, że jest jedyną osobą na świecie, która samodzielnie stworzyła kompilator C ++ o jakości produkcyjnej (głównie).

Wreszcie, mamy wszystkie zastrzeżone kompilatory dla wbudowanych mikrokontrolerów. IIRC, każdego roku sprzedaje się więcej mikrokontrolerów niż komputery stacjonarne, urządzenia mobilne, serwery, stacje robocze i komputery mainframe sprzedawane w całej historii komputerów razem wziętych. To zdecydowanie nie są produkty niszowe.

Wyróżnienie otrzymuje TruffleC , interpreter C (!) Działający na JVM (!), Napisany przy użyciu frameworku interpretera AST Truffle, który jest tylko 7% wolniejszy niż GCC i Clang (w zależności od tego, który z nich jest najszybszy na danym poziomie) Gra Benchmark w językach komputerowych i szybsza niż oba na mikrodrukach. Korzystając z TruffleC, zespół Truffle był w stanie uzyskać swoją wersję JRuby + Truffle, aby wykonywać rozszerzenia Ruby C szybciej niż faktyczna implementacja C Ruby!

Jest to więc 6 implementacji oprócz wymienionych przez ciebie, które mogę wymienić z góry głowy, nawet nie wiedząc nic o C.

Jörg W Mittag
źródło
1
Poza Microsoft Visual C większość wspomnianych kompilatorów C jest rzadko używana.
Basile Starynkevitch
6
MSVC jest dużym kompilatorem C ++, ale dla C jest trudny w użyciu i na stałe utknął w C89; kompilatory mikrokontrolerów są zazwyczaj specyficzne dla celu, utknęły w C89 i są dziwaczne; TruffleC nie wydaje się jeszcze dostępny (ale jest interesujący, dzięki). Pathscale i Digital Mars wydają się bardziej jak kontrprzykłady, których szukałem.
Leushenko
8
@Mario nie mam na myśli tego, że C89 jest zepsuty, ale C89 nie jest aktualną formą języka; a to oznacza, że istnieje mniej aktualnych kompilatorów .
Leushenko,
6
@Leushenko MSVC nie utknął na stałe w C89. Odbyło się kilka dyskusji i należy dodać więcej funkcji C99. Na początek większość bibliotek C99 jest obsługiwana od MSVC 2015, a także kilka funkcji językowych (głównie rzeczy potrzebne w C ++ 11).
Morwenn,
5
@Morwenn: Wydaje się, że polityka Microsoftu polega na tym, że C99 nie rozwiązuje żadnych problemów, których C ++ jeszcze nie rozwiązał, a jeśli programujesz, powinieneś używać podobnego do C podzbioru C ++ (wszystko, co nie wymaga środowiska wykonawczego lub gdzie nie można kontrolować, gdzie kompilator ma zamiar umieszczać różne rzeczy - ważne, jeśli trzeba się upewnić, że kod lub dane nie są stronicowane ze stanów, w których stronicowanie jest wyłączone). Jedynymi funkcjami z C99 będą rzeczy wymagane w późniejszych specyfikacjach C ++ oraz te, których wdrożenie jest bezproblemowe.
Mike Dimmick
8

Ile kompilatorów potrzebujesz?

Jeśli mają różne zestawy funkcji, możesz stworzyć problem z przenośnością. Jeśli są towarem, wybierz opcję „domyślną” (GCC, Clang lub VS). Jeśli zależy Ci na ostatnich 5% wydajności, masz benchmark.

Jeśli programujesz język programowania rekreacyjnie lub do celów badawczych, prawdopodobnie będzie to język bardziej nowoczesny. Stąd rozprzestrzenianie się kompilatorów zabawek dla Scheme i ML. Chociaż wydaje się, że OCaml zyskuje przyczepność do nie-zabawkowych zastosowań nieakademickich.

Uwaga: różni się to bardzo w zależności od języka. Java ma w zasadzie łańcuch narzędzi Sun / Oracle i GNU. Python ma różne kompilatory, z których żaden nie jest naprawdę szanowany w porównaniu do standardowego interpretera. Rust i Go mają dokładnie jedną implementację. C # ma Microsoft i Mono.

pjc50
źródło
1
Oczywiste jest, że istnieją bardziej interesujące powody do opracowania kompilatora ML ... Pomyślałem tylko, że społeczność C, która jest prawdopodobnie o trzy rzędy wielkości większa, zrównoważy ten efekt. Ale możesz mieć rację, 1000 * 0jest nadal 0.
Leushenko
Tworzenie nowego kompilatora jest często powiązane z fragmentacją społeczności (spowodowaną lub powodującą). Na przykład podział egcs vs opiekun gcc. Ponadto kompatybilność ze źródłami C jest zwykle poniżej 100%.
pjc50,
@ pjc50: Sposób, w jaki napisany jest standard, skutecznie dzieli C na kilka rozłącznych dialektów w oparciu o takie rzeczy jak podstawowy typ inti będzie wymagał różnych kompilatorów do interpretacji tego samego kodu źródłowego na bardzo różne sposoby.
supercat
5
Uważam, że Go ma dwie implementacje ( toolchain 6g/ 8g/… i gccgo). Istniała również bardzo interesująca, komercyjna implementacja o nazwie erGo, która była a) natywną implementacją Go dla Windows w czasach, gdy ani gccgo, ani oryginalny kompilator Go nie działały bardzo dobrze na Windows, b) firma obstawiająca Go, długo zanim stała się nawet 1.0, oraz c) pierwsza implementacja Go napisana w Go (gccgo i 6g / 8g są napisane w C). Zarówno projekt, jak i firma zniknęły, zanim jeszcze wyszły z zamkniętej wersji beta.
Jörg W Mittag
6

C / C ++ jest unikalny wśród skompilowanych języków, ponieważ ma 3 główne implementacje wspólnej specyfikacji.

Zgodnie z zasadą odrzucania wszystkiego, co nie jest często używane, każdy inny skompilowany język ma wartość od 0 do 1.

I myślę, że javascript jest jedynym powodem, dla którego musisz określić „skompilowany”.

soru
źródło
2
Etykieta „C” jest stosowana w wielu różnych językach; niektórzy definiują kod uint16_t a=48000u; unsigned uint32_t b=(a*a)/2;jako przypisujący bwartość 8192. Niektórzy definiują go jako przypisujący 1152000000. Większość obecnie uważa go za zachowanie niezdefiniowane i prawdopodobnie zapisze 3299483648, ale nie obiecuje pod tym względem.
supercat
1
@supercat: Ah, dobry dziwny z przepełnieniami i zasadami promocji liczb całkowitych. Zależy to od użycia 2lub 2unajwyraźniej.
Zan Lynx,
1
@ZanLynx: Nie sądzę, żeby były przypadki, w których 2 kontra 2u są uzasadnione ; jedyny przypadek, w którym wiem, gdzie to może mieć znaczenie, dotyczy Nieokreślonego Zachowania zarówno z 2, jak i 2u.
supercat
3
@ superupat: jak można uzyskać niezdefiniowane zachowanie /2u? Przepełnienie niepodpisane jest zdefiniowane (jako modulo 2 ^ N dla zdefiniowanego w implementacji N), ale podział nie może nawet przepełnić.
MSalters
2
Niezdefiniowane zachowanie wynikałoby z mnożenia wartości, które byłyby promowane do podpisanych int, ale których produkt nie pasowałby do tego typu. Wymuszenie tego wyniku na unsigned int prawdopodobnie zmieni interpretację wartości wynikowej, ale nie neguje niezdefiniowanego zachowania z poprzedniego obliczenia.
supercat
5

Jaki jest twój język docelowy?

Kompilatory SML często celują w C lub coś w rodzaju LLVM (lub jak widać w twoim linku, JVM lub JavaScript).

Jeśli kompilujesz C, to nie dlatego, że idziesz do JVM. Idziesz na coś gorszego niż C. Znacznie gorzej. A potem kilka razy zduplikujesz to drobne piekło dla wszystkich docelowych platform.

I oczywiście C nie jest C ++, ale powiedziałbym, że jest bliższy C ++ niż Scheme. Ma swój własny podzbiór nieokreślonego zachowania szatańskiego (patrzę na ciebie rozmiar wbudowanych typów). A jeśli spieprzysz te drobiazgi (lub zrobisz to „poprawnie”, ale nieoczekiwanie), masz dziesięciolecia istniejącego kodu w ważnych systemach, który powie ci, jak straszny jesteś. Jeśli spieprzysz kompilator SML, to po prostu nie zadziała - i ktoś może to zauważyć. Pewnego dnia.

Telastyn
źródło
Zarówno SML / NJ, jak i PolyML kompilują się do kodu maszynowego ...
Basile Starynkevitch,
2
W jaki sposób int rozmiar jest „niezdefiniowanym zachowaniem”? A dlaczego UB i tak byłby obciążeniem dla dostawców kompilatorów? Jedynym prawdziwym obciążeniem dla autorów kompilatorów jest to, że int szerokości są zdefiniowane w implementacji, a nie są nieokreślone, więc musisz udokumentować to, co zrobiłeś.
MSalters
@MSalters W rzeczywistości twórcy kompilatorów dla ustalonej platformy mają obowiązek dopasowywania tego, co zrobili inni, którzy wcześniej je zrobili. Czasami jest to udokumentowane i ustandaryzowane, a czasem nie. Łatwo jest ustalić, jaki jest rozmiar int, ale trudniej jest ustalić, co dzieje się z wartościami rejestru i gdzie przechowywane są argumenty podczas wywoływania funkcji (które mogą się zmieniać w zależności od typów argumentów i typu zwracanego przez funkcję), reguł układu struktury, itp.
Random832
@MSalters Większość ludzi spodziewa intsię, że będzie to 32 lub 64 bity, ale może mieć nawet 16 bitów. Nie jest wcale trudno wyprodukować liczbę spoza zakresu, [−32767, +32767]a intprzepełnienie to UB. Istnieje również char/ shortawans do int lub w unsigned int zależności od tego, czy intmoże reprezentować każdą wartość oryginalnego typu, co może dodatkowo wyzwalać konwersję z intdo, unsigned intjeśli operandy miały różne typy i zostały przekonwertowane inaczej, a także potencjalnie kolejną konwersję, gdy przypiszesz wynik do zmiennej .
Doval
@MSalters Jest wystarczająco dużo swobody w rozmiarach standardowych typów i wystarczająca domyślna konwersja, że ​​założę się, że dla prawie każdego nietrywialnego programu w C istnieje wybór dopuszczalnych wielkości całkowitych, które spowodują, że zrobi to źle lub spowoduje niezdefiniowaną zachowanie.
Doval