it-swarm-eu.dev

Jak jsou kompilátory tak spolehlivé?

Kompilátory používáme každý den, jako by byla zadána jejich správnost, ale kompilátory jsou také programy a mohou případně obsahovat chyby. Vždycky jsem přemýšlel o této neomylné robustnosti. Už jste někdy narazili na chybu v samotném kompilátoru? Co to bylo a jak jste si uvědomili, že problém byl v samotném kompilátoru?

... a jak do dělají kompilátory tak spolehlivými?

67
EpsilonVector

V průběhu času se důkladně testují pomocí tisíců nebo dokonce milionů vývojářů.

Rovněž je dobře definován problém, který má být vyřešen (velmi podrobnou technickou specifikací). A povaha úkolu se snadno hodí k jednotkovým/systémovým testům. Tj. v zásadě překládá textový vstup ve velmi specifickém formátu za účelem výstupu do jiného druhu dobře definovaného formátu (nějaký druh bytového kódu nebo strojového kódu). Je tedy snadné vytvářet a ověřovat testovací případy.

Navíc jsou chyby snadno reprodukovatelné: kromě přesné informace o verzi platformy a kompilátoru je obvykle potřeba jen část vstupního kódu. Nemluvě o tom, že uživatelé kompilátoru (kteří jsou sami vývojáři) mají tendenci poskytovat mnohem přesnější a podrobnější zprávy o chybách než kterýkoli průměrný uživatel počítače :-)

101
Péter Török

Kromě všech dosud skvělých odpovědí:

Máte "zaujatost pozorovatele". Nesledujete chyby, a proto předpokládáte, že žádné neexistují.

Myslela jsem, jako ty. Pak jsem začal psát kompilátory profesionálně a dovolte mi říci, že tam je spousta chyb!

Nevidíte chyby, protože píšete kód, který je stejně jako 99,999% všech ostatních kódů, které lidé píšou. Pravděpodobně budete psát naprosto normální, přímý, jasně správný kód, který volá metody a spustí smyčky a nedělá nic efektního nebo divného, ​​protože jste normální vývojář, který řeší běžné obchodní problémy.

Nevidíte žádné chyby kompilátoru, protože chyby kompilátoru nejsou v snadno analyzovatelných přímých scénářích normálního kódu; chyby jsou v analýze divného kódu, který nepíšete.

Na druhé straně mám opačné pozorování. Vidím bláznivý kód celý den každý den, a tak se mi zdá, že kompilátoři jsou chock plné chyb.

Pokud jste se posadili s jazykovou specifikací jakéhokoli jazyka a přijali jste jakoukoli implementaci kompilátoru pro tento jazyk, a opravdu jste se těžce pokusili zjistit, zda kompilátor přesně implementoval specifikaci nebo ne, soustředit se na obskurní rohové případy, brzy byste našli kompilátor chyby poměrně často. Dovolte mi uvést příklad, tady je chyba kompilátoru C #, kterou jsem našel doslova před pěti minutami.

static void N(ref int x){}
...
N(ref 123);

Kompilátor dává tři chyby.

  • Argument ref nebo out musí být přiřazitelná proměnná.
  • Nejlepší shoda pro N (ref int x) má neplatné argumenty.
  • Chybí „ref“ argumentu 1.

První chybová zpráva je zjevně správná a třetí chybou. Algoritmus generování chyb se snaží zjistit, proč byl první argument neplatný, dívá se na něj, vidí, že je konstanta, a nevrací se zpět ke zdrojovému kódu, aby zkontroloval, zda byl označen jako „ref“; spíše předpokládá, že nikdo nebude tak hloupý, aby označil konstantu jako ref, a rozhodne, že rozhodčí musí chybět.

Není jasné, jaká je správná třetí chybová zpráva, ale není to tak. Ve skutečnosti není jasné, zda je chybová zpráva druhá správná. Mělo by selhání řešení přetížení selhat nebo by mělo být „ref 123“ považováno za argument ref správného typu? Teď si to musím promyslet a promluvit si s týmem třídění, abychom mohli určit, jaké správné chování je.

