O símbolo de função obtém o sufixo '.part' após a compilação

7

Ao compilar cruzado o kernel Linux 3.18.10, o compilador adiciona um sufixo .part.<N> ao final de alguns símbolos (veja um exemplo abaixo). O número <N> muda ao usar diferentes defconfigs. Alguém sabe sob quais condições o compilador adiciona o sufixo da peça no final de um símbolo?

$ arm-none-linux-gnueabi-readelf -a vmlinux | grep do_kernel_fault
c03a48f8 116 FUNC LOCAL DEFAULT 2 __do_kernel_fault.part.10

    
por mblaettler 13.08.2015 / 16:06

1 resposta

8

O símbolo que termina em .part é um símbolo de função real, não algum tipo de decoração de função. Mais precisamente, uma função que termina em .part é uma função gerada pelo GCC a partir de uma função maior.

Às vezes, o GCC avalia que uma parte do fluxo de controle de uma função grande poderia ser embutida, mas não seria correto incorporar toda a função enorme. Portanto, ele divide a função para colocar a parte grande em sua própria função, que recebe como nome o nome da função original mais .part + .<some number> e insere o restante em outras funções.

Isso faz parte de uma otimização descrita no código-fonte do GCC, em gcc/ipa-split.c . No gcc-4.8.3, pelo menos (e provavelmente versões posteriores, não posso verificar agora), diz:

/* The purpose of this pass is to split function bodies to improve
   inlining.  I.e. for function of the form:

   func (...)
     {
       if (cheap_test)
   something_small
       else
   something_big
     }

   Produce:

   func.part (...)
     {
  something_big
     }

   func (...)
     {
       if (cheap_test)
   something_small
       else
   func.part (...);
     }

   When func becomes inlinable and when cheap_test is often true, inlining func,
   but not fund.part leads to performance improvement similar as inlining
   original func while the code size growth is smaller.

   The pass is organized in three stages:
   1) Collect local info about basic block into BB_INFO structure and
      compute function body estimated size and time.
   2) Via DFS walk find all possible basic blocks where we can split
      and chose best one.
   3) If split point is found, split at the specified BB by creating a clone
      and updating function to call it.  

   The decisions what functions to split are in execute_split_functions
   and consider_split.  

   There are several possible future improvements for this pass including:

   1) Splitting to break up large functions
   2) Splitting to reduce stack frame usage
   3) Allow split part of function to use values computed in the header part.
      The values needs to be passed to split function, perhaps via same
      interface as for nested functions or as argument.
   4) Support for simple rematerialization.  I.e. when split part use
      value computed in header from function parameter in very cheap way, we
      can just recompute it.
   5) Support splitting of nested functions.
   6) Support non-SSA arguments.  
   7) There is nothing preventing us from producing multiple parts of single function
      when needed or splitting also the parts.  */

Como você deve ter adivinhado, esse processo é totalmente controlado pelo compilador. O novo nome do símbolo é produzido pela função clone_function_name in gcc/cgraphclones.c . O número adicionado após .part não tem significado particular, é usado apenas para evitar conflitos de nome. É um contador simples que é incrementado toda vez que o GCC cria uma nova função a partir de um existente (o que os desenvolvedores do GCC chamam de 'clone').

Você pode usar a opção -fdisable-ipa-fnsplit para impedir que o compilador aplique essa otimização ou -fenable-ipa-fnsplit para ativá-la. Por padrão, ele é aplicado nos níveis de otimização -O2 e -O3 e desativado de outra forma.

    
por 04.12.2015 / 14:31