Clang

Resources

Clang is Article description::a "LLVM native" C/C++/Objective-C compiler using LLVM as a backend and optimizer. It aims to be GCC compatible yet stricter, offers fast compile times with low memory usage, and has useful error and warning messages for easier compile troubleshooting.

Note that the compile times regressed pretty much with llvm 9, 10, 11. Examples: Building clang 11 with gcc 9.3.0 required 28 minutes but with clang 11 it was built in 2 hours 45 minutes on the same machine. Same with llvm: From 20 minutes with gcc ith went to 2 hours 25 minutes. Same with media-libs/mesa: it went from 2 minutes 45 seconds to 17 minutes 30 seconds. No fancy optimization was used in these examples, just "-march=... -O2 -pipe".

And while older clang versions were pretty fast in building chromium (around 2 hours), gcc needed 5 hours and clang 11 requires about 18 hours now. This is not a real benchmark though, as chromium versions changed as well. But nonetheless the compile times are very significant. So if you are looking for fast compile times, clang may not be the solution at the moment.

Installation

Prerequisites

One of the goals of the Clang project is to be compatible with GCC. Occasionally some packages will fail to build correctly with it and some may build successfully but segfault when executed. Some packages also have GCC specific code and will also fail during compiling. In these events, GCC will need to be used as a fallback.

USE flags

Some packages are aware of the clang USE flag.

FILE /etc/portage/make.conf
<syntaxhighlight lang="bash">USE="clang"</syntaxhighlight>

Emerge

root #emerge --ask --update --deep --changed-use sys-devel/clang

Configuration

GCC fallback environments

Create a configuration file with a set of environment variables using Portage's built in /etc/portage/env directory. This will override any defaults for any packages that fail to compile with clang. The name used below is just an example, so feel free to choose whatever name is desired for the fallback environment. Be sure to substitute chosen name with the examples used in this article.

FILE /etc/portage/env/compiler-gccEnvironment named compiler-gcc
<syntaxhighlight lang="bash">
CC="gcc"
CXX="g++"
</syntaxhighlight>

The above is the most basic environmental variable needed. You can change it to suit your needs, such as enabling/disabling link-time optimizations, alternative AR, NM, RANLIB, and so on. Here are two examples below:

FILE /etc/portage/env/compiler-gcc-fltoEnvironment variable named compiler-gcc-flto
<syntaxhighlight lang="bash">
CC="gcc"
CXX="g++"
CFLAGS="-flto -march=native -O2 -pipe"
CXXFLAGS="${CFLAGS}"
AR="gcc-ar"
NM="gcc-nm"
RANLIB="gcc-ranlib"
</syntaxhighlight>
FILE /etc/portage/env/compiler-gcc-fno-ltoEnvironment variable named compiler-gcc-fno-lto
<syntaxhighlight lang="bash">
CC="gcc"
CXX="g++"
CFLAGS="-fno-lto -march=native -O2 -pipe"
CXXFLAGS="${CFLAGS}"
AR="ar"
NM="nm"
RANLIB="ranlib"
</syntaxhighlight>

Basically, copy over your current working GCC config from your make.conf in the event we need to use it as a fallback. If you choose to use LLVM's implementation of AR, NM, and RANLIB as detailed later in the article, be sure to set them back to the GNU versions for your GCC fallback environments as shown in the above example. If you choose not to, you can ignore the AR, NM, and RANLIB variables. If you want to use link-time optimization it's a good idea to have two separate environments like the above examples.

In the event you have to use the GCC fallback environment(s) set the appropriate flags in the /etc/portage/package.env file.

FILE /etc/portage/package.envFalling back to GCC for app-foo/bar and app-bar/baz
app-foo/bar compiler-gcc-flto        #compiled using GCC with link-time optimization since package bar compiles using flto
app-bar/baz compiler-gcc-fno-lto     #compiled using GCC with no link-time optimization since package baz fails using flto

Alternatively, if you have app-portage/flaggie installed, the /etc/portage/package.env file could be modified using:

root #flaggie app-foo/bar app-bar/baz +compiler-gcc

