6

When cross compiling the Linux kernel 3.18.10, the compiler adds a .part.<N> suffix at the end of some symbols (see an example below). The number <N> changes when using different defconfigs. Does anybody know under which conditions the compiler adds the part suffix at the end of a symbol?

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

7

The symbol ending in .part is a real function symbol, not some kind of function decoration. More precisely, a function ending in .part is a function generated by GCC from a bigger function.

Sometimes, GCC evaluates that a some part of the control flow of a big function could esily be inlined, but that it would not be okay to inline the entire huge function. Therefore, it splits the function to put the big part in its own function, which receives as a name the original function name plus .part + .<some number>, and inlines the rest in other functions.

This is part of an optimization described in the GCC source code, in gcc/ipa-split.c. In gcc-4.8.3 at least (and probably later versions, I'm not able to check right now), it says:

/* 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.  */

As you may have guessed, this process is entirely controlled by the compiler. The new symbol name is produced by function clone_function_name in gcc/cgraphclones.c. The number added after .part has no particular meaning, it's used only to prevent name clashes. It's a simple counter which is incremented each time GCC creates a new function from some existing one (what the developpers of GCC call a 'clone').

You can use the option -fdisable-ipa-fnsplit to prevent the compiler from applying this optimization, or -fenable-ipa-fnsplit to enable it. By default, it's applied at optimization levels -O2 and -O3 and disabled otherwise.

  • How can I tell GCC not to do this? – JohnnyFromBF Oct 10 '16 at 17:47
  • 1
    @JohnnyFromBF You can pass the -fdisable-ipa-fnsplit to gcc when compiling. For example you can set the CFLAGS=-fdisable-ipa-fnsplit environment variable. Alternatively, you can disable all optimizations with flag -O0. – lgeorget Oct 11 '16 at 6:31
  • I tried to append -fdisable-ipa-fnsplit to KBUILD_CFLAGS in the Linux Kernel Makefile, but it seems i386 does not support this. With -O0 the .part.* symbols are still there. What's missing? – JohnnyFromBF Oct 16 '16 at 8:15
  • The Linux build system tends to play by its own rules. Try setting the EXTRA_CFLAGS variable maybe? – lgeorget Oct 17 '16 at 5:47
  • If I remember correctly it is (or was?) actually impossible to compile without -O2 because that breaks the build... – lgeorget Oct 17 '16 at 5:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.