LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 43374 - arm: silent == and != and __builtin_isnan compile to signaling compare
Summary: arm: silent == and != and __builtin_isnan compile to signaling compare
Status: RESOLVED FIXED
Alias: None
Product: libraries
Classification: Unclassified
Component: Backend: ARM (show other bugs)
Version: trunk
Hardware: All All
: P enhancement
Assignee: Unassigned LLVM Bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-09-20 03:28 PDT by Szabolcs Nagy
Modified: 2019-10-08 02:58 PDT (History)
6 users (show)

See Also:
Fixed By Commit(s):


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Szabolcs Nagy 2019-09-20 03:28:32 PDT
for

int foo(float x) {
  return __builtin_isnan(x) ? 0 : 1;
}
int bar(float x) {
  return x!=x ? 0 : 1;
}

i get

foo:
        vcmpe.f32       s0, s0
        vmrs    APSR_nzcv, fpscr
        mov     r0, #0
        movvc   r0, #1
        bx      lr
bar:
        vcmpe.f32       s0, s0
        vmrs    APSR_nzcv, fpscr
        mov     r0, #0
        movvc   r0, #1
        bx      lr

in standard conform compilation mode, i expected vcmp.f32 instead of vcmpe.f32.

in principle the difference is not observable if FENV_ACCESS is off, but since clang does not support toggling that switch it must be assumed to be always on for conforming behaviour and then != and is* operations must be non-signaling (iso c annex f requirement).
Comment 1 Stephen Hines 2019-09-20 06:58:17 PDT
Adding Kristof and Peter, since this seems ARM-specific. https://github.com/ARM-software/optimized-routines/issues/16 is where this was reported (and failing tests).
Comment 2 Kristof Beyls 2019-09-27 01:59:56 PDT
The C99 standard states in section 7.12.14:

"""
The relational and equality operators support the usual mathematical
relationships between numeric values. For any ordered pair of numeric values
exactly one of the relationships — less, greater, and equal — is true.
Relational operators may raise the ‘‘invalid’’ floating-point exception when
argument values are NaNs.
"""

My interpretation of that paragraph is that it's OK for <, <=, > and >= to
raise an exception when argument values are NaNs. It is not OK for == an != to
raise an exception when argument values are NaNs.

Therefore,
int bar(float x) {
  return x!=x ? 0 : 1;
}
should not produce an exception when x is NaN, and hence vcmp rather than vcmpe
should be produced when generating Arm code for this.

Nonetheless, http://llvm.org/viewvc/llvm-project?rev=294945&view=rev introduced
support for generating vcmp instead of vcmpe for equality comparisons. How come
vcmpe is generated (x!=x)?

The answer is that InstCombine transform the equality comparison into an
"ordered comparison":

*** IR Dump Before Combine redundant instructions ***
define dso_local i32 @foo(float %x) local_unnamed_addr {
entry:
  %cmp = fcmp une float %x, %x
  %cond = select i1 %cmp, i32 0, i32 1
  ret i32 %cond
}


INSTCOMBINE ITERATION #1 on foo
IC: ADDING: 3 instrs to worklist
IC: Visiting:   %cmp = fcmp une float %x, %x
IC: Mod =   %cmp = fcmp une float %x, %x
    New =   %cmp = fcmp uno float %x, 0.000000e+00
IC: ADD:   %cmp = fcmp uno float %x, 0.000000e+00
IC: Visiting:   %cmp = fcmp uno float %x, 0.000000e+00
IC: Visiting:   %cond = select i1 %cmp, i32 0, i32 1
IC: ADD:   %not.cmp = xor i1 %cmp, true
IC: Old =   %cond = select i1 %cmp, i32 0, i32 1
    New =   <badref> = zext i1 %not.cmp to i32
IC: ADD:   %cond = zext i1 %not.cmp to i32
IC: ERASE   %0 = select i1 %cmp, i32 0, i32 1
IC: ADD:   %cmp = fcmp uno float %x, 0.000000e+00
IC: Visiting:   %cmp = fcmp uno float %x, 0.000000e+00
IC: Visiting:   %cond = zext i1 %not.cmp to i32
IC: Visiting:   %not.cmp = xor i1 %cmp, true
IC: ADD:   %cond = zext i1 %not.cmp to i32
IC: Replacing   %not.cmp = xor i1 %cmp, true
    with   %cmp = fcmp ord float %x, 0.000000e+00
IC: Mod =   %not.cmp = xor i1 %cmp, true
    New =   %not.cmp = xor i1 %cmp, true
IC: ERASE   %not.cmp = xor i1 %cmp, true
IC: ADD:   %cmp = fcmp ord float %x, 0.000000e+00
IC: Visiting:   %cmp = fcmp ord float %x, 0.000000e+00
IC: Visiting:   %cond = zext i1 %cmp to i32
IC: Visiting:   ret i32 %cond

; Function Attrs: norecurse nounwind readnone
define dso_local i32 @foo(float %x) local_unnamed_addr #0 {
entry:
  %cmp = fcmp ord float %x, 0.000000e+00
  %cond = zext i1 %cmp to i32
  ret i32 %cond
}

Seemingly, instcombine does a transform that illegally transforms a "fcmp une"
into an "fcmp ord".

How come this issue isn't seen on x86_64 or AArch64 backends?
Those backends seem to simply not produce code that generates floating point
exceptions for any floating point relationships when argument values are NaNs.

In summary, so far, it looks to me like this is a bug in InstCombine.
Comment 3 Kristof Beyls 2019-10-08 02:58:03 PDT
Should be fixed in r374025, by never generating vcmpe, but always vcmp for floating point comparisons. This makes the ARM backend behave similar to the other backends with respect to signalling quiet NaNs in compare operations.

Also see LLVMdev thread http://lists.llvm.org/pipermail/llvm-dev/2019-October/135574.html for more background.