Einführung in die ARM-Linker-Optimierungsfunktionen

Eliminieren Sie die gemeinsame Gruppe

Der Linker kann mehrere Kopien einer Abschnittsgruppe erkennen und andere verwerfen.

® Arm Compiler für Embedded generiert komplette Objekte zur Verknüpfung. daher:

  • Wenn Inline-Funktionen im C- und C++-Quellcode vorhanden sind, enthält jedes Objekt eine Out-of-Line-Kopie der für das Objekt erforderlichen Inline-Funktion.
  • Wenn Sie Vorlagen im C++-Quellcode verwenden, enthält jedes Objekt die Vorlagenfunktionen, die das Objekt benötigt.

Wenn diese Funktionen in einer gemeinsamen Header-Datei deklariert werden, können sie mehrmals in separaten Objekten definiert werden, die anschließend miteinander verknüpft werden. Um Duplikate zu vermeiden, kompiliert der Compiler diese Funktionen in separate Instanzen der gemeinsamen Abschnittsgruppe.

Einzelne Instanzen einer öffentlichen Abschnittsgruppe sind möglicherweise nicht identisch. Einige Kopien befinden sich beispielsweise möglicherweise in Bibliotheken, die mit unterschiedlichen, aber kompatiblen Build-Optionen, unterschiedlichen Optimierungs- oder Debugging-Optionen erstellt wurden.

Wenn die Kopien nicht identisch sind, 则 armlink wird die beste verfügbare Variante jeder gemeinsamen Abschnittsgruppe basierend auf den Eigenschaften des Eingabeobjekts beibehalten. Armlink Den Rest wegwerfen.

Bei identischen Kopien 则 armlink bleibt die erste gefundene Teilgruppe erhalten.

Sie können diese Optimierung mithilfe der folgenden Linker-Optionen steuern:

  • Verwenden Sie die Option - -bestdebug , um die größte gemeinsame Datengruppe (COMDAT) zu verwenden (bietet wahrscheinlich die beste Debugging-Ansicht).
  • Verwenden Sie die -no_bestdebug Option -, um die kleinste COMDAT-Gruppe zu verwenden (und möglicherweise die kleinste Codegröße bereitzustellen). Dies ist die Standardeinstellung.

    Wenn Sie - verwenden, g um alle Dateien zu kompilieren, die die COMDAT-Gruppe A enthalten, -no_bestdebugändert sich das Bild, auch wenn - verwendet wird.

 Beseitigen Sie ungenutzte Teile

Das Eliminieren ungenutzter Teile ist die wichtigste Optimierung, die der Linker an der Bildgröße durchführt.

Eliminierung ungenutzter Teile:

  • Entfernen Sie unzugänglichen Code und Daten aus dem endgültigen Bild.
  • Wird unter Umständen unterdrückt, die zur Löschung aller Teile führen könnten.

Um diese Optimierung zu steuern, verwenden Sie  armlink die Optionen -- remove, -- no_remove, --first, - -last und - -keep.

Für die Eliminierung ungenutzter Teile ist ein Einstiegspunkt erforderlich. Wenn also kein Einstiegspunkt für das Bild angegeben ist, verwenden Sie  armlink die Option -entry -specify enter point.

Option verwenden  armlink – -info unused weist den Linker an, eine Liste der nicht verwendeten Abschnitte zu erstellen, die er entfernt.

Beachten

armlink  Meldet 错误:L6218E:未定义的符号 <symbol> , dass dieses Symbol entfernt wurde, auch wenn der nicht verwendete Teil entfernt wurde. Dieses Verhalten unterscheidet sich vom GNU-Linker  ld  .

Der Eingabeteil bleibt im endgültigen Bild erhalten, wenn:

  • Es enthält einen Einstiegspunkt oder ein von außen zugängliches Symbol. Eingabefunktionen beispielsweise im Sicherheitscode der Arm® v8-M-Sicherheitserweiterung.
  • Es ist SHT_INIT_ARRAYoder SHT_FINI_ARRAYein SHT_PREINIT_ARRAYTeil davon.
  • Es wird als erster oder letzter Eingabeteil angegeben, der durch die first Option  --or --last oder das Scatter-Loading-Äquivalent angegeben wird.
  • Es wird  --keep durch die Option als nicht entfernbar gekennzeichnet.
  • Es wird direkt oder indirekt durch eine nicht schwache Referenz auf den im Bild enthaltenen Eingabeteil referenziert.
  • Sein Name stimmt mit dem Namen überein, auf den das Eingabeabschnittssymbol verweist, und das Symbol wird von dem im Bild beibehaltenen Abschnitt referenziert.

Beachten

Compiler sammeln normalerweise Funktionen und Daten zusammen und geben einen Abschnitt für jede Kategorie aus. Der Linker kann nur völlig unbenutzte Teile entfernen.

