https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128229
Bug ID: 128229
Summary: [9/10/11/12/13/14/15/16/17 Regression] x86:
FP-trapping unspecs (cvtsd2si / lrint, minpd) hoisted
out of a guarded loop by RTL loop-invariant motion;
default_unspec_may_trap_p ignores operand modes and
vector-float result modes
Product: gcc
Version: 16.1.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: rtl-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: lzy20000419 at outlook dot com
Target Milestone: ---
```c
#include <fenv.h>
#include <stdio.h>
#include <immintrin.h>
volatile int sink;
int a[16]; /* all zero: the guarded statement never runs */
__attribute__((noipa)) void f (double x, int n)
{
for (int i = 0; i < n; i++)
if (a[i])
sink = _mm_cvtsd_si32 (_mm_set_sd (x));
}
int main (void)
{
feclearexcept (FE_ALL_EXCEPT);
f (__builtin_nan (""), 16);
printf ("%d\n", fetestexcept (FE_INVALID) != 0); /* expect 0 */
}
```
`a[]` is all zero, so the conversion never runs and no FP exception may be
raised. Expected output is 0.
- `gcc -O1` / `-O2` / `-Os t.c -lm` → prints 1. `cvtsd2sil %xmm0, %ecx` is
placed before the loop, behind only the `n > 0` test.
- `-O0`, `-O2 -fno-move-loop-invariants`, clang -O2 → 0. With `feenableexcept
(FE_INVALID)` the -O2 binary gets SIGFPE.
- n = 0 → 0 (the loop is not entered), n = 1 → 1.
- trunk: https://godbolt.org/z/54MzasYxE (executors: -O2 → 1, -O2
-fno-move-loop-invariants → 0, -O0 → 0)
Same thing with a vector-float unspec (`_mm_min_pd` → `UNSPEC_IEEE_MIN`, V2DF
result; `minpd` with a NaN operand raises FE_INVALID):
```c
volatile double sink;
int a[16];
__attribute__((noipa)) void f (__m128d x, __m128d y, int n)
{
for (int i = 0; i < n; i++)
if (a[i])
sink = _mm_cvtsd_f64 (_mm_min_pd (x, y));
}
/* main: f (_mm_set1_pd (NaN), _mm_set1_pd (1.0), 16); expect FE_INVALID clear
*/
```
-O1/-O2/-Os → 1, -O0 / -fno-move-loop-invariants / clang → 0. trunk:
https://godbolt.org/z/nrPesnefW
And without intrinsics: `sink = __builtin_lrint (x);` (volatile long sink) at
`-O2 -fno-math-errno` → 1 (`-O0` and `-fno-move-loop-invariants` give 0).
Versions (-O2): the cvtsd2si case fails on 9.1 through trunk and works on 8.5
and 7.5. The minpd case fails on 6.5 through trunk and works on 6.1 and 5.4.
The lrint case fails on 8.1 through trunk and works on 7.5.