这个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 处理器上运行 Linux 并使用 glibc 作为 C 标准库实现的目标。
同样,
x86_64-linux-musl
将是一个类似的目标,但使用 musl C 标准库实现。
此外还有
-m
编译器开关(例如 -mglibc
、-mmusl
...)来更改假定的目标 C 标准库实现。
通过这种方式,GCC 知道目标是否支持非标准
sincos
函数,如果支持,那么它可以重写对 std::cos
和 std::sin
的调用,它们的语义始终是已知的,因为它们是由 C++ 和 C 标准指定,以您所看到的方式调用 sincos
。
例如,如果您使用 uclibc C 标准库实现对 Linux 目标使用
-muclibc
进行编译,则 GCC 会假定 sincos
不存在,并且不会重写为 sincos
。 (虽然我认为uclibc确实支持sincos。)
使用默认配置(即没有任何
-std=c*
或 -ansi
标志),编译器可以重写非标准 sincos
函数,即使 sincos
不是任何标准保留的名称,并且因此,用户可以将其用于不同的目的,因为 GCC 的默认选项假定 GNU 扩展(即 -std=gnu++XX
而不是 -std=c++XX
),不严格符合标准。
不幸的是,即使在严格的标准一致性模式下,GCC 仍然会执行这种转换,而这是不应该发生的。请参阅错误报告此处。