Sie können  __attribute__(used)) Attribute verwenden, um Funktionen oder Variablen in Ihrem Quellcode zu markieren. Diese Eigenschaft bewirkt, dass  armclang Symbole  __tagsym$$used.<num> für jede Funktion oder Variable generiert werden, wobei  <num> ein Zähler zur Unterscheidung jedes Symbols verwendet wird. Durch das Entfernen nicht verwendeter Abschnitte werden eingeschlossene Abschnitte nicht gelöscht  __tagsym$$used.<num> .

Sie können auch  armclang die Option - verwenden ffunction-sections , um den Compiler anzuweisen, für jede Funktion in der Quelldatei einen ELF-Abschnitt zu generieren. 

Optimieren Sie mithilfe der RW-Datenkomprimierung

RW-Datenbereiche enthalten häufig eine große Anzahl sich wiederholender Werte (z. B. Nullen), wodurch sie für die Komprimierung geeignet sind.

Standardmäßig ist die RW-Datenkomprimierung aktiviert, um die ROM-Größe zu minimieren.

Der Linker komprimiert die Daten. Diese Daten werden dann zur Laufzeit auf dem Ziel dekomprimiert.

Die Arm-Bibliothek enthält eine Reihe von Dekomprimierungsalgorithmen und der Linker wählt den besten Algorithmus aus, der dem Bild hinzugefügt wird, um den Datenbereich zu dekomprimieren, wenn das Bild ausgeführt wird. Der vom Linker ausgewählte Algorithmus kann überschrieben werden.

Wie der Linker einen Kompressor auswählt

Armlink Sammeln Sie Informationen über den Inhalt von Teilen der Daten, bevor Sie den am besten geeigneten Komprimierungsalgorithmus auswählen, um das kleinste Bild zu erzeugen.

Bei geeigneter Komprimierung armlink kann nur ein Datenkompressor für alle komprimierbaren Datenteile des Bildes verwendet werden. An diesen Teilen können verschiedene Komprimierungsalgorithmen ausprobiert werden, um die beste Gesamtgröße zu erzielen. Die Komprimierung wird automatisch angewendet, wenn:

Compressed data size + Size of decompressor < Uncompressed data size

Wenn Sie einen Kompressor auswählen, armlink wird der Dekomprimierer zum Codebereich des Bildes hinzugefügt. Wenn das endgültige Bild keine komprimierten Daten enthält, wird kein Dekomprimierer hinzugefügt.

Verfügbare Optionen zum Überschreiben des vom Linker verwendeten Komprimierungsalgorithmus

Der Linker verfügt über Optionen zum Deaktivieren der Komprimierung oder zum Angeben des zu verwendenden Komprimierungsalgorithmus.

Der vom Linker verwendete Komprimierungsalgorithmus kann auf eine der folgenden Arten überschrieben werden:

  • Verwenden Sie die -datacompressor off Option -, um die Komprimierung zu deaktivieren.
  • Geben Sie den Komprimierungsalgorithmus an.

Um einen Komprimierungsalgorithmus anzugeben, verwenden Sie die Nummer des gewünschten Kompressors in der Linker-Befehlszeile, zum Beispiel:

armlink --datacompressor 2 ...

Befehlszeilenoption verwenden – -datacompressor list Rufen Sie eine Liste der im Linker verfügbaren Komprimierungsalgorithmen ab:

armlink --datacompressor list
...
Num     Compression algorithm
========================================================
0       Run-length encoding
1       Run-length encoding, with LZ77 on small-repeats
2       Complex LZ77 compression

Bitte beachten Sie bei der Auswahl eines Komprimierungsalgorithmus:

  • Kompressor 0 funktioniert gut bei Daten mit vielen Null-Bytes, aber weniger Nicht-Null-Bytes.
  • Kompressor 1 funktioniert gut, wenn Daten mit einer Byte-Duplizierung ungleich Null verarbeitet werden.
  • Kompressor 2 bietet eine gute Leistung bei der Verarbeitung von Daten, die doppelte Werte enthalten.

Der Linker bevorzugt Kompressor 0 oder 1, wobei die Daten größtenteils null Bytes (>75 %) enthalten. Wenn Kompressor 2 ausgewählt ist, enthalten die Daten sehr wenige Nullbytes (<10 %). Wenn das Bild nur aus A32-Code besteht, wird automatisch der A32-Dekomprimierer verwendet. Wenn das Bild T32-Code enthält, wird der T32-Dekomprimierer verwendet. Gibt es keine eindeutige Präferenz, werden alle Kompressoren getestet, um die beste Gesamtgröße zu erzielen.

 

Was Sie bei der Verwendung der RW-Datenkomprimierung beachten sollten

