Eliminar grupo comum
O vinculador pode detectar múltiplas cópias de um grupo de seções e descartar outras.
® Arm Compiler for Embedded gera objetos completos para vinculação. portanto:
- Se funções embutidas estiverem presentes no código-fonte C e C++, cada objeto conterá uma cópia fora de linha da função embutida exigida pelo objeto.
- Se você usar modelos no código-fonte C++, cada objeto conterá as funções de modelo que o objeto requer.
Quando essas funções são declaradas em um arquivo de cabeçalho comum, elas podem ser definidas diversas vezes em objetos separados que são posteriormente vinculados entre si. Para eliminar a duplicação, o compilador compila essas funções em instâncias separadas do grupo de seções comuns.
Instâncias individuais de um grupo de seções públicas podem não ser idênticas. Por exemplo, algumas cópias podem estar em bibliotecas construídas com opções de construção diferentes, mas compatíveis, diferentes opções de otimização ou depuração.
Se as cópias não forem idênticas, 则 armlink
a melhor variante disponível de cada grupo de seções comuns será mantida com base nas propriedades do objeto de entrada. Armlink
Descarte o resto.
Se as cópias forem idênticas, 则 armlink
será mantido o primeiro grupo parcial localizado.
Você pode controlar essa otimização usando as seguintes opções de vinculador:
- Use a opção -
-bestdebug
para usar o maior grupo de dados comuns (COMDAT) (provavelmente fornece a melhor visualização de depuração). -
Use a
-no_bestdebug
opção - para usar o menor grupo COMDAT (possivelmente fornecendo o menor tamanho de código). Esta é a configuração padrão.Se você usar -
g
para compilar todos os arquivos contendo o grupo COMDAT A,-no_bestdebug
a imagem mudará mesmo se - for usado.
Elimine peças não utilizadas
A eliminação de partes não utilizadas é a otimização mais importante que o vinculador realiza no tamanho da imagem.
Eliminação de peças não utilizadas:
- Remova códigos e dados inacessíveis da imagem final.
- Suprimido em circunstâncias que podem resultar na exclusão de todas as partes.
Para controlar essa otimização, use armlink
as opções -- remove
, -- no_remove
, --first, - -last
e - -keep
.
A eliminação de porções não utilizadas requer um ponto de entrada. Portanto, se nenhum ponto de entrada for especificado para a imagem, use armlink
a opção -entry
-specify ponto de entrada.
Usar armlink
opção - -info unused
instrui o vinculador a gerar uma lista de seções não utilizadas que ele remove.
Perceber
armlink
Relata错误:L6218E:未定义的符号 <symbol>
que este símbolo foi removido mesmo que a parte não utilizada tenha sido removida. Este comportamento é diferente do vinculador GNUld
.
A parte de entrada permanecerá na imagem final se:
- Ele contém um ponto de entrada ou um símbolo acessível externamente. Por exemplo, funções de entrada no código de segurança da extensão de segurança Arm® v8-M.
- É
SHT_INIT_ARRAY
ouSHT_FINI_ARRAY
fazSHT_PREINIT_ARRAY
parte de. - É especificado como a primeira ou última parte de entrada, especificada pela
first
opção --or--last
ou equivalente de carregamento de dispersão. - Está
--keep
marcado como não removível pela opção. - É referenciado direta ou indiretamente por uma referência não fraca à parte de entrada contida na imagem.
- Seu nome corresponde ao nome referenciado pelo símbolo da seção de entrada, e o símbolo é referenciado pela seção retida na imagem.
Perceber
Os compiladores geralmente coletam funções e dados e emitem uma seção para cada categoria. O vinculador só pode remover peças completamente não utilizadas.
Você pode usar
__attribute__(used))
atributos para marcar funções ou variáveis em seu código-fonte. Esta propriedade faz com que sejam geradosarmclang
símbolos <num> para cada função ou variável, onde é utilizado um contador para distinguir cada símbolo. Eliminar seções não utilizadas não exclui seções incluídas.__tagsym$$used.
<num>
__tagsym$$used.<num>
Você também pode usar
armclang
a opção -ffunction-sections
para instruir o compilador a gerar uma seção ELF para cada função no arquivo de origem.
Otimize usando compactação de dados RW
As áreas de dados RW geralmente contêm um grande número de valores repetidos (como zeros), o que os torna adequados para compactação.
Por padrão, a compactação de dados RW está habilitada para minimizar o tamanho da ROM.
O vinculador compacta os dados. Esses dados são então descompactados no destino em tempo de execução.
A biblioteca Arm contém vários algoritmos de descompressão e o vinculador escolhe o melhor algoritmo para adicionar à imagem a fim de descompactar a região de dados quando a imagem é executada. O algoritmo selecionado pelo vinculador pode ser substituído.
Como o vinculador seleciona um compressor
Armlink
Reúna informações sobre o conteúdo de partes dos dados antes de escolher o algoritmo de compressão mais adequado para produzir a menor imagem.
Se a compactação for apropriada, armlink
apenas um compressor de dados poderá ser usado para todas as partes de dados compactáveis da imagem. Diferentes algoritmos de compressão podem ser testados nessas peças para produzir o melhor tamanho geral. A compactação é aplicada automaticamente se:
Compressed data size + Size of decompressor < Uncompressed data size
Quando você seleciona um compressor, armlink
o descompressor é adicionado à área de código da imagem. Se a imagem final não contiver dados compactados, nenhum descompressor será adicionado.
Opções disponíveis para substituir o algoritmo de compactação usado pelo vinculador
O vinculador possui opções para desabilitar a compactação ou especificar o algoritmo de compactação a ser usado.
O algoritmo de compactação usado pelo vinculador pode ser substituído de qualquer uma das seguintes maneiras:
- Use a
-datacompressor off
opção - para desligar a compactação. - Especifique o algoritmo de compactação.
Para especificar um algoritmo de compactação, use o número do compressor desejado na linha de comando do vinculador, por exemplo:
armlink --datacompressor 2 ...
Use a opção de linha de comando - -datacompressor list
Obtenha uma lista de algoritmos de compactação disponíveis no vinculador:
armlink --datacompressor list
...
Num Compression algorithm
========================================================
0 Run-length encoding
1 Run-length encoding, with LZ77 on small-repeats
2 Complex LZ77 compression
Ao escolher um algoritmo de compactação, observe:
- O compressor 0 funciona bem em dados com muitos bytes zero, mas menos bytes diferentes de zero.
- O compressor 1 tem um bom desempenho ao lidar com dados com duplicação de bytes diferente de zero.
- O Compressor 2 funciona bem ao processar dados contendo valores duplicados.
O vinculador prefere o compressor 0 ou 1, onde os dados contêm principalmente zero bytes (>75%). Quando o Compressor 2 é selecionado, os dados contêm poucos zero bytes (<10%). Se a imagem consistir apenas no código A32, o descompressor A32 é automaticamente utilizado. Se a imagem contiver algum código T32, o descompressor T32 será usado. Se não houver uma preferência clara, todos os compressores serão testados para produzir o melhor tamanho geral.
Coisas a serem observadas ao usar compactação de dados RW
Existem algumas considerações ao usar a compactação de dados RW.
Ao usar compactação de dados RW:
- Use opções de vinculador -
-map
Veja onde a compactação é aplicada às áreas do seu código. - Se houver uma referência de uma área compactada a um símbolo definido pelo vinculador usando um endereço de carregamento, o vinculador desativa a compactação RW.
- Se você estiver usando um processador Arm® com cache no chip, habilite o cache após a descompactação para evitar problemas de consistência de código.
Segmentos de dados compactados são descompactados automaticamente em tempo de execução se executados usando código da biblioteca Arm __main
. Este código deve ser colocado na zona raiz. InRoot$$Sections
A melhor maneira de fazer isso é usar um arquivo scatter .
Se você estiver usando arquivos dispersos, poderá NOCOMPRESS
especificar que as regiões de carregamento ou execução não sejam compactadas adicionando atributos.
Funções alinhadas com o vinculador
Os recursos de inlining do vinculador dependem das opções especificadas e do conteúdo dos arquivos de entrada.
O vinculador pode incorporar uma pequena função no lugar da instrução de desvio para essa função. Para que o vinculador possa fazer isso, a função (sem instrução de retorno) deve caber nos quatro bytes da instrução de desvio.
Use as opções de linha de comando -- inline
e -- para controlar o inlining de ramificação. -no_inline
No entanto - -no_inline
desativa apenas o inlining de objetos fornecidos pelo usuário. Por padrão, o vinculador ainda incorpora funções na biblioteca padrão Arm.
Se a otimização de inlining de ramificação estiver habilitada, o vinculador verifica cada chamada de função na imagem e as incorpora conforme necessário. Quando o vinculador encontra uma função adequada para incorporar, ele substitui a chamada da função por instruções da função que está sendo chamada.
O vinculador aplica a otimização de ramificação embutida antes de eliminar quaisquer seções não utilizadas, para que as seções embutidas também possam ser removidas quando não forem mais chamadas.
Perceber
- Para Arm®v7-A, o vinculador pode incorporar duas instruções Thumb codificadas de 16 bits no lugar das
BL
instruções Thumb® codificadas de 32 bits.- Para Armv8-A e Armv8-M, o vinculador pode incorporar duas instruções T32 de 16 bits em vez de
BL
instruções T32 de 32 bits.
Use a -info=inline
opção de linha de comando - para listar todas as funções embutidas.
Sobre otimizar a ramificação para NOP
Embora o vinculador possa substituir branchs NOP
, em alguns casos você pode querer evitar que isso aconteça.
Por padrão, o vinculador substitui qualquer ramificação por uma realocação que resolve para a NOP
próxima instrução com a instrução. Essa otimização também pode ser aplicada se o vinculador reordenar a seção de chamada final.
No entanto, em alguns casos, você pode querer desabilitar esta opção, por exemplo, ao realizar validação ou atualizações de pipeline.
Para controlar essa otimização, use as opções de linha de comando - -branchnop
e .--no_branchnop
Reordenação do vinculador de seções de chamada final
Em alguns casos, você pode querer que o vinculador reordene a seção de chamada final.
A seção de chamada final é a seção que contém as instruções de ramificação no final da seção. Se a instrução de desvio tiver uma realocação que tenha como alvo uma função que começa em outra seção, o vinculador poderá colocar a seção de chamada final antes da seção chamada. O vinculador pode então otimizar as instruções de ramificação no final da seção de chamada final em NOP
instruções.
Para aproveitar esse comportamento, use a opção de linha de comando -tailreorder
-move a seção de chamada final antes de seu destino.
Use a -info=tailreorder
opção de linha de comando - para exibir informações sobre quaisquer otimizações de chamada final executadas pelo vinculador.
Limitações no reordenamento parcial da chamada final
Existem algumas restrições ao reordenamento das seções de chamada final.
Vinculador:
- Para cada alvo de chamada final, apenas uma parte da chamada final pode ser movida. Se houver várias chamadas finais para uma única seção, a seção de chamada final com o mesmo nome de seção será movida antes do destino. Se um nome de seção não for encontrado em uma seção de chamada final com um nome correspondente, o vinculador moverá a primeira seção que encontrar.
- A seção de chamada final não pode ser movida para fora de sua região de execução.
- A cauda não é movida antes do folheado em linha.
Mesclar constantes idênticas
O vinculador pode tentar mesclar constantes idênticas em objetos direcionados ao estado AArch32. Os objetos devem ser gerados usando Arm® Compiler for Embedded 6. armclang -ffunction-sections
A mesclagem é mais eficiente se compilada com opções. Esta opção é o padrão.
Sobre esta tarefa
O procedimento a seguir é um exemplo que mostra a funcionalidade de mesclagem.
Perceber
Se estiver usando um arquivo scatter, quaisquer áreas marcadas com um atributoOVERLAY
ouPROTECTED
afetarãoarmlink --merge_litpools
o comportamento das opções.
programa
- Crie um arquivo fonte C
litpool.c
contendo o seguinte código:int f1() { return 0xdeadbeef; } int f2() { return 0xdeadbeef; }
- Use
-S
o código-fonte compilado para criar arquivos assembly:armclang -c -S -target arm-arm-none-eabi -mcpu=cortex-m0 -ffunction-sections \ litpool.c -o litpool.s
Perceber
-ffunction-sections
é o valor padrão.Por
0xdeadbeef
se tratar de uma constante difícil de ser criada por meio de instruções, é criado um pool de texto, por exemplo:... 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 ...
Perceber
Cada função possui uma cópia das constantes, poisarmclang
essas constantes não podem ser compartilhadas entre duas funções. - Compile o código-fonte para criar o objeto:
armclang -c -target arm-arm-none-eabi -mcpu=cortex-m0 litpool.c -o litpool.o
--merge_litpools
Vincule arquivos de objeto usando opções:armlink --cpu=Cortex-M0 --merge_litpools litpool.o -o litpool.axf
Perceber
--merge_litpools
é o valor padrão.- Execute
fromelf
para visualizar a estrutura da imagem:fromelf -c -d -s -t -v -z litpool.axf
O exemplo a seguir mostra os resultados combinados:
... 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 ...