Description
Describe the bug
The initial value for sycl::minimum<float>
is set to inf
which gets turned into 0
with -ffast-math
. More generally, both min and max are affected for any type which has_infinity
.
The issue is here:
llvm/sycl/include/sycl/known_identity.hpp
Lines 262 to 271 in 7271d61
std::numeric_limits<T>::infinity()
should not be used here when -fno-honor-infinities
is enabled (e.g. by -ffast-math
). This causes the value to be evaluated as zero when used (at least in some cases) and subsequently leads to incorrect results of a program. Note that std::numeric_limits<float>::has_infinity
evaluates to true regardless of the honor-infinities
option.
This started failing in the nightly-2024-02-16 tag. The code in question hasn't changed in months, but it seems that the behaviour of -ffast-math
has changed, possibly due to 73159a9
So it seems the code has been problematic earlier, but it was hidden by the -fno-honor-infinities
not being set in this context.
I think there could be three solutions:
- Just use
std::numeric_limits<T>::max()
in all cases. What's the benefit of using infinity here? - Add a second condition checking whether
-fno-honor-infinities
is used. - Make sure
std::numeric_limits<T>::has_infinity
evaluates to false when-fno-honor-infinities
is enabled.
To reproduce
This is a minimal example of sycl::joint_reduce
computing the minimum for a vector of 4 positive floats:
#include <sycl/sycl.hpp>
#include <cstdio>
int main() {
sycl::queue q{};
std::vector<float> data{4.0f, 1.0f, 3.0f, 2.0f};
float result{data[0]};
{
sycl::buffer<float> inBuf{data};
sycl::buffer<float> outBuf{&result, 1};
q.submit([&inBuf, &outBuf](sycl::handler& cgh){
sycl::accessor inAcc{inBuf, cgh, sycl::read_only};
sycl::accessor outAcc{outBuf, cgh, sycl::write_only};
cgh.parallel_for(sycl::nd_range<1>{1,1}, [inAcc, outAcc](sycl::nd_item<1> item){
const float* start{&inAcc[0]};
const float* end{start + inAcc.size()};
outAcc[0] = sycl::joint_reduce(item.get_group(), start, end, sycl::minimum<>{});
});
});
}
printf("Result: %f\n", result);
return 0;
}
When compiled with -O3 -ffast-math
this prints 0.0 instead of the expected 1.0:
$ clang++ -fsycl -o test test.cpp -O3 -ffast-math
./test
Result: 0.000000
It can be worked around with -fhonor-infinities
:
$ clang++ -fsycl -o test test.cpp -O3 -ffast-math -fhonor-infinities
./test
Result: 1.000000
Note this example doesn't directly use sycl::known_identity
. It presents that the issue breaks a high-level feature. I didn't manage to create a small enough reproducer directly using the value, as it tends to get inlined / evaluated at compile time, and therefore not showing the issue. I have seen the value directly evaluating to zero in a larger project though.
Environment
- Linux, Ubuntu 22.04
- any target (tested Level Zero and CUDA)
- GNU libstdc++6 v12.3.0
- DPC++ tag nightly-2024-02-16 (first failing) through 2024-05-15 (latest tested)
Additional context
No response