【发布时间】:2022-01-03 16:44:34
【问题描述】:
考虑以下代码:
bool AllZeroes(const char buf[4])
{
return buf[0] == 0 &&
buf[1] == 0 &&
buf[2] == 0 &&
buf[3] == 0;
}
带有-O3 的 Clang 13 的输出程序集:
AllZeroes(char const*): # @AllZeroes(char const*)
cmp byte ptr [rdi], 0
je .LBB0_2
xor eax, eax
ret
.LBB0_2:
cmp byte ptr [rdi + 1], 0
je .LBB0_4
xor eax, eax
ret
.LBB0_4:
cmp byte ptr [rdi + 2], 0
je .LBB0_6
xor eax, eax
ret
.LBB0_6:
cmp byte ptr [rdi + 3], 0
sete al
ret
每个字节都是单独比较的,但可以优化为单个 32 位 int 比较:
bool AllZeroes(const char buf[4])
{
return *(int*)buf == 0;
}
导致:
AllZeroes2(char const*): # @AllZeroes2(char const*)
cmp dword ptr [rdi], 0
sete al
ret
我还检查了 GCC 和 MSVC,但它们都没有进行此优化。 C++ 规范不允许这样做吗?
编辑:
将短路与 (&&) 更改为按位与 (&) 将生成优化代码。此外,更改字节比较的顺序不会影响代码生成:https://godbolt.org/z/Y7TcG93sP
【问题讨论】:
-
这可能与对齐有关吗?
-
因为编译器不知道数组的大小并且
&&是短路评估。大于0的数组索引可能仅在buf[0] == 0为true时才有效。如果第一个测试是false,那么评估buf[1] == 0 &&可能是UB -
@dave 不,函数参数中的数组大小仅供开发人员参考。
const char buf[4]与const char *buf作为函数参数完全相同。 -
@RichardCritten 这是有道理的,将
&&更改为按位与将生成优化的代码。我也尝试过先比较第 3 个字节,但再次失败:godbolt.org/z/Y7TcG93sP -
return *(int*)buf == 0;在技术上是 UB,除非传入的buf确实指向int。将任何指针转换为char *并取消引用是可以的,但将char *转换为int *并且仅当char *最初指向int时才能取消引用。 [注意:为清楚起见,删除了所有consts]
标签: c++ assembly optimization compiler-optimization