Testing against GCC

Those wishing to rebuild the system's @world set with clang should first enable tests via FEATURES=test in make.conf. Note that many developers do not actually care about their own tests, and as a result there may be many false positives. Because of this, it is wise to compare the package test results against GCC build tests. Some packages that fail with clang will also fail with gcc.

Clang environments

Now that we've set up a safe fallback we can proceed to enable the usage of Clang in Gentoo. There are two ways to do this: System wide using /etc/portage/make.conf or via environmental variables like the one(s) we created for the GCC fallback.

We'll use the same process as we did earlier in the article for setting up GCC fallbacks.

FILE /etc/portage/env/compiler-clangEnvironment variable named compiler-clang
<syntaxhighlight lang="bash">
CC="clang"
CXX="clang++"
</syntaxhighlight>

You can now use Clang on a per package basis by invoking the compiler-clang environmental variable you created.

FILE /etc/portage/package.envUsing the Clang compiler for app-foo/bar and app-bar/baz
app-foo/bar compiler-clang
app-bar/baz compiler-clang

Of course, that's about as basic as it gets. You can create more complex environments if you wish. It's a good idea to have a variable for link-time optimized Clang and one without if you want to take advantage of LTO as detailed later in the article. Simply create two environments, one with the flag -flto and one with -fno-lto and apply each to packages as needed.

FILE /etc/portage/env/compiler-clang-fltoEnvironment variable named compiler-clang-flto
<syntaxhighlight lang="bash">
CC="clang"                            
CXX="clang++"                         
CFLAGS="${CFLAGS} -flto"              
CXXFLAGS="${CXXFLAGS} -flto"          
LDFLAGS="-Wl,-O2 -Wl,--as-needed"     #please use whichever optimization level you're comfortable with
</syntaxhighlight>
FILE /etc/portage/env/compiler-clang-fno-ltoEnvironment variable named compiler-clang-fno-lto
<syntaxhighlight lang="bash">
CC="clang"                            
CXX="clang++"                         
CFLAGS="${CFLAGS} -fno-lto"              
CXXFLAGS="${CXXFLAGS} -fno-lto"          
</syntaxhighlight>

You can now use Clang with or without link-time optimization on a per package basis by invoking the proper environmental you created. Please see "Using link-time optimizations with Clang" for a detailed guide.

FILE /etc/portage/package.envUsing the Clang compiler for app-foo/bar and app-bar/baz
app-foo/bar compiler-clang-flto      #compiled using Clang with link-time optimization since package bar compiles using flto
app-bar/baz compiler-clang-fno-lto   #compiled using Clang with no link-time optimization since package baz fails using flto

Global configuration via make.conf

When attempting to use Clang system wide the system absolutely must have a GCC fallback! This cannot be stressed enough as the system will not be able to compile everything using Clang at the moment, such as the GCC compiler. Configuring Gentoo to use Clang system wide is simple. Change the CC and CXX variables in /etc/portage/make.conf to reference the Clang equivalents. No further configuration is necessary.

FILE /etc/portage/make.confSetting the system compiler to Clang
<syntaxhighlight lang="bash">
CC="clang"
CXX="clang++"
</syntaxhighlight>

Packages that must use GCC for compiling can be handled with one of the fallback environments created earlier.

Usage

Bootstrapping the clang toolchain

Mixing clang and its toolchain / libraries with the gcc toolchain / libraries (especially the linker) will often lead to issues like linker errors during emerge. To prevent this, the clang toolchain is built first with gcc and then with itself to get a self-providing compiler.

Prepare the environment for the clang toolchain (see above), e.g.

FILE /etc/portage/env/compiler-clang
CC="clang"
CXX="clang++"
CXXFLAGS="-stdlib=libc++ ${CFLAGS}"
LDFLAGS="-fuse-ld=lld -rtlib=compiler-rt -unwindlib=libunwind"

This example replaces not only the compiler but also the GNU linker ld with the llvm linker lld. It is a drop-in replacement, but about twice as fast as the GNU gold linker.

