Wie kann man mit dem avr-gcc toten Code eliminieren? https://stackoverflow.com/questions/14737641/have-linker-remove-unused-object-files-for-avr-gcc empfielt die Linker-Option -gc-sections und die Compiler-Optionen -ffunction-sections und -fdata-sections – nur leider bewirken die nichts. Ein anderer Tipp aus derselben Quelle sagt, mit den Compiler-Optionen -Wl,-gc-sections würde es gehen – stimmt für den avr-gcc 5.4.0 aber auch nicht. Wie bekommt man es hin?
Hast du toten Code? Hat vielleicht der Compiler diesen toten Code bereits entfernt? Auch eine Idee wäre, eine statische Codeanalyse drüber laufen zu lassen und sich die Stellen anzusehen, die als toter Code identifiziert wurden. mfg mf
Achim M. schrieb: > Hat vielleicht der Compiler diesen toten Code bereits entfernt? Nein, hat er nicht. Ich habe eine Funktion zu einem Modul zugefügt, die nirgends aufgerufen wird – also toter Code ist. Trotzdem steht sie – egal, wie Compiler/Linker optioniert werden – im .lss-File.
Mit LinkTimeOptimization -flto kann man toten Code eigentlich ganz zuverlässig entfernen lassen. -flto muss dem Compiler und dem Linker angegeben werden.
Moriz schrieb: > Ich habe eine Funktion zu einem Modul zugefügt, die nirgends aufgerufen > wird – also toter Code ist. Die wird wohl programm-global sein und Du übersetzt die TUs einzeln. Dann kann / darf der Compiler die nicht eliminieren. Also entweder TU-lokal oder auf LTO umsteigen.
Gibt vermutlich mehrere Wege, das zu erreichen - paar sehr gute sind schon genannt worden. Persönlich würde ich auch mit der Link-Time-Optimization rangehen. Eventuell machts sogar Sinn, wenn du dein Projekt bereits frei von Warnungen hast, Warnungen als Fehler und dich dann mit der LTO auf unreferenzierte Funktionen/Variablen hinweisen zu lassen, eventuell auch nur pro einzelnem File, wenn's zuviel wird. Rausmachen musste das dann selber - wegoptimieren sollte der die aber normalerweise so oder so ab genügend hohem Optimierungslevel.
Wilhelm M. schrieb: > Dann kann / darf der Compiler die nicht eliminieren. Der Compiler nicht, aber der Linker. Mit den Optionen, die ich oben genannt hatte, sollte das passierten, aber offenbar funktioniert das nicht. Falk S. schrieb: > wenn du dein Projekt bereits frei von Warnungen hast Das ist es. > Rausmachen musste das dann selber Das möchte ich vermeiden, denn es handelt sich um eine Quelltextbibliothek – die will man nicht für jeden Anwendungsfall zerpflucken… > wegoptimieren sollte der die aber normalerweise so oder so ab genügend > hohem Optimierungslevel. Dafür prädestiniert sein sollte -Os – die habe ich aktiviert. Hilft aber nix.
:
Bearbeitet durch User
Moriz schrieb: > Der Compiler nicht, aber der Linker. Mit den Optionen, die ich oben > genannt hatte, sollte das passierten, aber offenbar funktioniert das > nicht. Nein, das ist nicht ausreichend. -fwhole-program fehlt. Der Linker kann sonst nicht wissen, ob du nicht später doch noch vor hast etwas hinzuzulinken. (Der Linker wird nicht nur auf Embedded verwendet.)
Arduino F. schrieb: > LTO Ja ich würde auch unbedingt LTO empfehlen. Das optimiert auch Code innerhalb von Funktionen raus, wenn einzelne Ausführungspfade in einer tatsächlichen Konfiguration nicht betreten werden können. Ganz ohne hässliche #ifdefs. Du wirst überrascht sein, was LTO leisten kann.
Wenn CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections nicht den gewünschten Effekt hat, dann machst Du etwas falsch.
:
Bearbeitet durch User
MaWin O. schrieb: > -fwhole-program fehlt. Das hat das Bild verändert: Der Linker mault jetzt massenweise Symbole an, die jeweils in anderen Modulen liegen. Beispiel:
1 | obj/Release/libraries/watchdog.o: In function `__vector_6': |
2 | watchdog.c:(.text.__vector_6+0x18): undefined reference to `errorShutdown' |
errorShutdown liegt in main.c Sie wird in dem Fall zwar aus einer ISR referiert, aber darauf scheint es nicht anzukommen:
1 | obj/Release/main.o: In function `sensorError': |
2 | main.c:(.text.sensorError+0xe): undefined reference to `uart_puts_P' |
uart_puts_P liegt in uart.c Die unerfüllten Links sind jeweils einer Funktion zugeordnet, es sind schlichtweg alle Funktionen, die darin aufgerufen werden, sogar mehrfach.
Super. Mit der Compiler-Optionierung funktioniert es: -Wl,-gc-sections -fdata-sections -ffunction-sections -fwhole-program -std=gnu99 Allerdings kotzt er jetzt noch einen Sack voll
1 | warning: MPFR header version 4.0.1-rc1 differs from library version 4.0.1. |
auf die ich mir keinen Reim machen kann.
Moriz schrieb: > Das hat das Bild verändert: Der Linker mault jetzt massenweise Symbole > an, die jeweils in anderen Modulen liegen. Die Option darfst du natürlich nur dem Linkerlauf mitgeben. Nicht dem Compiler.
-fwhole-program macht halt nicht das, was du erwartest. -Os zusammen mit -flto wurde ja nun schon mehrfach genannt, besser wird es nicht. Allerdings ist dein gcc jetzt nicht der jüngste, es kann durchaus sein, daß neuere das besser hinbekommen. Oliver
MaWin O. schrieb: > Moriz schrieb: >> Das hat das Bild verändert: Der Linker mault jetzt massenweise Symbole >> an, die jeweils in anderen Modulen liegen. > > Die Option darfst du natürlich nur dem Linkerlauf mitgeben. Nicht dem > Compiler. Die -f…-Optionen werden mit -Wl vom Compiler-Driver an den Linker durchgereicht. Der Effekt: der Code ist von 21300 auf 15030 Byte geschrumpft. -fwhole-program kann man weglassen, die ändert nichts. Mein Fehler war, -Wl in eine eigene Zeile unter Compiler-Optionen in Codeblocks zu schreiben.
:
Bearbeitet durch User
Moriz schrieb: > Mein Fehler war, -Wl in eine eigene Zeile unter Compiler-Optionen in > Codeblocks zu schreiben. Typisch Salami.
Und hier nochmal für die Nachwelt die Erkenntnisse aus diesem Thread: Um toten Code zu eliminieren, muss man dem avr-gcc folgende Optionen mitgeben:
1 | -flto |
2 | -fdata-sections |
3 | -ffunction-sections |
4 | -Wl,-gc-sections |
-Wl,-gc-sections wird dabei an den Linker durchgereicht. Bei dieser Optimierung werden Module wegoptimiert, für die es im Code keine Referenzen gibt. Das führt zu Linkerfehlern. Hier ein Beispiel: Der Optiboot Bootlader gibt in R2 den Wert des MCUSR-Registers, den es direkt nach dem Start hatte, an die Anwendung weiter. Ich hatte dieses Feature durch folgendes Codefragment genutzt:
1 | uint8_t mcusr __attribute__ ((section (".noinit"))); |
2 | |
3 | void init_MCUSR_copy(void) __attribute__ ((section (".init0"))); |
4 | |
5 | void init_MCUSR_copy(void) { |
6 | __asm__ __volatile__ ("sts %0, r2\n" : "=m" (mcusr) ); // save r2 to mcusr. r2 has been setup by optiboot |
7 | }
|
Der Code bewirkt, dass der Linker den Maschinenbefehl
1 | sts mcusr, r2 |
direkt vor den C-Startcode der Anwendung plaziert, also an die Stelle, an die der Interruptvektor 0 der Anwendung zeigt. Auf init_MCUSR_copy gibt es in der Anwendung keine Referenz, weshalb der Linker den Befehl als toten Code erkennt und raus schmeißt. Man kann das dadurch beheben, dass man irgendwo in einem if-Statement, das nie erfüllt ist, init_MCUSR_copy aufruft – der Linker findet eine Referenz und baut den Modul ein. Etwas eleganter geht es, wenn man das Codefragment als Interruptroutine für den Reset deklariert:
1 | #define RESET_VECT _VECTOR(0)
|
2 | |
3 | uint8_t mcusr __attribute__ ((section (".noinit"))); |
4 | |
5 | void RESET_VECT(void) __attribute__ ((naked)) __attribute__ ((section (".init0"))); |
6 | |
7 | ISR(RESET_VECT, ISR_NAKED) { |
8 | __asm__ __volatile__ ("sts %0, r2\n" : "=m" (mcusr) ); // save r2 to mcusr. r2 has been setup by optiboot |
9 | }
|
Die Variable mcusr, in die der Inhalt von R2 gespeichert wird, wird durch das _attribute_ ((section (".noinit"))) in einen Speicherbereich gelegt, der vom Startup-Code der Anwendung nicht überschrieben wird.
Moriz schrieb: > Man kann das dadurch beheben, dass man irgendwo in einem if-Statement, > das nie erfüllt ist, init_MCUSR_copy aufruft – der Linker findet eine > Referenz und baut den Modul ein.
1 | #define NOINIT __attribute__((section(".noinit")))
|
2 | #define INIT0 __attribute__((section(".init0")))
|
3 | #define NAKED __attribute__((naked))
|
4 | #define USED __attribute__((used))
|
5 | |
6 | |
7 | byte mcusr NOINIT; |
8 | |
9 | void init_MCUSR_copy() INIT0 USED NAKED; |
:
Bearbeitet durch User
Moriz schrieb: > Auf init_MCUSR_copy gibt es in der Anwendung keine Referenz, > weshalb der Linker den Befehl als toten Code erkennt und raus schmeißt. Im originalen Linker-Skript sind die .initN Startup-Sections als KEEP gekennzeichnet. Ergo: Es wird ein fehlerhaftes ld-Skript verwendet, oder die Funktion wird früher (vom Compiler) entsorgt. Damit letzteres nicht geschieht:
1 | __attribute__((__naked__, __used__, __unused__, __section__(".init0"))) |
2 | static void init_MCUSR_copy (void) |
3 | {
|
4 | __asm __volatile ("sts %0, r2" :: "i" (&mcusr) : "memory"); |
5 | }
|
> -Wl,-gc-sections wird dabei an den Linker durchgereicht. > Bei dieser Optimierung werden Module wegoptimiert, Es werden (rekursiv) Input-Sections gelöscht, die nicht referenziert werden, nicht KEEP sind, und die kein Entry-Symbol enthalten. > Das führt zu Linkerfehlern. Hier ein Beispiel: Bug in Optiboot, siehe oben.
:
Bearbeitet durch User
Johann L. schrieb: > Bug in Optiboot, siehe oben. Nein, mit Optiboot hat das überhaupt nichts zu tun – der lädt nur ganz zu Beginn R2 mit dem Inhalt von MCUSR, das er anschließend löscht. Der gesamte Code von Optiboot rührt R2 anschließend nicht mehr an.
Moriz schrieb: > Johann L. schrieb: >> Bug in Optiboot, siehe oben. > > Nein, mit Optiboot hat das überhaupt nichts zu tun – der lädt nur ganz > zu Beginn R2 mit dem Inhalt von MCUSR, das er anschließend löscht. Der > gesamte Code von Optiboot rührt R2 anschließend nicht mehr an. Jedenfalls ein Bug in den Attributen von init_MCUSR_copy und / oder dem verwendeten ld-Skript.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.