Nikdy jste neviděli tuto chybu, protože byste pravděpodobně nikdy nedělali něco tak hloupého, aby se pokusili projít 123 odkazem. A pokud jste tak učinili, pravděpodobně byste si ani nevšimli, že třetí chybová zpráva je nesmyslná, protože ta první je správná a postačující k diagnostice problému. Ale snažím se dělat takové věci, protože jsem snažím se rozbít kompilátor. Pokud jste to zkusili, viděli byste také chyby.

66
Eric Lippert

Děláš si ze mě srandu? Kompilátory mají také chyby, opravdu se načítají.

GCC je pravděpodobně nejslavnějším kompilátorem s otevřeným zdrojovým kódem na planetě a podívejte se na jeho databázi chyb: http://gcc.gnu.org/bugzilla/buglist.cgi?product=gcc&component=c%2B% 2B a rozlišení = ---

Mezi GCC 3.2 a GCC 3.2.3 se podívejte, kolik opravených chyb: http://gcc.gnu.org/gcc-3.2/changes.html

Pokud jde o ostatní, jako je Visual C++, nechci ani začít.

Jak děláte kompilátory spolehlivými? Pro začátek mají spoustu a spoustu jednotkových testů. A celá planeta je používá, takže žádný nedostatek testerů.

Vážně, nicméně, vývojáři kompilátorů, kterým věřím, jsou vynikající programátoři a přestože nejsou neomylní, zabalí se do docela rána.

54
Fanatic23

Ve své době jsem se setkal se dvěma nebo třemi. Jediným skutečným způsobem, jak jej zjistit, je podívat se na kód shromáždění.

Přestože kompilátoři jsou vysoce spolehliví z důvodů, které poukazují ostatní plakáty, myslím si, že spolehlivost kompilátoru je často samoaplňujícím hodnocením. Programátoři považují kompilátor za standardní. Když se něco pokazí, předpokládáte jeho chybu (protože 99,999% času) a změňte svůj kód tak, aby obcházel problém kompilátoru, nikoli naopak. Například zhroucení kódu při vysokém nastavení optimalizace je určitě chyba kompilátoru, ale většina lidí ji jednoduše nastavila o něco níže a pokračovala bez nahlášení chyby.

21
Karl Bielefeldt

Kompilátory mají několik vlastností, které vedou k jejich správnosti:

  • Doména je velmi dobře známa a zkoumána. Problém je dobře definován a nabízená řešení jsou dobře definována.
  • Automatizované testování je dostatečné pro ověření správnosti fungování kompilátorů
  • Kompilátory mají velmi rozsáhlé, obvykle veřejné, automatizované a jednotkové testy, které se časem hromadí, aby pokryly více chybového prostoru než u většiny ostatních programů.
  • Kompilátory mají velmi velké množství očních bulví, které sledují jejich výsledky
15
blueberryfields

Kompilátory používáme každý den

... a jak činí kompilátory tak spolehlivými?

Oni ne. My ano. Protože Každý je používá neustále, chyby jsou rychle nalezeny.

Je to hra čísel. Protože kompilátoři jsou zvyklí tak všudypřítomně, je vysoce pravděpodobné, že jakákoli chyba bude bude spuštěna někým, ale protože existuje tak velký počet uživatelů, je velmi nepravděpodobné, že někdo vám bude konkrétně.

Takže záleží na vašem pohledu: kompilátoři jsou u všech uživatelů buggy. Je ale velmi pravděpodobné, že někdo jiný bude kompilovat podobný kód dříve, než jste to udělali, takže pokud by jejich byl chyba, zasáhlo by to, ne vy, takže z vašeho individuální z pohledu, vypadá to, že chyba tam nikdy nebyla.

K tomu samozřejmě můžete přidat všechny další odpovědi: kompilátory jsou dobře prozkoumány a dobře pochopeny. Existuje mýtus, že je těžké psát, což znamená, že pouze velmi inteligentní, velmi dobří programátoři se skutečně snaží napsat jeden a jsou zvlášť opatrní, když to dělají. Obvykle se snadno testují a snadno se testují zátěžovým testem nebo fuzz testem. Uživatelé kompilátoru bývají sami odborní programátoři, což vede ke kvalitním hlášením o chybách. A naopak: spisovatelé kompilátoru bývají uživateli vlastního kompilátoru.

