alive2 icon indicating copy to clipboard operation
alive2 copied to clipboard

bug in minnum and maxnum

Open regehr opened this issue 8 months ago • 2 comments

I think these functions are incorrect in one corner case.

take this IR:

declare float @llvm.min.f32(float, float)
declare float @llvm.max.f32(float, float)

declare float @llvm.minnum.f32(float, float)
declare float @llvm.maxnum.f32(float, float)

declare float @llvm.minimum.f32(float, float)
declare float @llvm.maximum.f32(float, float)

declare float @llvm.minimumnum.f32(float, float)
declare float @llvm.maximumnum.f32(float, float)

define float @f() {
  %2 = call float @llvm.maxnum.f32(float 0x0000000000000000, float 0x8000000000000000)
  ret float %2
}

here's what alive-exec says:

~/svn/code/alive2/fp$ ~/alive2-regehr/build/alive-exec tmp.ll 

----------------------------------------
define float @f() {
#0:
  %#1 = fmax float 0.000000, -0.000000
  ret float %#1
}
Executing %#0
%#1 = #x80000000
Returned #x80000000 / true
~/svn/code/alive2/fp$ 

but LangRef says that "-0.0 < +0.0 for the purposes of this intrinsic."

the analogous case for minnum is also wrong:

~/svn/code/alive2/fp$ ~/alive2-regehr/build/alive-exec tmp.ll 

----------------------------------------
define float @f() {
#0:
  %#1 = fmin float -0.000000, 0.000000
  ret float %#1
}
Executing %#0
%#1 = #x00000000
Returned #x00000000 / true

regehr avatar May 01 '25 21:05 regehr

This was a recent change in 363b05944f9212511ee6811d0eb1af841c177226

arsenm avatar May 01 '25 21:05 arsenm

ah, thanks, I'll just put the text here

commit 363b05944f9212511ee6811d0eb1af841c177226
Author: YunQiang Su <[email protected]>
Date:   Thu Feb 27 11:22:30 2025 +0800

    LangRef: Clarify llvm.minnum and llvm.maxnum about sNaN and signed zero (#112852)

    The documents claims that it ignores sNaN, while in the current code it
    may be different.

    - as the finally callback, it use libc call fmin(3)/fmax(3). while C23
    clarifies that fmin(3)/fmax(3) should return NaN for sNaN vs NUM.
    - on some architectures, such as aarch64, it converts to `fmaxnm`, which
    returns qNaN for sNaN vs NUM.
    - on RISC-V (SPEC 2019+), it converts to `fmax`, which returns NUM for
    sNaN vs NUM.

    Since we have introduced llvm.minimumnum and llvm.maximumnum, which
    follow IEEE 754-2019's minimumNumber/maximumNumber.

    So, it's time for us to clarify llvm.minnum and llvm.maxnum. Since the
    final fallback of llvm.minnum and llvm.maxnum is
    fmin(3)/fmax(3), so that it is reasonable to follow the behaviors of
    fmin(3)/fmax(3).

    Although C23 clarified the behavior about sNaN and +0.0/-0.0:
         (NUM or NaN) vs sNaN -> qNaN
         +0.0 vs -0.0 -> either one of +0.0/-0.0
    It is the same the IEEE754-2008's maxNUM and minNUM.
    Not all implementation work as expected.

    Since some architectures such as aarch64/MIPSr6/LoongArch, have
    instructions that implements +0.0>-0.0.
    So Let's define llvm.minnum and llvm.maxnum to IEEE754-2008 with
    +0.0>-0.0.

    The architectures without such instructions can implements `NSZ` flavor
    to speed up,
    and the frontend, such as clang, can call them with `nsz` attribute.

regehr avatar May 01 '25 21:05 regehr