此 C++ 代码
#include<cmath>
double f1(double a){
return std::cos(a);
}
double f2(double a){
return std::cos(a) + std::sin(a);
}
被编译成以下程序集(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
这是否意味着 gcc 知道 glibc 的标准函数并且针对使用它们的特定情况有优化技术?
当编译 GCC 本身时,您可以指定一个目标三元组,其中包括有关平台的信息以及(可能间接地)编译后的 GCC 将为其编译的目标上使用的 C 标准库实现。
例如
x86_64-linux-gnu
,或者x86_64-unknown-linux-gnu
是运行在 x86-64 处理器上的 Linux 并以 glibc 作为 C 标准库实现的目标的典型目标三元组。类似地
x86_64-linux-musl
,或者x86_64-unknown-linux-musl
将是使用 musl C 标准库实现的类似目标。此外,还有
-m
编译器开关(例如-mglibc
,,-mmusl
...)来改变假定的目标 C 标准库实现。通过这种方式,GCC 就知道目标是否支持非标准
sincos
函数,如果支持,那么它可以重写对std::cos
和的调用,由于它们由 C++ 和 C 标准指定,因此具有始终已知的语义,以您所看到的方式std::sin
调用。sincos
例如,如果你
-muclibc
使用 uclibc C 标准库实现为 Linux 目标进行编译,那么 GCC 会假定sincos
不存在并且不会重写为sincos
。(尽管我认为 uclibc 确实支持 sincos。)使用默认配置(即没有任何
-std=c*
或-ansi
标志),编译器可以重写为非标准sincos
函数,即使sincos
不是任何标准保留的名称,因此用户可以将其用于其他目的,因为 GCC 的默认选项假定 GNU 扩展(即而-std=gnu++XX
不是-std=c++XX
),而不是严格符合标准。不幸的是,即使在严格的标准符合模式下,GCC 仍然会执行这种转换,而这种情况不应该发生。请参阅此处的错误报告。