As exposed in 2013 and continues to be a problem, there’s a side-effect of our definition of “compiler runtime”.
Currently, compiler-rt has 23 sub-projects, one of them being the builtins
, which is where the problem lies.
The problem
- Compiler-RT implements functions that
libgcc
doesn’t (ex.__muloti4
). - LLVM lowers 128-bit arithmetic to calls to that builtin, because the target (AArch64) said it supports.
- The linker fails to find the symbol because the actual runtime (
libgcc
) doesn’t.
The heart of the problem is that LLVM thinks it’s supported because Compiler-RT implements it, but it’s not linked by default on GNU platforms. The compile-time decision cannot be proven safe as the information is obtained at link time.
This disconnect causes genuine responses like:
I’m confused about why you think I should need to explicitly link with compiler-rt : if the compiler doesn’t automatically link in its own runtime functions as it needs them surely that’s a compiler bug? I don’t need to explicitly link with libgcc if I’m compiling with gcc…
The work arounds
My explanations of “LLVM is a modular compiler” over the years don’t quite cut it, because this isn’t something the affected platforms can fix. There is no build flag that allows them to assume libgcc
or compiler-rt
as their runtime, so no way to enable/disable this behaviour based on the platform.
All the work-arounds are downright ugly:
- Implement it in
libgcc
: Will take years to take effect, and may not be accepted in a compatible way. - Add
-rtlib=compiler-rt -lgcc_s
(or vice-versa) to every command line. - Copy RT’s implementation in a local file somewhere (like Android did for many years).
Problems reverting the code generation choice
As exposed on the Phab review, reverting that decision, while safer from a compiler’s “least surprise principle”, will regress on all platforms that currently use Compiler-RT as their runtime (FreeBSD, Android, Chromium, Apple and others I don’t know about).
Split builtins
from Compiler-RT
So I’d like for us to consider my proposal from 2016 once again: let’s split the builtins from the rest of Compiler-RT and include it in the build by default. This would allow us to grow the builtins as much as we want, and would provide the certainty to the compiler for code generation decisions.
The easiest way, I think, would be to not make the builtins
as the default rtlib
, but to always link them through Clang
(and probably llc
, lli
, etc). It doesn’t stop people using LLVM in their tooling from having the mismatch, but there’s another mismatch there anyway, so isn’t a big problem for developers.
If the default rtlib
is indeed compiler-rt
, then linking the builtins would provide zero benefit, but still wouldn’t break linkage, I think.
Another alternative would be to just split the builtins that aren’t supported by other builtins. That’s a bit ugly and fiddly, but would allow Compiler-RT to continue to be the runtime in the same way.
We just need to decide what to do with the conflicting symbols: Do we use the runtime first, and then our builtins or vice-versa?
To answer that question, we’d need to know the expected behaviours on multiple platforms out there and I don’t have that visibility. That’s the reason this isn’t an RFC, it’s just a proposal.
There’s also a lot of “I think” in this post, which means, I really don’t know. Hoping people more familiar with linkage tricks can help here.
@compnerd @echristo @nickdesaulniers @davidchisnall @hansw2000