This is an archive of the discontinued LLVM Phabricator instance.

[builtins] Fix ABI-incompatibility with GCC for floating-point compare
ClosedPublic

Authored by arichardson on Mar 8 2021, 11:48 AM.

Details

Summary

While implementing support for the float128 routines on x86_64, I noticed
that __builtin_isinf() was returning true for 128-bit floating point
values that are not infinite when compiling with GCC and using the
compiler-rt implementation of the soft-float comparison functions.
After stepping through the assembly, I discovered that this was caused by
GCC assuming a sign-extended 64-bit -1 result, but our implementation
returns an enum (which then has zeroes in the upper bits) and therefore
causes the comparison with -1 to fail.

Fix this by using a CMP_RESULT typedef and add a static_assert that it
matches the GCC soft-float comparison return type when compiling with GCC
(GCC has a libgcc_cmp_return mode that can be used for this purpose).

Also move the 3 copies of the same code to a shared .inc file.

Diff Detail

Event Timeline

arichardson created this revision.Mar 8 2021, 11:48 AM
arichardson requested review of this revision.Mar 8 2021, 11:48 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 8 2021, 11:48 AM
Herald added a subscriber: Restricted Project. · View Herald Transcript
arichardson added inline comments.Mar 8 2021, 11:49 AM
compiler-rt/lib/builtins/fp_compare_impl.inc
26–32

I'm not sure there's much point keeping these enums named, I could just change the return type of the helper functions to CMP_RESULT?

  • Remove name from LE_RESULT/GE_RESULT enum and return CMP_RESULT directly
compnerd accepted this revision.Mar 9 2021, 8:42 AM
compnerd added inline comments.
compiler-rt/lib/builtins/fp_compare_impl.inc
15

I think that we should use intptr_t instead. That ensures that this remains the same size on LLP64 environments.

19

Can we use the reserved spelling for mode please? (__mode__)

26–32

The named result constants is the useful thing IMO. Changing the return types to CMP_RESULT is fine. However, please put a trailing comma on the enum cases so that it is similar to the GE case.

This revision is now accepted and ready to land.Mar 9 2021, 8:42 AM
arichardson added inline comments.Mar 9 2021, 9:49 AM
compiler-rt/lib/builtins/fp_compare_impl.inc
15

Good point, I did not think about LLP64 platforms, I'm not sure what GCC would use there?

Unfortunately that is incorrect for the environment I mostly work with (CHERI). In our environment pointers/intptr_t are 128+1-bit capabilities but long/long long is still 64 bits (we also have a 32-bit version where pointers are 64+1 bits).

intptr_t would happen to work on CHERI-RISC-V and Arm Morello due to a merged register file where it will return a value with the other 65 bits zeroed. However, for our MIPS adaptation we have a split register file where intptr_t and long are passed in different registers. In this case using intptr_t would place the return value in register $c3 instead of the expected $v0.

If long is incorrect for LLP64, I think ptrdiff_t (or the non-standard ssize_t) might work on all supported architectures (we don't do segmented AS) despite not technically being correct.

19

Sure, will update.

arichardson marked an inline comment as done.
  • handle LLP64 ABIs
  • shared implementation of __unord{s,d,t,}f2
  • clang-format
  • add missing comma
  • use mode
arichardson added inline comments.Mar 10 2021, 2:12 AM
compiler-rt/lib/builtins/fp_compare_impl.inc
15

I just tested with x86_64-w64-mingw32-gcc-10.2.0 but there doesn't seem to be a builtin macro to check for LLP64 (other than _WIN64), so I went for __SIZEOF_POINTER__ == 8 && __SIZEOF_LONG__ == 4

jrtc27 added a subscriber: jrtc27.Mar 10 2021, 11:12 AM
jrtc27 added inline comments.
compiler-rt/lib/builtins/fp_compare_impl.inc
15

intptr_t is definitely wrong, it's always an integer and never a pointer (see CHERI which cares about the distinction; using intptr_t would give us inefficient code gen). In reality ssize_t is about as good as you can get, and would suffice other than things like the AArch64 weirdness below.

19

AArch64 looks like it's using SImode i.e. a 32-bit int (aarch64_libgcc_cmp_return_mode). In practice I assume the calling convention will mean that extending too much doesn't matter (although perhaps may cause weirdness if GCC assumes the upper half of the value is 0 not sign-extended and relies on that for later computation?), but the static assertion will fail here.

  • Fix CMP_RESULT typedef on aarch64
arichardson added inline comments.Mar 12 2021, 2:02 AM
compiler-rt/lib/builtins/fp_compare_impl.inc
20

We could probably avoid the LLP64 ifdef by using typedef int CMP_RESULT __attribute__((__mode__(__word__))); but I'm not particularly keen on using this rather ugly GCC attribute for non-GCC builds.

ping. Is the current version okay or should I use something like typedef int CMP_RESULT __attribute__((__mode__(word))); instead?

If there are no further comments, I will commit this early next week.

This revision was landed with ongoing or failed builds.Apr 28 2021, 4:23 AM
This revision was automatically updated to reflect the committed changes.
aykevl added a subscriber: aykevl.May 4 2022, 10:06 AM

This change resulted in a regression for AVR: D124939

Herald added a project: Restricted Project. · View Herald TranscriptMay 4 2022, 10:06 AM