c++gcccompiler-optimizationglibc

How can gcc optimize separate call of sin(x) and cos(x) into single sincos?


This c++ code is

#include<cmath>
double f1(double a){
    return std::cos(a);
}

double f2(double a){
    return std::cos(a) + std::sin(a);
}

is compiled into the following assembly (https://godbolt.org/z/Y578h1TKx)

f1(double):
        jmp     cos
f2(double):
        sub     rsp, 24
        lea     rdi, [rsp+8]
        mov     rsi, rsp
        call    sincos
        movsd   xmm0, QWORD PTR [rsp+8]
        addsd   xmm0, QWORD PTR [rsp]
        add     rsp, 24
        ret

Does this mean that gcc knows the glibc's standard functions and have optimization technique for specific cases using them?


Solution

  • When compiling GCC itself, you specify a target triple that includes information about the platform and (maybe indirectly) the C standard library implementation used on the target that the compiled GCC will compile for.

    For example x86_64-linux-gnu or x86_64-unknown-linux-gnu is a typical target triple for a target that runs Linux on a x86-64 processor with glibc as C standard library implementation.

    Similarly x86_64-linux-musl or x86_64-unknown-linux-musl would be a similar target that uses the musl C standard library implementation instead.

    Additionally there are -m compiler switches (e.g. -mglibc, -mmusl, ...) to change the assumed target C standard library implementation.

    In this way, GCC knows whether or not the target supports the non-standard sincos function and if it does, then it can rewrite calls to std::cos and std::sin, which have semantics that are always known because they are specified by the C++ and C standards, to a call to sincos in the way that you are seeing.

    For example if you compile with -muclibc for a Linux target using the uclibc C standard library implementation, then GCC assumes that sincos is not present and won't rewrite to sincos. (Although I think uclibc does actually support sincos.)

    With the default configuration (i.e. without any -std=c* or -ansi flag) it is ok for the compiler to rewrite to the non-standard sincos function, even though sincos is not a name reserved by any standard and could therefore be used for a different purpose by the user, because the default options of GCC assume GNU extensions (i.e. -std=gnu++XX instead of -std=c++XX), not strict conformance to the standards.

    Unfortunately GCC still performs this transformation even in strict standard conformance mode, where it shouldn't happen. See bug report here.