Clang or GCC


I’m starting to look into getting julia working on riscv, specifically a VisionFive2 Board. For that I need to choose a recommended toolchain.

Does GCC or clang do significantly better or do they just trade blows similarly to other architectures?

Right now, trading blows seems to be the case. Some things, GCC does ever so slightly better on things, and vice-versa.


If you not care GPL license, GCC is Better. LLVM is “business friendly”.

From my field experience, clang does things better even with default cflags passed to it like -O2 -g. Sometimes GCC wins because it’s native for certain software. If applying sufficient CFLAGS for this platform like

-menable-experimental-extensions -mabi=lp64d -march=rv64imafdczbb_zba -mcpu=sifive-u74 -mtune=sifive-7-series -pipe --param l1-cache-size=32 --param l2-cache-size=2048

, then both will be at par for most applications because both know about listed extensions. Make sure you’re using GCC at least 12, older might be too buggy and produce bug riddled code (omit -menable-experimental-extensions if using gcc).

If you’re building on VF2 itself, note that clang is kinda slower to execute compared to gcc which is fast.

Personally I use clang and try to build anything with it because in many times it wins in speed. Not much but it’s noticeable. And of course to see how compatible software with standards today, i.e. not tied to gccisms.

1 Like

Actually, with Jeff Geerling’s results, the LLVM backend trends to make innocuous but still incorrect outputs of code compared to GCC. I haven’t done any checks myself…yet…but I’d be inclined to accept Jeff’s observations for right now.

From my field experience with both on embedded targets as well as X86, you’d be right- but right now it’s kind of a coin-toss as to whether it does it right or not.

1 Like

As for building on the VF2…ugh…that’s what cross compilers are for, right? :rofl:

GCC isn’t a license concern for business. Unless you’re shipping it, at which you’d need the license consideration, the code produced by GCC is not covered under the GPL license grant. It’s subject to whatever licensing you see fit to use with it or from the person providing it to you.

libgcc is GPL licensed. GCC’s stdc++ lib is GPL .
You must careful use GCC to keep the licenses are not be polluted .
SO, change is a easy way…

1 Like

I always build my kernel inside the box with clang. I have my personal bias toward llvm.

1 Like

Nope it is a bit more complicated than that, all lib that GCC provide that can only be statically linked to an executable are LGPL at best, or with a clear exemption license at worse, as in it is not the standard GPL:

See: GCC Runtime Library Exception Rationale and FAQ - GNU Project - Free Software Foundation


Therefore, these libraries have always had license exceptions that allow people to distribute the object code GCC produces under any license. We are now moving these libraries to GPLv3 and updating their exceptions. Our fundamental policy has not changed; the new license is meant to permit all the uses of GCC that were permitted before.

The permission you need—to convey the object code from these GCC libraries under your own project’s license—is primarily contained in section 1:

You have permission to propagate a work of Target Code formed by combining the Runtime Library with Independent Modules, even if such propagation would otherwise violate the terms of GPLv3, provided that all Target Code was generated by Eligible Compilation Processes. You may then convey such a combination under terms of your choice, consistent with the licensing of the Independent Modules.

Basically the GPL when the library are GPL cannot be enforced when the executable is linked with these library.

This does not cover the gnu libc though which is LGPL, but the libc (unlike the libstdc++) is not directly part of GCC

1 Like

glibc is LGPL.
If you created a LFS-based rootfs, you need release sources at the same time. This is a common choice in embedded.
This will add a lot of work.
But musl does not have this things. Sell the finial porduct will be easy.

“LLVM backend trends to make innocuous but still incorrect outputs of code compared to GCC.”

I’d be very skeptical about prescribing that conclusion widely. Compiler bugs happen, but it’s (thankfully) not 1985 and compiler bug (as opposed to undefined behavior which just happens to be different between the two) are pretty rare in sensible code these days. These days, the RISC-V side (especially Vector support, which doesn’t really apply here) is the newest side of the parser/generator/optimizer/emitter matrix, so that’s a comparatively small percentage of a modern compiler suite.

There was a time when LLVM and GCC devs were running the test suite for each other on their own compilers. Certainly they both run the well-known benchmarking suites on their stuff regularly, if not with every build during CI.

I won’t say silly things like it’s impossible that you found an actual bug in either toolchain, but these days, they’re both of super high quality (both are used to build entire operating systems) and are widespread. If you’re turning on “experimental extensions” and complaining about bugs, that’s just not fair.

