https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119120
Bug ID: 119120
Summary: Unnecessary fld when assigning real or imaginary part
of a complex variable
Product: gcc
Version: 14.2.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: vajdaz at protonmail dot com
Target Milestone: ---
Following C++ code generates the assembly underneath when compiling with -O0
for x86 architecture (32 bit).
C++ source:
#include <complex>
void foo(const std::complex<double>& z) {
_Complex double __t;
__real__ __t = z.real();
__imag__ __t = z.imag();
}
Generated asm code:
foo(std::complex<double> const&):
pushl %ebp
movl %esp, %ebp
subl $40, %esp
subl $12, %esp
pushl 8(%ebp)
call std::complex<double>::real[abi:cxx11]() const
addl $16, %esp
fstpl -16(%ebp)
fldl -24(%ebp) ; <= here an uninitialized load happens
fldl -16(%ebp)
fstpl -40(%ebp)
fstpl -32(%ebp)
subl $12, %esp
pushl 8(%ebp)
call std::complex<double>::imag[abi:cxx11]() const
addl $16, %esp
fstpl -24(%ebp)
fldl -24(%ebp)
fldl -16(%ebp)
fstpl -40(%ebp)
fstpl -32(%ebp)
nop
leave
ret
At the marked position loading of an uninitialized floating point value is done
unnecessarily. This may cause an unintended FPU exception if that value
accidentally is a signaling NaN.
The expected code here would be this one (no unnecessary load/store pairs):
foo(std::complex<double> const&):
pushl %ebp
movl %esp, %ebp
subl $40, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call std::complex<double>::real() const
fldl (%eax)
fstpl -24(%ebp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call std::complex<double>::imag() const
fldl (%eax)
fstpl -16(%ebp)
leave
ret
And this is the code which is generated by GCC 4.5.3 and earlier. Later GCCs
generate the (in my opinion) wrong code.
This is not an academic problem. The implementation of operator /= of
std::complex has this behavior until GCC 8.5 (inclusive).