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-Linkerld
.
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_ARRAY
oderSHT_FINI_ARRAY
einSHT_PREINIT_ARRAY
Teil 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, dassarmclang
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 - verwendenffunction-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_inline
jedoch nur das Inlining von vom Benutzer bereitgestellten Objekten. -no_inline
Standardmäß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 einemOVERLAY
oder- Attribut markierten Bereiche auf das Verhalten der Optionen aus.PROTECTED
armlink --merge_litpools
Programm
- Erstellen Sie eine C-Quelldatei
litpool.c
mit dem folgenden Code:int f1() { return 0xdeadbeef; } int f2() { return 0xdeadbeef; }
- 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
0xdeadbeef
es 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, daarmclang
diese Konstanten nicht von zwei Funktionen gemeinsam genutzt werden können. - Kompilieren Sie den Quellcode, um das Objekt zu erstellen:
armclang -c -target arm-arm-none-eabi -mcpu=cortex-m0 litpool.c -o litpool.o
--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.- 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 ...