14
Jörg W Mittag

Kromě všech odpovědí, které již mám, bych chtěl přidat:

věřím mnohokrát, prodejci jedí své vlastní krmivo pro psy. Znamená to, že píšou kompilátory samy o sobě.

12
DevSolo

Často jsem narazil na chyby kompilátoru.

Najdete je v temnějších rozích, kde je méně testerů. Chcete-li například najít chyby v GCC, zkuste:

  • Sestavte křížový kompilátor. Doslova najdete desítky chyb v konfiguračních a sestavovacích skriptech GCC. Některé mají za následek selhání sestavení během kompilace GCC a jiné budou mít za následek selhání křížového kompilátoru při vytváření pracovních spustitelných souborů.
  • Vytvořte verzi GCC pro Itanium pomocí profilu-bootstrap. Poslední párkrát jsem to vyzkoušel na GCC 4.4 a 4.5 se nepodařilo vyrobit funkční obslužný program výjimek C++. Neoptimalizované sestavení fungovalo dobře. Zdálo se, že se nikdo nezajímá o opravu chyby, kterou jsem nahlásil, a poté, co jsem se pokusil vykopat to, co porušovalo specifikace paměti GCC asm, se vzdal opravy sám.
  • Zkuste si vytvořit svůj vlastní funkční GCJ z nejnovějších věcí, aniž byste sledovali distro build skript. Troufám si vás.
8
Zan Lynx

Několik důvodů:

  • Spisovatelé kompilátoru "jedí své vlastní krmivo pro psy".
  • Kompilátory jsou založeny na dobře chápaných principech CS.
  • Kompilátory jsou postaveny na velmi jasné specifikace.
  • Kompilátory dostanou testováno.
  • Kompilátory jsou ne vždy velmi spolehlivé.
6
Kramii

Obvykle jsou velmi dobré při -O0. Ve skutečnosti, pokud máme podezření na chybu kompilátoru, porovnáme -00 s jakoukoli úrovní, kterou se snažíme použít. Vyšší úroveň optimalizace jde s větším rizikem. Některé jsou dokonce úmyslně a jako takové jsou označeny v dokumentaci. Setkal jsem se s velkým množstvím (nejméně sto během mého času), ale v poslední době jsou mnohem vzácnější. Přesto ve snaze dosáhnout dobrých čísel specmarků (nebo jiných benchmarků důležitých pro marketing) je pokušení posouvat limity skvělé. Měli jsme problémy před několika lety, kdy se dodavatel (nechat beze jména) rozhodnout, že bude porušovat výchozí závorky - jinak než některé speciální jasně označené možnosti kompilace.

Může být obtížné diagnostikovat chybu kompilátoru versus odkaz na toulavou paměť. Rekompilace s různými možnostmi může jednoduše zakódovat relativní umístění datových objektů v paměti, takže nevíte, zda se jedná o Heisenbug zdrojového kódu nebo buggy. překladač. Také mnoho optimalizací provádí legitimní změny v pořadí operací, nebo dokonce algebraické zjednodušení vaší algebry, a ty budou mít odlišné vlastnosti s ohledem na zaokrouhlování s plovoucí desetinnou čárkou a pod/přetečení. Je obtížné oddělit tyto účinky od skutečných chyb. Z tohoto důvodu je obtížné počítat s pohyblivou řádovou čárkou s tvrdým jádrem, protože chyby a numerická citlivost se často nedají snadno rozeznat.

5
Omega Centauri

Chyby kompilátoru nejsou tak vzácné. Nejběžnějším případem je kompilátor, který hlásí chybu v kódu, který by měl být přijat, nebo kompilátor, který přijal kód, který měl být odmítnut.

5
kevin cline

Už jste někdy narazili na chybu v samotném kompilátoru? Co to bylo a jak jste si uvědomili, že problém byl v samotném kompilátoru?

Jo!

Dva nejpamátnější byly první dva, které jsem kdy narazil. Oba byli v kompilátoru Lightspeed C pro Mac 680x0 zpět asi v letech 1985-7.

