Bug 30998 - fesetexceptflag clears too many floating-point exception flags on alpha
Summary: fesetexceptflag clears too many floating-point exception flags on alpha
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: math (show other bugs)
Version: 2.36
: P2 normal
Target Milestone: 2.39
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-10-24 18:19 UTC by Bruno Haible
Modified: 2023-12-19 18:39 UTC (History)
1 user (show)

See Also:
Host: alpha-linux-gnu
Target:
Build:
Last reconfirmed:


Attachments
test case foo.c (497 bytes, text/x-csrc)
2023-10-24 18:19 UTC, Bruno Haible
Details
proposed fix (290 bytes, patch)
2023-10-24 18:24 UTC, Bruno Haible
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Bruno Haible 2023-10-24 18:19:55 UTC
Created attachment 15196 [details]
test case foo.c

On Linux/alpha, the function fesetexceptflag() clears some exception flags that are outside the EXCEPTS argument. This violates ISO C 23 ยง 7.6.4.5, which says:
  "The fesetexceptflag function attempts to set the floating-point status flags indicated by the argument excepts to the states stored in the object pointed to by flagp."

How to reproduce:
Compile and run the attached program.
$ gcc -Wall foo.c -lm
$ ./a.out

Expected: It terminates with exit code 0.
Actual:
a.out: foo.c:58: main: Assertion `fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO' failed.
Aborted

When you compare the program's lines 49 and 58, you see that the invocation
  fesetexceptflag (&saved_flags_2, FE_OVERFLOW)
not only modified the FE_OVERFLOW flag bit, but also cleared the FE_DIVBYZERO flag bit.
Comment 1 Bruno Haible 2023-10-24 18:20:28 UTC
Seen on Debian 12.0/alpha.
Comment 2 Bruno Haible 2023-10-24 18:24:06 UTC
Created attachment 15197 [details]
proposed fix

Find attached a proposed fix.

The bug is obviously in glibc/sysdeps/alpha/fpu/fsetexcptflg.c .
The line

  tmp = (tmp & ~SWCR_STATUS_MASK) | (*flagp & excepts & SWCR_STATUS_MASK);

needs to be changed to

  tmp = (tmp & ~(excepts & SWCR_STATUS_MASK)) | (*flagp & excepts & SWCR_STATUS_MASK);

A optimized expression (produces one less instruction when compiled by "alpha-linux-gnu-gcc -O2") is:

  tmp ^= (tmp ^ *flagp) & excepts & SWCR_STATUS_MASK;
Comment 3 Adhemerval Zanella 2023-12-19 18:39:26 UTC
Fixed on 2.39 (80a40a9e14d9a01e3f70c5b37ecd1da83033b6de).