Set USE flags default-compiler-rt default-libcxx default-lld for clang. Then emerge clang llvm libcxx libcxxabi compiler-rt llvm-libunwind lld with the default gcc environment:

root #emerge clang llvm libcxx libcxxabi compiler-rt llvm-libunwind lld

Note that sys-libs/llvm-libunwind deals with linking issues that sys-libs/libunwind has, so it is preferred to use and replaces the non-llvm libunwind package if installed (it builds with -lgcc_s to resolve issues with __register_frame / __deregister_frame undefined symbols).

Enable the clang environment for these packages now:

FILE /etc/portage/package.env
sys-devel/llvm compiler-clang
sys-libs/libcxx compiler-clang
sys-libs/libcxxabi compiler-clang
sys-libs/compiler-rt compiler-clang
sys-libs/compiler-rt-sanitizers compiler-clang
sys-libs/llvm-libunwind compiler-clang
sys-devel/lld compiler-clang
sys-devel/clang compiler-clang

Repeat the emerge step with the new environment. The toolchain will now be rebuild with itself instead of gcc.

root #emerge clang llvm libcxx libcxxabi compiler-rt llvm-libunwind lld

It may be necessary to recompile all packages that depend on llvm directly the same way, like media-libs/mesa which uses llvm for graphic cards drivers. Otherwise gentoo keeps back some compatibility binaries to keep the packages from breaking immediately, but if this is forgotten, the packages may break on the next update. Just add them to package.env with compiler-clang environment and emerge them.

You are now free to use clang with other packages, like e.g. chromium. If these packages depend on further libraries (like dev-libs/re2 for chromium, these libraries must be built with clang as well, otherwise the linker will exit with undefined symbols).

The link-time optimization feature defers optimizing the resulting executables to linking phase. This can result in better optimization of packages but isn't standard behavior in Gentoo yet. In order to use LTO we must first install LLVM with the Gold linker plugin so Clang can pass the -plugin flag to the linker.

The Gold linker

Set the clang USE flag in the LLVM package so Portage knows to include the Gold plugin.

FILE /etc/portage/package.use
sys-devel/llvm gold

Now emerge LLVM and check the USE flags to make sure the changes went into effect.

root #emerge -1vN sys-devel/llvm
These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild   R    ] sys-devel/llvm-7.1.0:7/7.1::gentoo  USE="gold* libffi ncurses xml -debug -doc -exegesis -libedit -test -xar" ABI_X86="(64) -32 (-x32)" LLVM_TARGETS="AMDGPU BPF NVPTX (X86) -AArch64 -ARM -Hexagon -Lanai -MSP430 -Mips -PowerPC -Sparc -SystemZ -XCore" 0 KiB
[ebuild  N     ] sys-devel/llvmgold-7::gentoo  0 KiB

Total: 2 packages (1 new, 1 reinstall), Size of downloads: 0 KiB

Would you like to merge these packages? [Yes/No]

Gold must be set as the default linker since it allows clang to pass the -plugin flag. By default, Gentoo uses the bfd linker. As of July 2016, the bfd linker bundled with binutils does not support passing the -plugin as seen below.

root #/usr/bin/ld -plugin
/usr/bin/ld: unrecognized option '-plugin'
/usr/bin/ld: use the --help option for usage information
Default linker using binutils-config

Using the sys-devel/binutils-config program, set ld to default to ld.gold instead of ld.bfd.

root #binutils-config --linker ld.gold
* Setting default linker to ld.gold for x86_64-pc-linux-gnu-2.25.1 ...

Once ld is set to Gold, check the -plugin flag to see if it works.

root #/usr/bin/ld -plugin
/usr/bin/ld: -plugin: missing argument
/usr/bin/ld: use the --help option for usage information

Great! It works. If you want to switch back to the default linker change the command from ld.gold to ld.bfd.

root #binutils-config --linker ld.bfd
* Setting default linker to ld.bfd for x86_64-pc-linux-gnu-2.25.1 ...

LTO

Environment

