为什么编译器不丢弃未使用的参数?
Why are unused parameters not discarded by the compiler?
我玩了一下编译器资源管理器 (Godbolt),发现了一些我无法解释的东西。
假设我们有一个函数
int answer(){
return 42;
}
程序集如下所示(Clang 9,但其他编译器的行为类似):
push rbp
mov rbp, rsp
mov eax, 42
pop rbp
ret
比较有道理。现在,如果我们将一个 UNUSED 参数引入函数,例如:
int answer( double d ){ // or even without name
return 42;
}
程序集更改为:
push rbp
mov rbp, rsp
movsd qword ptr [rbp - 8], xmm0
mov eax, 42
pop rbp
ret
我的问题是为什么编译器不省略双精度数的移动,因为它显然未被使用(并且编译器知道这一点,因为它为此提供了警告)。这背后的原因是什么?
我建议你在编译器选项框中输入-O1
,你会看到优化后的结果:
mov eax, 42
ret
你可以看到那个版本的参数没有做任何事情。此外,没有创建或拆除堆栈框架,只是一个简单的 "load up the retutn value and exit".
除非用户指定(即使该指定是因为您的环境可能具有某些默认优化级别),否则几乎没有理由执行优化。
My question is why doesn't the compiler omit the moving of the double, because it is clearly unused
因为这是一个优化,你并没有告诉编译器去优化。
实际上 不 通常有充分的理由优化这种事情,除非被问到:调试像这样的非优化构建要容易得多,因为参数,本地变量和堆栈帧都以标准方式布局。
当您尝试 运行 通过调试器优化构建时,您会发现,例如,它 可能 无法向您显示函数内的双参数:即使未使用,您也可能想查看函数的调用方式(调试器能够显示优化代码的详细信息将取决于编译器、版本和选项,但是这是一般的想法)。
我玩了一下编译器资源管理器 (Godbolt),发现了一些我无法解释的东西。
假设我们有一个函数
int answer(){
return 42;
}
程序集如下所示(Clang 9,但其他编译器的行为类似):
push rbp
mov rbp, rsp
mov eax, 42
pop rbp
ret
比较有道理。现在,如果我们将一个 UNUSED 参数引入函数,例如:
int answer( double d ){ // or even without name
return 42;
}
程序集更改为:
push rbp
mov rbp, rsp
movsd qword ptr [rbp - 8], xmm0
mov eax, 42
pop rbp
ret
我的问题是为什么编译器不省略双精度数的移动,因为它显然未被使用(并且编译器知道这一点,因为它为此提供了警告)。这背后的原因是什么?
我建议你在编译器选项框中输入-O1
,你会看到优化后的结果:
mov eax, 42
ret
你可以看到那个版本的参数没有做任何事情。此外,没有创建或拆除堆栈框架,只是一个简单的 "load up the retutn value and exit".
除非用户指定(即使该指定是因为您的环境可能具有某些默认优化级别),否则几乎没有理由执行优化。
My question is why doesn't the compiler omit the moving of the double, because it is clearly unused
因为这是一个优化,你并没有告诉编译器去优化。
实际上 不 通常有充分的理由优化这种事情,除非被问到:调试像这样的非优化构建要容易得多,因为参数,本地变量和堆栈帧都以标准方式布局。
当您尝试 运行 通过调试器优化构建时,您会发现,例如,它 可能 无法向您显示函数内的双参数:即使未使用,您也可能想查看函数的调用方式(调试器能够显示优化代码的详细信息将取决于编译器、版本和选项,但是这是一般的想法)。