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.
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.
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.)
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.
-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.
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:
__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
stsmcusr,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:
__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.
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.
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.
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.
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.