Das beste Buch, um Ihre Frage zu beantworten, ist wahrscheinlich: Cooper und Torczon, "Engineering a Compiler", 2003. Wenn Sie Zugang zu einer Universitätsbibliothek haben, sollten Sie eine Kopie ausleihen können.
In einem Produktionscompiler wie llvm oder gcc bemühen sich die Designer, alle Algorithmen unter wobei n die Größe der Eingabe ist. Für einige der Analysen für die "Optimierungs" -Phasen bedeutet dies, dass Sie Heuristiken verwenden müssen, anstatt wirklich optimalen Code zu erzeugen.O ( n2)n
O ( n )O ( n )
O ( n )O ( 1 )O ( s )s
Dann wird der Analysebaum typischerweise zu einem Kontrollflussgraphen "abgeflacht". Die Knoten des Steuerflussdiagramms können Anweisungen mit drei Adressen sein (ähnlich einer RISC-Assemblersprache), und die Größe des Steuerflussdiagramms ist in der Regel linear in Bezug auf die Größe des Analysebaums.
O ( d)dO ( n )n
O ( n2)Dies bedeutet jedoch, dass Sie auf Informationen (und programmverbessernde Transformationen) verzichten müssen, deren Nachweis möglicherweise teuer ist. Ein klassisches Beispiel hierfür ist die Alias-Analyse, bei der Sie für ein Paar von Speicherschreibvorgängen nachweisen möchten, dass die beiden Schreibvorgänge niemals auf denselben Speicherort abzielen können. (Möglicherweise möchten Sie eine Alias-Analyse durchführen, um festzustellen, ob Sie eine Anweisung über die andere verschieben können.) Um jedoch genaue Informationen zu Aliasen zu erhalten, müssen Sie möglicherweise jeden möglichen Steuerpfad durch das Programm analysieren, der in Bezug auf die Anzahl der Zweige exponentiell ist im Programm (und damit exponentiell in der Anzahl der Knoten im Kontrollflussgraphen.)
Als nächstes kommen Sie in die Registerzuordnung. Registerzuordnung kann als formulieren Graph-Färbungsproblem , und Färbung ein Diagramm mit einer minimalen Anzahl von Farben ist bekannt, dass NP-Hard. Daher verwenden die meisten Compiler eine Art gierige Heuristik in Kombination mit Register-Spilling, um die Anzahl der Register-Spills innerhalb eines angemessenen Zeitraums so gut wie möglich zu reduzieren.
Schließlich kommen Sie in die Codegenerierung. Die Codegenerierung erfolgt typischerweise als maximaler Basisblock zu einem Zeitpunkt, an dem ein Basisblock eine Menge linear verbundener Kontrollflussgraphknoten mit einem einzelnen Eingang und einem einzelnen Ausgang ist. Dies kann als ein Diagramm umformuliert werden, das ein Problem abdeckt, bei dem das Diagramm, das Sie abdecken möchten, das Abhängigkeitsdiagramm des Satzes von Anweisungen mit drei Adressen im Basisblock ist, und Sie versuchen, einen Satz von Diagrammen abzudecken, die die verfügbare Maschine darstellen Anleitung. Dieses Problem ist exponentiell in Bezug auf die Größe des größten Basisblocks (der im Prinzip in der gleichen Größenordnung wie die Größe des gesamten Programms liegen kann), weshalb dies wiederum in der Regel mit Heuristiken durchgeführt wird, bei denen nur eine kleine Teilmenge der möglichen Abdeckungen vorhanden ist untersucht.