陈泽霖*_*陈泽霖 4 c++ optimization assembly compiler-optimization undefined-behavior
操作系统:ubuntu2204
编译器:gcc 11.2 x86_64
这是一个简单的代码:
#include <cstdlib>
int func(int val) {
if (val == 1) {
} else {
abort();
}
}
int main(int argc, char* argv[]) {
func(argc);
}
Run Code Online (Sandbox Code Playgroud)
当我在没有任何优化的情况下编译它并运行它时,它工作正常。
但是当我用 编译它时g++ tmp.cpp -O3,结果func会忽略输入值,而只调用abort.
当然,我可以通过在 的末尾添加 return 语句来修复它func,但是,为什么呢?
这是objdump -d a.out优化函数的一些输出func:
0000000000001060 <_Z4funci>:
1060: f3 0f 1e fa endbr64
1064: 50 push %rax
1065: 58 pop %rax
1066: 50 push %rax
1067: e8 e4 ff ff ff call 1050 <abort@plt>
Run Code Online (Sandbox Code Playgroud)
Jan*_*tke 14
首先,在没有return语句的情况下从非 void 函数的末尾流出是 C++ 中的未定义行为。请参阅为什么在不返回值的情况下流出非 void 函数的末尾不会产生编译器错误?通常,编译器使用未定义的行为进行优化。也可以看看:
直观上,编译器可以看到分支中存在未定义的行为,if (val == 1)因为您从函数末尾流出。然后,编译器可以说该分支无法访问并始终调用abort(),就好像条件始终是一样false。
更具体地说,LLVM 从以下 IR 开始:
entry:
// ...
br i1 %cmp, label %if.then, label %if.else
if.then:
br label %if.end
if.else:
call void @abort()
unreachable
if.end:
unreachable
Run Code Online (Sandbox Code Playgroud)
根据 的值val,控制流分支到if.then(立即转到if.end)或到if.else(调用函数[[noreturn]])。
在SimplifyCFG(简化控制流图)过程中,该结构被简化为:
%1 = xor i1 %cmp, true
call void @llvm.assume(i1 %1)
call void @abort()
unreachable
Run Code Online (Sandbox Code Playgroud)
编译器现在假设val ^ 1始终为真,这意味着它val必须是false。不再有分支,而是控制直接流入abort(). 直观上,if 语句被更改以防止到达unreachable指令。具体来说,消除br跳转到包含该基本块的a。unreachable
经过进一步优化后,整个函数本质上变成:
tail call void @abort()
unreachable
Run Code Online (Sandbox Code Playgroud)
请访问https://godbolt.org/z/bnvh7KrG4查看整个优化流程
在调试版本中,如果没有优化,编译器不会简化 if 语句。该代码“按预期”工作。GCC 和 clang 都会输出类似以下内容的内容:
je .L2
call abort
.L2:
ud2
Run Code Online (Sandbox Code Playgroud)
从函数末尾流出成为ud2伪指令(从 生成unreachable),执行它会立即停止程序。
这个答案特定于 clang 并且您正在使用 GCC,但是,这些编译器执行的优化相对相似,特别是在像这样的小情况下。
这与警告无关,而是与警告的含义有关。每个执行分支都必须有一个有效的结束(对于非void函数,通常是return带值、抛出异常或终止程序),否则您的程序将出现未定义行为。未定义的行为可能会导致时间旅行(除其他外)。
最有可能的是,优化器注意到,如果不调用未定义行为,就不可能val等于1,并且由于允许假设代码中没有 UB,因此它可以假设这val == 1永远不会成立,因此它将所有内容优化为合理的路径在代码中。
| 归档时间: |
|
| 查看次数: |
228 次 |
| 最近记录: |