Now that we're done setting up the Gold linker, we can create a new environment for LTO enabled clang. We covered this a little bit earlier but we'll go more into depth here. We enable LTO by passing the -flto flag to clang via the CFLAGS variable which in turn passes the -plugin flag to the linker.[1] You need to set optimization flags via the LDFLAGS variable which Clang and Gold use when linking because that's when code optimization occurs. Linker flags should have -Wl, preceding them since they're being invoked by the compiler and not run manually[2].

FILE /etc/portage/env/compiler-clang-fltoEnvironment named compiler-clang-flto
<syntaxhighlight lang="bash">
CC="clang"                            
CXX="clang++"                         
CFLAGS="${CFLAGS} -flto"              
CXXFLAGS="${CXXFLAGS} -flto"          
LDFLAGS="-Wl,-O2 -Wl,--as-needed"     #please use whichever optimization level you're comfortable with
</syntaxhighlight>

As an alternative, LLVM provides their own ar, nm, and ranlib. You're free to use them and may or may not get more mileage over using the standard ar, nm, and ranlib since they're intended to handle LLVM bitcode which Clang produces when using the -flto flag.

FILE /etc/portage/env/compiler-clang-fltoEnvironment named compiler-clang-flto
<syntaxhighlight lang="bash">
CC="clang"                            
CXX="clang++"                         
CFLAGS="${CFLAGS} -flto"              
CXXFLAGS="${CXXFLAGS} -flto"          
LDFLAGS="-Wl,-O2 -Wl,--as-needed"    #please use whichever optimization level you're comfortable with
AR="llvm-ar"
NM="llvm-nm"
RANLIB="llvm-ranlib"
</syntaxhighlight>

Now you can set /etc/portage/package.env overrides using Clang with LTO enabled.

FILE /etc/portage/package.envEnabling LTO for app-foo/bar and app-bar/baz
app-foo/bar compiler-clang-flto
app-bar/baz compiler-clang-flto

Global configuration

Similar to what we covered earlier in the article, we can do a system wide Clang with LTO enabled setup by changing our /etc/portage/make.conf file.

FILE /etc/portage/make.confSetting the system compiler to Clang
<syntaxhighlight lang="bash">
CC="clang"                            
CXX="clang++"                         
CFLAGS="${CFLAGS} -flto"              
CXXFLAGS="${CXXFLAGS} -flto"          
LDFLAGS="-Wl,-O2 -Wl,--as-needed"    #please use whichever optimization level you're comfortable with
AR="llvm-ar"
NM="llvm-nm"
RANLIB="llvm-ranlib"
</syntaxhighlight>

Again, it's up to you if you want to set the AR, NM, and RANLIB to the LLVM implementations. Since earlier in the article we set up compiler environments using Clang without LTO, GCC without LTO, and GCC with LTO, we can pick and choose which is best on a per package basis. Since the goal is to compile packages system wide with Clang using LTO and not every package will successfully compile using it, we'll have to fall back to Clang with LTO disabled or GCC. Your /etc/portage/package.env may look like this:

FILE /etc/portage/package.envExample package.env setup
app-foo/bar compiler-clang-fno-lto   #compiled using Clang with no link-time optimization since package bar fails using flto
app-bar/baz compiler-gcc-fno-lto     #compiled using GCC with no link-time optimization since package bar fails using flto
app-baz/foo compiler-gcc-flto        #compiled using GCC with link-time optimization since package foo compiles using flto

ThinLTO starting with Clang 3.9

As of Clang 3.9, a new feature called ThinLTO has been introduced. In a nutshell, ThinLTO gives you the performance of full LTO, but with the compile times of no LTO.[3] To invoke the command, simply change -flto (which defaults to full when invoked) to -flto=thin in your configuration files.

The Gold linker is still required for this so make sure you've followed the steps above to set it as the default linker.

FILE /etc/portage/make.confEnabling Clang's ThinLTO feature
<syntaxhighlight lang="bash">
CC="clang"                            
CXX="clang++"                         
CFLAGS="${CFLAGS} -flto=thin"              
CXXFLAGS="${CXXFLAGS} -flto=thin"          
LDFLAGS="-Wl,-O2 -Wl,--as-needed"    #please use whichever optimization level you're comfortable with
</syntaxhighlight>