Prvním z nich bylo to, že za určitých okolností neprovedl operátor celočíselného přírůstku nic - jinými slovy, v určitém kusu kódu, „i ++“ jednoduše neudělal nic „i“. Vytáhl jsem si vlasy, až jsem se podíval na rozebrání. Pak jsem provedl přírůstek jinak a předložil hlášení o chybě.

Druhý byl trochu komplikovanější a byl to opravdu špatně považovaný „rys“, který se zhoršil. Počáteční Mac měli složitý systém pro provádění operací na discích nízké úrovně. Z nějakého důvodu jsem nikdy nepochopil - pravděpodobně s vytvářením menších spustitelných souborů - než kompilátor, který právě generuje instrukce pro provoz disku na místě v kódu objektu, kompilátor Lightspeed zavolá interní funkci, která za běhu vygenerovala operaci disku instrukce na zásobníku a skočil tam.

To fungovalo skvěle na 68 000 procesorech, ale když spustíte stejný kód na procesoru 68020, často by to dělalo divné věci. Ukázalo se, že novou funkcí modelu 68020 byla primitivní 256bitová mezipaměť instrukcí. Toto byly první dny s mezipaměťmi CPU, nemělo ponětí, že mezipaměť je „špinavá“ a musí být doplňována; Myslím, že konstruktéři CPU u společnosti Motorola nepřemýšleli o samopravujícím kódu. Takže pokud jste ve své prováděcí sekvenci provedli dvě operace disku dostatečně blízko sebe a běhový modul Lightspeed vytvořil skutečné instrukce na stejném místě v zásobníku, CPU by si chybně myslela, že došlo k zásahu do mezipaměti instrukcí a první operaci disku spustíte dvakrát.

Znovu, přijít na to, že ven vzal nějaké kopat kolem s rozebíračem, a spousta single-stepping v low-level debugger. Mým řešením bylo prefixovat každou operaci disku voláním funkce, která provedla 256 instrukcí „NOP“, které zaplavily (a tím vyčistily) mezipaměť instrukcí.

Za 25 let od té doby jsem viděl v průběhu času méně a méně chyb kompilátoru. Myslím, že pro to existuje několik důvodů:

  • Pro kompilátory existuje stále rostoucí sada ověřovacích testů.
  • Moderní kompilátory jsou obvykle rozděleny do dvou nebo více částí, z nichž jedna generuje kód nezávislý na platformě (např. Cílení LLVM na to, co byste mohli považovat za imaginární CPU), a druhý, který jej převádí do pokynů pro váš skutečný cílový hardware. U kompilátorů s více platformami se první část používá všude, takže získává spoustu testů v reálném světě.
4
Bob Murphy

Našli jsme v Turbo Pascal před 5,5 lety do očí bijící chybu. Chyba přítomná v předchozí (5.0) ani následující (6.0) verzi kompilátoru. A ten, který měl být snadno otestovatelný, protože to vůbec nebyl rohový kufřík (jen hovor, který se běžně nepoužívá).

Obecně platí, že tvůrci komerčních kompilátorů (spíše než hobby projekty) budou mít velmi rozsáhlé postupy kontroly kvality a testování. Vědí, že jejich kompilátoři jsou jejich stěžejními projekty a že vady na nich budou vypadat velmi špatně, horší než na jiných společnostech vyrábějících většinu jiných produktů. Vývojáři softwaru jsou neodpustitelnou partou, naši dodavatelé nástrojů nás nechávají dolů, abychom se raději vydali hledat alternativy, než čekat na opravu od dodavatele, a je velmi pravděpodobné, že tuto skutečnost sdělíme našim kolegům, kteří by mohli dobře sledovat naše příklad. V mnoha jiných odvětvích tomu tak není, takže potenciální ztráta výrobci překladačů v důsledku vážné chyby je mnohem větší než u výrobce softwaru pro úpravu videa.

4
jwenting

Jo, právě včera jsem narazil na chybu v kompilátoru ASP.NET:

Při použití silně zadaných modelů v pohledech je omezeno, kolik šablon parametrů může obsahovat. Je zřejmé, že nemůže trvat více než 4 parametry šablony, takže oba příklady uvedené níže způsobí, že kompilátor příliš nezpracuje:

ViewUserControl<System.Tuple<type1, type2, type3, type4, type5>>

