为什么编译器复制一些说明
Why do compilers duplicate some instructions?
有时编译器会生成具有怪异指令重复的代码,可以安全地删除。考虑以下代码:
int gcd(unsigned x, unsigned y) {
return x == 0 ? y : gcd(y % x, x);
}
这是汇编代码(由Clang 5.0生成并启用了优化):
gcd(unsigned int, unsigned int): # @gcd(unsigned int, unsigned int)
mov eax, esi
mov edx, edi
test edx, edx
je .LBB0_1
.LBB0_2: # =>This Inner Loop Header: Depth=1
mov ecx, edx
xor edx, edx
div ecx
test edx, edx
mov eax, ecx
jne .LBB0_2
mov eax, ecx
ret
.LBB0_1:
ret
在以下片段中:
mov eax, ecx
jne .LBB0_2
mov eax, ecx
如果跳跃没有发生,则eax
是没有明显原因重新分配的。
另一个示例是功能末尾的两个RET:一个也可以完美地工作。
编译器根本不够聪明,或者有理由不删除重复?
编译器可以执行对人不明显的优化,并且删除说明并不总是使事情更快。
少量搜索表明,当RET立即在有条件分支之后,各种AMD处理器都有分支预测问题。通过用本质上是一个no-op填充该插槽,可以避免性能问题。
更新:
示例参考," AMD64处理器的软件优化指南"第6.2节(请参阅http://support.amd.com/techdocs/25112.pdf)说:
特别是避免以下两种情况:
-
任何类型的分支(条件性或无条件),其单字节近返回指令作为其目标。请参阅"示例"。
-
直接在单字节近返回ret指令之前发生的条件分支。
它还详细介绍了为什么跳跃目标应具有对齐方式,这也可能在功能结束时解释重复的RET。
任何编译器都会有一堆转换,用于重命名,展开,提升等等。结合其输出可能会导致次优案例,例如您所显示的内容。马克·格里斯(Marc Glisse)提供了很好的建议:这是一个错误报告。您正在描述窥视孔优化器的机会,以丢弃
的说明- 不影响注册表的状态&完全记忆,或
- 不影响对函数后条件重要的状态,对其公共API无关紧要。
听起来像是符号执行技术的机会。如果约束求解器找不到给定MOV的分支点,也许它确实是NOP。
相关文章:
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 使用strcpy将char数组的元素复制到另一个数组
- 是否可以初始化不可复制类型的成员变量(或基类)
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- C++ Windows 驱动程序MSB3030无法复制该文件,因为它找不到
- 复制列表初始化的隐式转换的等级是多少
- 当从函数参数中的临时值调用复制构造函数时
- 有可能在Armadillo中复制MATLAB circshift方法吗
- 复制几乎为空的数组的最快方法
- 以下示例中如何避免代码复制?C++/库达
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- unique_ptr赋值和复制构造函数说明
- 复制省略用于列表初始化,它在标准中在哪里说明?
- 复制作业说明 (C++)
- 为什么编译器复制一些说明
- 复制构造函数说明
- 正在复制分配增压选项_说明