Bug 121534 - GCC misoptimizes C23 sinpi/cospi/tanpi during inlining of round()
Summary: GCC misoptimizes C23 sinpi/cospi/tanpi during inlining of round()
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 15.1.1
: P3 normal
Target Milestone: 15.3
Assignee: Vineet Gupta
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2025-08-13 15:23 UTC by Levi Zim
Modified: 2025-08-24 14:04 UTC (History)
4 users (show)

See Also:
Host: riscv64-linux-gnu
Target: riscv64-linux-gnu
Build: riscv64-linux-gnu
Known to work:
Known to fail:
Last reconfirmed: 2025-08-14 00:00:00


Attachments
Disasm of miscompiled s_sinpi.o (2.62 KB, text/plain)
2025-08-13 15:23 UTC, Levi Zim
Details
Disasm of s_sinpi.o (with O0 applied to sinpi()) (2.81 KB, text/plain)
2025-08-13 15:24 UTC, Levi Zim
Details
preprocessed source code of s_sinpi.c (7.86 KB, text/plain)
2025-08-13 15:45 UTC, Levi Zim
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Levi Zim 2025-08-13 15:23:31 UTC
Created attachment 62111 [details]
Disasm of miscompiled s_sinpi.o

When building glibc 2.42+r3+gbc13db739377-1 with gcc 15.1.1+r7+gf36ec88aa85a-1,
additional test failures occurs for C23 trigonometric pi-based functions(sinpi/cospi/tanpi):

FAIL: math/test-double-cospi                      
FAIL: math/test-double-sinpi
FAIL: math/test-double-tanpi
FAIL: math/test-float32x-cospi
FAIL: math/test-float32x-sinpi
FAIL: math/test-float32x-tanpi
FAIL: math/test-float64-cospi
FAIL: math/test-float64-sinpi
FAIL: math/test-float64-tanpi

All the failures are caused by unexpected "Invalid operation" exception from fflags.

Failure: sinpi (qNaN): Exception "Invalid operation" set
Failure: sinpi (-qNaN): Exception "Invalid operation" set
Failure: sinpi_downward (qNaN): Exception "Invalid operation" set
Failure: sinpi_downward (-qNaN): Exception "Invalid operation" set
Failure: sinpi_towardzero (qNaN): Exception "Invalid operation" set
Failure: sinpi_towardzero (-qNaN): Exception "Invalid operation" set
Failure: sinpi_upward (qNaN): Exception "Invalid operation" set
Failure: sinpi_upward (-qNaN): Exception "Invalid operation" set

My investigation determines that it is caused by a misoptimization
done by gcc during the inlining of round() into __{sin,cos,tan}pi functions.

This does not occur when using GCC 14.3.1+r25+g42e99e057bd7-1, which chooses to not inline the round() call.

Labeling __round() in glibc with __attribute__((noipa)) could not solve it. I am not sure whether it's caused by symbol aliasing (__round -> round) or it is not ipa that misoptimizes it.

