Introdução às funções de otimização do vinculador ARM

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_bestdebuga 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 GNU  ld  .

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_ARRAYou SHT_FINI_ARRAYfaz SHT_PREINIT_ARRAYparte 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 gerados  armclang 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_inlineNo 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  atributo OVERLAY ou  PROTECTED afetarão  armlink --merge_litpools o comportamento das opções.

programa

  1. Crie um arquivo fonte C  litpool.ccontendo o seguinte código:
    int f1() {
        return 0xdeadbeef;
    }
    int f2() {
        return 0xdeadbeef;
    }
  2. 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 0xdeadbeefse 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, pois  armclang essas constantes não podem ser compartilhadas entre duas funções.
  3. Compile o código-fonte para criar o objeto:
    armclang -c -target arm-arm-none-eabi -mcpu=cortex-m0 litpool.c -o litpool.o
  4. --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.
  5. 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
    ...

Acho que você gosta

Origin blog.csdn.net/Miao8miao/article/details/135428505
Recomendado
Clasificación