avr-gcc: toten Code eliminieren

OP #7371101
Lesenswert?

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?

#7371133
Lesenswert?

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.

OP #7371138
Lesenswert?

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.

OP #7371162
Lesenswert?

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.

OP #7371174
Lesenswert?

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.

OP #7371178
Lesenswert?

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.

OP #7372799
Lesenswert?

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.

(Firma: Gast) #7372835
Lesenswert?

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;
Persönliche Seite #7372841
Lesenswert?

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.

Persönliche Seite #7373144
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren