EEN compiler is een programma dat vertaalt door mensen leesbaar broncode in computer-uitvoerbare machinecode. Om dit met succes te doen, moet de voor mensen leesbare code voldoen aan de syntaxis regels van de programmeertaal waarin het is geschreven. De compiler is slechts een programma en kan uw code niet voor u repareren. Als u een fout maakt, moet u de syntaxis corrigeren, anders compileert deze niet.
Wat gebeurt er als u code compileert?
De complexiteit van een compiler hangt af van de syntaxis van de taal en hoeveel abstractie die programmeertaal biedt. Een C-compiler is veel eenvoudiger dan een compiler voor C ++ of C #.
Lexicale analyse
Tijdens het compileren leest de compiler eerst een stroom tekens uit een broncodebestand en genereert een stroom lexicale tokens. Bijvoorbeeld de C ++ code:
int C = (A * B) +10;
kan worden geanalyseerd als deze tokens:
- typ "int"
- variabele "C"
- is gelijk aan
- linker beugel
- variabele "A"
- keer
- variabele "B"
- rechterbeugel
- plus
- letterlijke "10"
Syntactische analyse
De lexicale uitvoer gaat naar het syntactische analysatorgedeelte van de compiler, die de grammaticaregels gebruikt om te beslissen of de invoer geldig is of niet. Tenzij variabelen A en B waren eerder gedeclareerd en bereikten, de compiler zou kunnen zeggen:
- 'A': niet-aangegeven ID.
Als ze zijn aangegeven maar niet zijn geïnitialiseerd. de compiler geeft een waarschuwing:
- lokale variabele 'A' gebruikt zonder te worden geïnitialiseerd.
Negeer compilerwaarschuwingen nooit. Ze kunnen je code op rare en onverwachte manieren breken. Corrigeer altijd compilerwaarschuwingen.
Eén pas of twee?
Sommige programmeertalen zijn zo geschreven dat een compiler de broncode slechts eenmaal kan lezen en de machinecode kan genereren. Pascal is zo'n taal. Veel compilers vereisen ten minste twee passen. Soms is het vanwege voorwaartse verklaringen van functies of lessen.
In C ++ kan een klasse worden gedeclareerd maar pas later worden gedefinieerd. De compiler kan pas achterhalen hoeveel geheugen de klas nodig heeft als hij de body van de klas compileert. Het moet de broncode opnieuw lezen voordat de juiste machinecode wordt gegenereerd.
Machinecode genereren
Ervan uitgaande dat de compiler de lexicale en syntactische analyses met succes voltooit, is de laatste fase het genereren van machinecode. Dit is een ingewikkeld proces, vooral met moderne CPU's.
De snelheid van de gecompileerde uitvoerbaar code moet zo snel mogelijk zijn en kan enorm variëren afhankelijk van de kwaliteit van de gegenereerde code en hoeveel optimalisatie werd aangevraagd.
Bij de meeste compilers kunt u de hoeveelheid optimalisatie opgeven, die doorgaans bekend staat om snelle compilaties voor foutopsporing en volledige optimalisatie voor de vrijgegeven code.
Codegeneratie is een uitdaging
De schrijver van de compiler staat voor uitdagingen bij het schrijven van een codegenerator. Veel processors versnellen de verwerking met behulp van
- Pijplijninstructie
- Intern caches.
Als alle instructies binnen een code lus kan worden gehouden in de CPU cache, dan loopt die lus veel sneller dan wanneer de CPU instructies van het hoofd-RAM moet ophalen. De CPU-cache is een in de CPU-chip ingebouwd geheugenblok dat veel sneller toegankelijk is dan gegevens in het hoofd-RAM.
Caches en wachtrijen
De meeste CPU's hebben een wachtrij waarin de CPU instructies in de cache leest voordat ze worden uitgevoerd. Als er een voorwaardelijke vertakking plaatsvindt, moet de CPU de wachtrij opnieuw laden. De code moet worden gegenereerd om dit te minimaliseren.
Veel CPU's hebben afzonderlijke onderdelen voor:
- Geheel rekenkundig (gehele getallen)
- Rekenpunt drijvende komma (breukgetallen)
Deze bewerkingen kunnen vaak parallel worden uitgevoerd om de snelheid te verhogen.
Compilers genereren meestal machinecode in objectbestanden die dat zijn gekoppeld samen door een linkerprogramma.