The command-line to build the misoptimized object is gcc /build/glibc/src/glibc-build/math/s_sinpi.c -c -std=gnu11 -fgnu89-inline  -march=rv64gc -mabi=lp64d -O2 -pipe -fexceptions -Wformat -Werror=format-security -fstack-clash-protection -g -ffile-prefix-map=/build/glibc/src=/usr/src/debug/glibc -Wall -Wwrite-strings -Wundef -Wimplicit-fallthrough -fmerge-all-constants -frounding-math -fstack-protector-strong -fno-common -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -Wstrict-prototypes -Wold-style-definition  -fno-math
-errno   -fno-omit-frame-pointer -fPIE   -fno-builtin-sinpif32x -fno-builtin-sinpif64       -I../include -I/build/glibc/src/glibc-build/math  -I/build/glibc/src/glibc-build  -I../sysdeps/unix/sysv/linux/riscv/rv64  -I../sysdeps/unix/sysv/linux/riscv/include -I../sysdeps/unix/sysv/linux/riscv  -I../sysdeps/riscv/nptl  -I../sysdeps/unix/sysv/linux/wordsize-64  -I../sysdeps/unix/sysv/linux/riscv/multiarch  -I../sysdeps/unix/sysv/linux/include -I../sysdeps/unix/sysv/linux  -I../sysdeps/nptl  -I../sysdeps/pthread  -I../sysdeps/gnu  -I../sysdeps/unix/inet  -I../sysdeps/unix/sysv  -I../sysdeps/unix  -I../sysdeps/posix  -I../sysdeps/riscv/rv64/rvd  -I../sysdeps/riscv/rv64/rvf  -I../sysdeps/riscv/rvf  -I../s
ysdeps/riscv/rvd  -I../sysdeps/riscv/rv64  -I../sysdeps/riscv/multiarch  -I../sysdeps/riscv  -I../sysdeps/ieee754/ldbl-128  -I../sysdeps/ieee754/dbl-64  -I../sysdeps/ieee754/flt-32  -I../sysdeps/wordsize-64  -I../sysdeps/ieee754  -I../sysdeps/generic  -I.. -I../libio -I. -nostdinc -isystem /usr/lib/gcc/riscv64-unknown-linux-gnu/14.2.1/include -isystem /usr/lib/gcc/riscv64-unknown-linux-gnu/14.2.1/include-fixed -isystem /usr/include -D_LIBC_REENTRANT -include /build/glibc/src/glibc-build/libc-modules.h -DMODULE_NAME=libm -include ../include/libc-symbols.h  -DPIC     -DTOP_NAMESPACE=glibc -o /build/glibc/src/glibc-build/math/s_sinpi.o -MD -MP -MF /build/glibc/src/glibc-build/math/s_sinpi.o.dt -MT /build/glibc/src/glibc-build/math/s_sinpi.o   

Notably, it uses O2 and does not enable unsafe math optimizations like -ffast-math.

The round() function is from sysdeps/riscv/rv64/rvd/s_round.c for double type on riscv64.
It contains fflags save/restore code and code for handling NaN special case.
In the misoptimized assembly, fflags save/restore and NaN special case are all gone after inlining of round().

The most relevant part of the misoptimized code is:

  66:	a2c71753          	flt.d	a4,fa4,fa2
  6a:	e351                	bnez	a4,ee <.L24>

The fflags save/restore code is incorrectly optimized away and
flt.d performs a signaling comparison here even for quiet NaN.
When qNaN is passed in, the NV bit of fflags will be set, which finally causes glibc test to fail.