distcc

In order to use clang on a distcc client, additional symlinks have to be created in /usr/lib*/distcc/bin:

root #ln -s /usr/bin/distcc /usr/lib/distcc/bin/clang
root #ln -s /usr/bin/distcc /usr/lib/distcc/bin/clang++

ccache

Automatic with `>=ccache-3.9-r3` when Clang is emerged.

Troubleshooting

The main place for looking up known failures with clang is bug #408963. If you hit one not reported on our Bugzilla already, please open a new bug report and make it block 408963.

Compile errors when using Clang with -flto

If the packages you're installing are failing, check your logs. Often times packages with errors like the following will need to disable LTO by invoking the compiler-clang-fno-lto environment.

FILE /var/log/portage/sys-apps:less-483-r1:20160712-034715.log
/usr/bin/x86_64-pc-linux-gnu-ld: error: version.o:1:3: invalid character
/usr/bin/x86_64-pc-linux-gnu-ld: error: version.o:1:3: syntax error, unexpected $end
/usr/bin/x86_64-pc-linux-gnu-ld: error: version.o: not an object or archive

You will also most likely see this error in every LTO failure case.

FILE /var/log/portage/sys-apps:less-483-r1:20160712-034715.log
x86_64-pc-linux-gnu-clang-3.8: error: linker command failed with exit code 1 (use -v to see invocation)

Simply add the failing package to your /etc/portage/package.env. In this case it's sys-apps/less, so we'll apply the proper override.

FILE /etc/portage/package.envExample package.env setup
sys-apps/less compiler-clang-fno-lto   #compiled using Clang with no link-time optimization since package less fails using flto

Sometimes a package will fail to compile even when disabling LTO because it requires another package which was compiled using -flto and works incorrectly. You may see an error like this:

FILE /var/log/portage/dev-libs:boehm-gc-7.4.2:20160713-085706.log
/usr/lib64/libatomic_ops.a: error adding symbols: Archive has no index; run ranlib to add one

In this case libatomic_ops is causing boehm-gc to fail compiling. Recompile the program causing the failure using your -fno-lto environment and then recompile the new program. In this case, boehm-gc fails when using LTO, so we'll add both of them to our /etc/portage/package.env file and assign the environments using the -fno-lto flags.

FILE /etc/portage/package.envExample package.env setup
dev-libs/boehm-gc		compiler-clang-fno-lto
dev-libs/libatomic_ops		compiler-clang-fno-lto

Use of GNU extensions without proper -std=

Some packages tend to use GNU extensions in their code without specifying -std= appropriately. GCC allows that usage, yet Clang disables some of more specific GNU extensions by default.

If a particular package relies on such extensions being available, you will need to append the correct -std= flag to it:

  • -std=gnu89 for C89/C90 with GNU extensions,
  • -std=gnu99 for C99 with GNU extensions,
  • -std=gnu++98 for C++:1998 with GNU extensions.

A common symptom of this problem are multiple definitions of inline functions like this:

FILE /var/log/portage/Example package error in example log
/usr/bin/x86_64-pc-linux-gnu-ld: error: ../mpi/.libs/libmpi.a(mpi-bit.o): multiple definition of '_gcry_mpih_add'
/usr/bin/x86_64-pc-linux-gnu-ld: ../mpi/.libs/libmpi.a(mpi-add.o): previous definition here
/usr/bin/x86_64-pc-linux-gnu-ld: error: ../mpi/.libs/libmpi.a(mpi-bit.o): multiple definition of '_gcry_mpih_add_1'
/usr/bin/x86_64-pc-linux-gnu-ld: ../mpi/.libs/libmpi.a(mpi-add.o): previous definition here

This is because Clang uses C99 inline rules by default which do not work with gnu89 code. To work around it, you most likely have to pass -std=gnu89 or set one of your environmental overrides to use GCC to compile the failing package if passing the right -std= flag doesn't work.

References

External resources

This article is issued from Gentoo. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.