Bei der Verwendung der RW-Datenkomprimierung sind einige Überlegungen zu beachten.

Bei Verwendung der RW-Datenkomprimierung:

  • Linker-Optionen verwenden – -map Sehen Sie, wo die Komprimierung auf Bereiche in Ihrem Code angewendet wird.
  • Wenn ein Verweis von einem komprimierten Bereich auf ein vom Linker definiertes Symbol unter Verwendung einer Ladeadresse vorliegt, deaktiviert der Linker die RW-Komprimierung.
  • Wenn Sie einen Arm®-Prozessor mit On-Chip-Cache verwenden, aktivieren Sie den Cache nach der Dekomprimierung, um Probleme mit der Codekonsistenz zu vermeiden.

Komprimierte Datensegmente werden zur Laufzeit automatisch dekomprimiert, wenn sie mit Code aus der Arm-Bibliothek ausgeführt werden __main. Dieser Code muss in der Root-Zone platziert werden. InRoot$$Sections Dies geschieht am besten mit einer Scatter-Datei  .

Wenn Sie Scatter-Dateien verwenden, können Sie  NOCOMPRESS durch Hinzufügen von Attributen festlegen, dass die Lade- oder Ausführungsbereiche nicht komprimiert werden.

Funktioniert inline mit dem Linker

Die Linker-Inlining-Funktionen hängen von den von Ihnen angegebenen Optionen und den Inhalten der Eingabedateien ab.

Der Linker kann anstelle der Verzweigungsanweisung für diese Funktion eine kleine Funktion einbinden. Damit der Linker dies tun kann, muss die Funktion (ohne Rückgabeanweisung) in die vier Bytes der Verzweigungsanweisung passen.

Verwenden Sie die  Befehlszeilenoptionen -- inline und --, um das Inlining von Zweigen zu steuern.  Deaktiviert -no_inlinejedoch nur das Inlining von vom Benutzer bereitgestellten Objekten. -no_inlineStandardmäßig integriert der Linker weiterhin Funktionen in die Arm-Standardbibliothek.

Wenn die Branch-Inlining-Optimierung aktiviert ist, scannt der Linker jeden Funktionsaufruf im Bild und integriert ihn nach Bedarf. Wenn der Linker eine geeignete Inline-Funktion findet, ersetzt er den Funktionsaufruf durch Anweisungen der aufgerufenen Funktion.

Der Linker wendet eine Verzweigungs-Inlining-Optimierung an, bevor nicht verwendete Abschnitte entfernt werden, sodass Inline-Abschnitte auch entfernt werden können, wenn sie nicht mehr aufgerufen werden.

Beachten

  • Für Arm®v7-A kann der Linker zwei 16-Bit-codierte Thumb-Anweisungen anstelle der 32-Bit-codierten Thumb®-  BL Anweisungen einbinden.
  • Für Armv8-A und Armv8-M kann der Linker zwei 16-Bit-T32-Anweisungen anstelle von 32-Bit-T32-  BL Anweisungen einbinden.

Verwenden Sie die -info=inline Befehlszeilenoption -, um alle Inline-Funktionen aufzulisten.

Über die Optimierung der Verzweigung zu NOP

Obwohl der Linker Zweige ersetzen kann  NOP, möchten Sie in einigen Fällen möglicherweise verhindern, dass dies geschieht.

Standardmäßig ersetzt der Linker jede Verzweigung durch eine Verschiebung, die zur  NOP nächsten Anweisung mit der Anweisung aufgelöst wird. Diese Optimierung kann auch angewendet werden, wenn der Linker den Tail-Call-Abschnitt neu anordnet.

In einigen Fällen möchten Sie diese Option jedoch möglicherweise deaktivieren, beispielsweise bei der Durchführung von Validierungen oder Pipeline-Aktualisierungen.

Um diese Optimierung zu steuern, verwenden Sie die  Befehlszeilenoptionen - -branchnop und  .--no_branchnop

Linker-Neuordnung von Tail-Call-Abschnitten

In einigen Fällen möchten Sie möglicherweise, dass der Linker den Tail-Call-Abschnitt neu anordnet.

Der Tail-Call-Abschnitt ist der Abschnitt, der die Verzweigungsanweisungen am Ende des Abschnitts enthält. Wenn die Verzweigungsanweisung eine Verschiebung aufweist, die auf eine Funktion abzielt, die in einem anderen Abschnitt beginnt, kann der Linker den Tail-Calling-Abschnitt vor dem aufgerufenen Abschnitt platzieren. Der Linker kann dann die Verzweigungsanweisungen am Ende des Tail-Call-Abschnitts in  NOP Anweisungen optimieren.