Please be sure you’re sharing quantifiable conclusions when extrapolating to “tends to make” when discussing systems as wide and deep as a compilation environment.


I agree with that. I must admit that, given enough hints about platform you’re building for, both compilers emit mature code.

As a person who rebuilt entire Slackware with LLVM, I cannot say I ever had a problem with wrong code. UB’s caused by wrong code which used to be x86 only - yeah, but portable code emit same and expected behavior.

I also check asm listings often. Both compilers (latest versions available for production) emit quite reasonable code. GCC <=10.x emitted trashy code if not hinted enough (especially crypto code suffered much), but now it also quite good at guessing platform type.


Because it can, it is already fast enough, dont undermine it.

Also, quite a load of programs simply are not designed or adapted well for cross compiling. Heck, even those using quite old autotools or scons or whatever you stuck with, not enabling features that could be detected when running on platform you targeting for. The best result is that your code is slower than expected. The worst however is simple denial of service in form of segfaults, asserts, missing namespace symbols, symvers and other un-overridable conditions that you encounter into, and it is worse if your OS missing development tools right now and here, refusing you to fix trouble with recompiling.

A good read by Rob Landley (one of major former contributors to BusyBox) on cross compiling once was available, but I fail to find it now. Perhaps it was in his plain html blog post somewhere. But it boiled down to a very simple thing: its better to build everything in a system you target for. And if your system is weak enough for that, then use an emulator which will be close enough to it. A good QEMU config will suffice.

Can anyone explain these odd results? using zstd 1.5.4 as benchmark.
Compression bandwidth is twice as good without any RISC-V specific options.


gcc (GCC) 13.2.1 20230801

make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   23.2 MB/s,   42.8 MB/s

CFLAGS="-march=rv64imafdc_zba_zbb -mcpu=sifive-u74 -mtune=sifive-7-series" CXXFLAGS="-march=rv64imafdc_zba_zbb -mcpu=sifive-u74 -mtune=sifive-7-series"  make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   12.5 MB/s,   14.6 MB/s

CFLAGS="-mcpu=sifive-u74 -mtune=sifive-7-series" CXXFLAGS="-mcpu=sifive-u74 -mtune=sifive-7-series"  make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   12.8 MB/s,   14.3 MB/s

CFLAGS="-march=rv64imafdc_zba_zbb" CXXFLAGS="-march=rv64imafdc_zba_zbb" make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   13.0 MB/s,   14.5 MB/s

clang version 16.0.6

CC=clang CXX=clang++  make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   23.3 MB/s,   59.5 MB/s

CC=/usr/lib/llvm14/bin/clang-14 CXX=/usr/lib/llvm14/bin/clang-14++ make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   23.1 MB/s,   57.6 MB/s

CC=/usr/lib/llvm15/bin/clang-15 CXX=/usr/lib/llvm15/bin/clang-15++ make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   22.8 MB/s,   58.8 MB/s

CFLAGS="-mtune=sifive-7-series" CXXFLAGS="-mtune=sifive-7-series" CC=clang CXX=clang++ make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   12.6 MB/s,   17.1 MB/s

CFLAGS="-mcpu=sifive-u74" CXXFLAGS="-mcpu=sifive-u74" CC=clang CXX=clang++ make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   12.7 MB/s,   17.1 MB/s

CFLAGS="-menable-experimental-extensions" CXXFLAGS="-menable-experimental-extensions" CC=clang CXX=clang++ make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   12.2 MB/s,   17.1 MB/s

CFLAGS="-menable-experimental-extensions -mabi=lp64d -march=rv64imafdczbb_zba -mcpu=sifive-u74 -mtune=sifive-7-series -pipe --param l1-cache-size=32 --param l2-cache-size=2048" CXXFLAGS="-menable-experimental-extensions -mabi=lp64d -march=rv64imafdczbb_zba -mcpu=sifive-u74 -mtune=sifive-7-series -pipe --param l1-cache-size=32 --param l2-cache-size=2048" CC=clang CXX=clang++ make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   13.1 MB/s,   17.5 MB/s

with -mabi=lp64d the performance is equal to no flags.

CFLAGS="-O2 -pipe -fomit-frame-pointer --param l1-cache-size=32 --param l2-cache-size=2048 -mabi=lp64d -march=rv64imafdc_zicsr_zba_zbb -mcpu=sifive-u74 -mtune=sifive-7-series" make -j4
zstd -T4 -b3 ~/silesia.tar
 3#silesia.tar       : 211948544 ->  66543158 (x3.185),   22.8 MB/s,   43.4 MB/s