ABI of libgcc functions on hard-float ARM

Hi,

I’ve run into a compatibility issue with the calling convention of the runtime library (libgcc/compiler-rt) functions on ARM with hard floating point. Clang passes the floating point arguments in integer registers (like with -mfloat-abi=soft), but GCC calls them like other functions with the hard-float ABI. The only reference to this I can find online is this LLVM development mailing list post discussing why Clang switched to always using soft-float. At least one of the Clang developers thought GCC did this too, but it doesn’t seem to currently. Complex arithmetic is implemented with these functions even when hardware floating point is supported, which means complex arithmetic is pretty completely broken on platforms like this when using libgcc.

I found the code in clang’s lib/CodeGen/TargetInfo.cpp which sets BuiltinCC to CallingConv::ARM_AAPCS and the attribute((pcs(“aapcs”))) in compiler-rt’s lib/builtins/int_lib.h which compiles the relevant functions that way. I can’t find any evidence of GCC doing either of those.

In all three copies of libgcc.a I’ve disassembled (Debian Jessie armhf and both of the ones from gcc-linaro-4.9-2015.05-x86_64_arm-linux-gnueabihf) __divdc3 (for example) definitely uses the hard-float ABI. In clang+llvm-3.8.0-armv7a-linux-gnueabihf/lib/clang/3.8.0/lib/linux/libclang_rt.builtins-armhf.a (downloaded from llvm.org), __divdc3 is definitely using the soft-float ABI.

I wrote up a simple test program to demonstrate the effects:

$ cat test_complex.c
#include <complex.h>
#include <stdio.h>

int main(int argc, char **argv) {
double complex two = 2.0 + 0.0 * I;

// Prevent GCC from just optimizing all the math out.
if (argv[0] == ‘\0’) {
two += 1.0;
}

double complex result = two / two;
if (creal(result) > 1.5) {
printf(“broken\n”);
} else {
printf(“works\n”);
}
}

$ gcc -lm test_complex.c -O3 && ./a.out
works

$ clang -lm test_complex.c -O3 && ./a.out
broken

I’ve tried with Clang 3.7.1 I cross-compiled for armhf myself and 3.8.0 binaries downloaded from llvm.org.

I’m pretty sure using clang with libgcc is supposed to work, so this seems like a bug. Is there some solution I’m not seeing?

Thanks in advance,
Brian Silverman

Hi Brian,

As far as I'm aware, your analysis is spot on. Compiler-RT does assume
soft-float for obvious reasons, but as you have demonstrated, this is
not correct in some cases. GCC seems to have multiple.

These are the variants I have on my box:

/usr/lib/gcc/arm-none-eabi/5.3.0/armv6-m/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/armv7-m/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/armv7e-m/softfp/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/armv7e-m/fpu/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/armv7e-m/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/armv7-ar/thumb/softfp/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/armv7-ar/thumb/fpu/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/armv7-ar/thumb/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/fpu/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/thumb/libgcc.a
/usr/lib/gcc/arm-none-eabi/5.3.0/libgcc.a

While it'd be great to have all of them optimised for each arch, I
think we can start with hard and soft float for now. I also don't know
if we support Thumb-only code in compiler-rt, but we should.

I checked with the GCC folks, and the behaviour is:

* arm-linux-gnueabi has *only* soft-float
* arm-linux-gnueagihf has *only* hard float
* arm-none-eabi has *all* variations (above) with the default being soft-float

Can you create a bug in LLVM's bugzilla with your analysis and all the
info here, so we can have a look? Please copy me and the people I've
added CC on this email.

Thanks!
--renato

While it'd be great to have all of them optimised for each arch, I
think we can start with hard and soft float for now. I also don't know
if we support Thumb-only code in compiler-rt, but we should.

I remember setting up soft-float v6m and v4t multilibs, which both had to be thumb-only. Not sure if that made it upstream or not, off the top of my head. IIRC, the patch for it was almost trivial.

Jon

Hi Jon,

Do you mean it should be trivial to build multiple versions of
compiler-rt on the same build? If so, I think we should do it.

Also, Brian, add Jon on the bug, too. :slight_smile:

cheers,
--renato

I remember setting up soft-float v6m and v4t multilibs, which both had to be
thumb-only. Not sure if that made it upstream or not, off the top of my
head. IIRC, the patch for it was almost trivial.

Hi Jon,

Do you mean it should be trivial to build multiple versions of
compiler-rt on the same build? If so, I think we should do it.

Yeah, I used the compiler-rt makefiles for iOS as a starting point. As I understand it, doing the same thing for the Cmake build would be nontrivial.

Also, Brian, add Jon on the bug, too. :slight_smile:

Yes please!

Jon

Right, I thought so.

Though, it should still be possible, even if a bit messy, creating
multiple *different* "builtins" archives and just changing the
compilation flags and final path for them.

Maybe this could go together with the idea of building *all*
variations that have supported targets in the LLVM build?

cheers,
--renato

Filed as #27455.

Any thoughts on workarounds so Clang 3.7 will work with libgcc for now? For example, can anybody think of unintended consequences to just changing BuiltinCC to ARM_AAPCS_VFP for my build?

Compiling appropriate compiler-rt libraries for each architecture sounds helpful.

Thanks,
Brian

I think it should be fine, modulo bugs in the build system, to change the CC.

Cheers,
Renato