The misoptimized assembly and normal assembly with attribute((optimize("O0"))) are attached.
Comment 1 Levi Zim 2025-08-13 15:24:26 UTC
Created attachment 62112 [details]
Disasm of s_sinpi.o (with O0 applied to sinpi())
Comment 2 Drea Pinski 2025-08-13 15:28:40 UTC
Do you have the preprocessed source?
Comment 3 Levi Zim 2025-08-13 15:30:06 UTC
(In reply to Andrew Pinski from comment #2)
> Do you have the preprocessed source?

I will try to extract that. glibc's complex build system makes that a little hard.
Comment 4 Levi Zim 2025-08-13 15:45:50 UTC
Created attachment 62115 [details]
preprocessed source code of s_sinpi.c
Comment 5 Drea Pinski 2025-08-13 15:48:19 UTC
The round "inlining" is because it is a builtin and GCC knows about it and the target has a round optab included so this is a riscv specific issue.
Comment 6 Vineet Gupta 2025-08-14 15:06:51 UTC
FWIW while related to rounding, this is related to save/restore of FP Exception flags and not really related to rounding mode save/restore, hence de-linking with PR/120203.

A patch has been posted for this issue [1]

[1] https://gcc.gnu.org/pipermail/gcc-patches/2025-August/692605.html
Comment 7 GCC Commits 2025-08-15 17:40:59 UTC
The master branch has been updated by Vineet Gupta <vineetg@gcc.gnu.org>:

https://gcc.gnu.org/g:f5696e9b1113b0a8e464cabb8cc26c871b1d9289

commit r16-3220-gf5696e9b1113b0a8e464cabb8cc26c871b1d9289
Author: Vineet Gupta <vineetg@rivosinc.com>
Date:   Wed Aug 13 20:20:34 2025 -0700

    RISC-V: fix __builtin_round clobbering FP exceptions flags [PR121534]
    
    __builtin_round() fails to save/restore FP exception flags around the FP
    compare insn which can potentially clobber the same.
    
    Worth noting that the fflags restore bracketing is slightly different
    than the glibc implementation. Both FLT and FCVT can potentially clobber
    fflags. gcc generates below where even if branch is not taken and FCVT
    is not executed, FLT still executed. Thus FSFLAGS is placed AFTER the
    label 'L3'. glibc implementation FLT can't clobber due to early NaN check,
    so FSFLAGS can be moved under the branch, before the label.
    
    | convert_float_to_float_round
    | ...
    |   frflags     a5
    |   fabs.s      fa5,fa0
    |   flt.s       a4,fa5,fa4    <--- can clobber fflags
    |   beq a4,zero,.L3
    |     fcvt.w.s a4,fa0,rmm     <--- also
    |     fcvt.s.w  fa5,a4
    |     fsgnj.s   fa0,fa5,fa0
    | .L3:
    |    fsflags    a5            <-- both code paths
    
    Fixes: f652a35877e3 ("This is almost exclusively Jivan's work....")
    
            PR target/121534
    
    gcc/ChangeLog:
    
            * config/riscv/riscv.md (round_pattern): save/restore fflags.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/rvv/autovec/vls/math-nearbyint-1.c: Adjust
            scan pattern for additional instances of frflags/fsrflags.
    
    Signed-off-by: Vineet Gupta <vineetg@rivosinc.com>
Comment 8 Aurelien Jarno 2025-08-15 19:54:03 UTC
Thanks for the fix. Would it be possible to also backport this fix to the gcc-15 branch? Thanks in advance.
Comment 9 GCC Commits 2025-08-20 00:39:24 UTC
The releases/gcc-15 branch has been updated by Vineet Gupta <vineetg@gcc.gnu.org>:

https://gcc.gnu.org/g:f12a272169523823ff27d69f187f73c4211222ae

commit r15-10250-gf12a272169523823ff27d69f187f73c4211222ae
Author: Vineet Gupta <vineetg@rivosinc.com>
Date:   Wed Aug 13 20:20:34 2025 -0700

    RISC-V: fix __builtin_round clobbering FP exceptions flags [PR121534]
    
    __builtin_round() fails to save/restore FP exception flags around the FP
    compare insn which can potentially clobber the same.
    
    Worth noting that the fflags restore bracketing is slightly different
    than the glibc implementation. Both FLT and FCVT can potentially clobber
    fflags. gcc generates below where even if branch is not taken and FCVT
    is not executed, FLT still executed. Thus FSFLAGS is placed AFTER the
    label 'L3'. glibc implementation FLT can't clobber due to early NaN check,
    so FSFLAGS can be moved under the branch, before the label.
    
    | convert_float_to_float_round
    | ...
    |   frflags     a5
    |   fabs.s      fa5,fa0
    |   flt.s       a4,fa5,fa4    <--- can clobber fflags
    |   beq a4,zero,.L3
    |     fcvt.w.s a4,fa0,rmm     <--- also
    |     fcvt.s.w  fa5,a4
    |     fsgnj.s   fa0,fa5,fa0
    | .L3:
    |    fsflags    a5            <-- both code paths
    
    Fixes: f652a35877e3 ("This is almost exclusively Jivan's work....")
    
            PR target/121534
    
    gcc/ChangeLog:
    
            * config/riscv/riscv.md (round_pattern): save/restore fflags.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/rvv/autovec/vls/math-nearbyint-1.c: Adjust
            scan pattern for additional instances of frflags/fsrflags.
    
    Signed-off-by: Vineet Gupta <vineetg@rivosinc.com>
Comment 10 Vineet Gupta 2025-08-20 01:25:19 UTC
fixed on trunk and backported to gcc-15