Nezkompiloval by se, ale bude, pokud type5 je odebrán.

ViewUserControl<System.Tuple<MyModel, System.Func<type1, type2, type3, type4>>>

Kompilace, pokud type4 je odebrán.

Všimněte si, že System.Tuple má mnoho přetížení a může trvat až 16 parametrů (je to šílené, já vím).

3
user8685

Chyby kompilátoru se vyskytují, ale máte sklon je najít v lichých rozích ...

V 90. letech došlo v kompilátoru Digital Equipment Corporation VAX VMS C k podivné chybě

(Měl jsem na opasku cibuli, jako tomu bylo v té době v módě)

Jako tělo smyčky for by byl kompilován cizí středník kdekoli předcházející smyčce for.

f(){...}
;
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++){
     puts("hello");
  }
}

U dotyčného kompilátoru se smyčka provede pouze jednou.

vidí

f(){...}
g(){...}

void test(){
  int i;
  for ( i=0; i < 10; i++) ;  /* empty statement for fun */

  {
     puts("hello");
  }
}

To mě stálo spoustu času.

Starší verze kompilátoru PIC C, kterou jsme (zvyklí) působit na pracovní zkušenosti, nemohli studenti vygenerovat kód, který správně přerušil vysokou prioritu. Museli jste počkat 2-3 roky a upgradovat.

Kompilátor MSVC 6 měl v linkeru šikovnou chybu, došlo by k chybě segmentace a čas od času zemřel bezdůvodně. Čisté sestavení to obvykle opravilo (ale povzdech ne vždy).

3
Tim Williscroft

Pokud se chování vašeho softwaru při kompilaci s -O0 a -O2 liší, zjistili jste chybu kompilátoru.

Pokud se chování vašeho softwaru liší od toho, co očekáváte, je pravděpodobné, že chyba je v kódu.

2
mouviciel

V některých oblastech, jako je například avionický software, existují extrémně vysoké požadavky na certifikaci, na kód a hardware, stejně jako na kompilátor. O této poslední části je projekt, jehož cílem je vytvoření formálně ověřeného kompilátoru C nazvaného Compcert . Teoreticky je tento druh kompilátoru stejně spolehlivý, jak přicházejí.

2
Axel

Viděl jsem několik chyb kompilátoru, které jsem sám nahlásil (konkrétně v F #).

To znamená, že si myslím, že chyby kompilátoru jsou vzácné, protože lidé, kteří píšou kompilátory, jsou obecně velmi spokojeni s přísnými koncepty počítačové vědy, díky nimž jsou si opravdu vědomi matematických důsledků kódu.

Většina z nich je zřejmě velmi dobře obeznámena s věcmi, jako je počet lambda, formální ověření, denotační sémantika atd. - věci, které průměrný programátor jako já dokáže jen stěží pochopit.

Také je obvykle poměrně jednoduché mapování od vstupu k výstupu v kompilátorech, takže ladění programovacího jazyka je pravděpodobně mnohem jednodušší než ladění, řekněme, blogovací engine.

2
Rei Miyasaka

Nedávno jsem našel chybu v kompilátoru C #, vidíte, jak Eric Lippert (kdo je v týmu C # design) přišel na to, co je chyba zde .

Kromě již uvedených odpovědí bych rád přidal ještě několik věcí. Návrháři překladačů jsou často velmi dobrí programátoři. Kompilátory jsou velmi důležité: většina programování se provádí pomocí kompilátorů, takže je nezbytné, aby kompilátor byl vysoce kvalitní. Je proto v nejlepším zájmu společností, které vytvářejí kompilátory, aby na to kladly své nejlepší lidi (nebo alespoň velmi dobré: ty nejlepší by se nemusely líbit designu kompilátoru). Microsoft by velmi rád, kdyby jejich kompilátory C a C++ fungovaly správně, nebo by zbytek společnosti nemohl dělat svou práci.

Také, pokud stavíte opravdu složitý kompilátor, nemůžete to jen zaseknout. Logika kompilátorů je velmi složitá a snadno se formalizuje. Proto budou tyto programy často budovány velmi „robustním“ a obecným způsobem, což vede k menšímu počtu chyb.

2
Alex ten Brink