Um dieses Verhalten auszunutzen, verwenden Sie die Befehlszeilenoption -tailreorder -move den Tail-Call-Abschnitt vor seinem Ziel.

-info=tailreorder Verwenden Sie die Befehlszeilenoption - , um Informationen zu allen vom Linker durchgeführten Tail-Call-Optimierungen anzuzeigen.

Einschränkungen bei der teilweisen Neuordnung von Tail-Calls

Es gibt einige Einschränkungen bei der Neuordnung von Tail-Call-Abschnitten.

Linker:

  • Für jedes Tail-Call-Ziel kann nur ein Tail-Call-Teil verschoben werden. Wenn es mehrere Tail-Calls für einen einzelnen Abschnitt gibt, wird der Tail-Call-Abschnitt mit demselben Abschnittsnamen vor das Ziel verschoben. Wenn in einem Tail-Call-Abschnitt mit passendem Namen kein Abschnittsname gefunden wird, verschiebt der Linker den ersten Abschnitt, auf den er trifft.
  • Der Tail-Call-Abschnitt kann nicht aus seinem Ausführungsbereich verschoben werden.
  • Der Schwanz wird vor dem Inline-Furnier nicht bewegt.

Identische Konstanten zusammenführen

Der Linker kann versuchen, identische Konstanten in Objekten zusammenzuführen, die auf den AArch32-Status abzielen. Objekte müssen mit dem Arm® Compiler für Embedded 6 generiert werden. armclang -ffunction-sections Das Zusammenführen ist effizienter, wenn es mit Optionen kompiliert wird  . Diese Option ist die Standardeinstellung.

Über diese Aufgabe

Das folgende Verfahren ist ein Beispiel, das die Zusammenführungsfunktion zeigt.

Beachten

Wenn Sie eine Scatter-Datei verwenden,   wirken sich  alle mit einem OVERLAY oder-  Attribut markierten Bereiche auf  das Verhalten der Optionen aus.PROTECTEDarmlink --merge_litpools

Programm

  1. Erstellen Sie eine C-Quelldatei  litpool.cmit dem folgenden Code:
    int f1() {
        return 0xdeadbeef;
    }
    int f2() {
        return 0xdeadbeef;
    }
  2. Verwenden Sie  -S kompilierten Quellcode, um Assemblydateien zu erstellen:
    armclang -c -S -target arm-arm-none-eabi -mcpu=cortex-m0 -ffunction-sections \
        litpool.c -o litpool.s

    Beachten

    - ffunction-sections ist der Standardwert.

    Da 0xdeadbeefes sich um eine Konstante handelt, die über Anweisungen nur schwer zu erstellen ist, wird ein Textpool erstellt, zum Beispiel:

    ...
    f1:
        .fnstart
    @ BB#0:
        ldr    r0, __arm_cp.0_0
        bx     lr
        .p2align    2
    @ BB#1:
    __arm_cp.0_0:
        .long    3735928559              @ 0xdeadbeef
    ...
        .fnend
    
    ...
        .code    16                      @ @f2
        .thumb_func
    f2:
        .fnstart
    @ BB#0:
        ldr    r0, __arm_cp.1_0
        bx     lr
        .p2align    2
    @ BB#1:
    __arm_cp.1_0:
        .long    3735928559              @ 0xdeadbeef
    ...
        .fnend
    ...

    Beachten

    Jede Funktion verfügt über eine Kopie der Konstanten, da  armclang diese Konstanten nicht von zwei Funktionen gemeinsam genutzt werden können.
  3. Kompilieren Sie den Quellcode, um das Objekt zu erstellen:
    armclang -c -target arm-arm-none-eabi -mcpu=cortex-m0 litpool.c -o litpool.o
  4. --merge_litpools Verknüpfen Sie Objektdateien mit  Optionen:
    armlink --cpu=Cortex-M0 --merge_litpools litpool.o -o litpool.axf

    Beachten

    - -merge_litpools ist der Standardwert.
  5. Führen Sie Folgendes aus  fromelf , um die Bildstruktur anzuzeigen:
    fromelf -c -d -s -t -v -z litpool.axf

    Das folgende Beispiel zeigt die kombinierten Ergebnisse:

    ...
        f1
            0x00008000:    4801        .H      LDR      r0,[pc,#4] ; [0x8008] = 0xdeadbeef
            0x00008002:    4770        pG      BX       lr
        f2
            0x00008004:    4800        .H      LDR      r0,[pc,#0] ; [0x8008] = 0xdeadbeef
            0x00008006:    4770        pG      BX       lr
        $d.4
        __arm_cp.1_0
            0x00008008:    deadbeef    ....    DCD    3735928559
    ...

Ich denke du magst

Origin blog.csdn.net/Miao8miao/article/details/135428505
Empfohlen
Rangfolge