【发布时间】:2017-02-02 13:29:04
【问题描述】:
我目前有以下:
#include <cstdint>
#include <memory>
template<typename T>
bool isZeroed(T const & num) {
void const * ptr = std::addressof(num);
uint8_t const * pos = static_cast<uint8_t const *>(ptr);
uint8_t const * const endpos = pos + sizeof(T);
for (;pos < endpos; ++pos)
{
if (*pos != uint8_t(0))
return false;
}
return true;
}
int main(int argc, char * argv[])
{
return isZeroed(static_cast<uint64_t>(argc));
}
在 gcc 7 上使用 -O3 生成:
main:
movsx rdi, edi
test dil, dil
mov QWORD PTR [rsp-8], rdi
jne .L9
cmp BYTE PTR [rsp-7], 0
jne .L9
cmp BYTE PTR [rsp-6], 0
jne .L9
cmp BYTE PTR [rsp-5], 0
jne .L9
cmp BYTE PTR [rsp-4], 0
jne .L9
cmp BYTE PTR [rsp-3], 0
jne .L9
cmp BYTE PTR [rsp-2], 0
jne .L9
cmp BYTE PTR [rsp-1], 0
sete al
.L2:
movzx eax, al
ret
.L9:
xor eax, eax
jmp .L2
在我的脑海中,我认为应该可以将这些 BYTE 比较折叠成一次占用更多字节的比较,例如 WORD/DWORD/QWORD。
有人知道我在代码中做了什么阻止优化器这样做,或者这在 gcc 中是不可能的吗?
【问题讨论】:
-
我认为编译器忘记了所有字节都是有效的,因此采取措施不访问额外的内存,除非是实际需要的。如果
pos[0]不为零,则源代码不会访问pos[1-7],并且编译器会确保该行为。 PS:int通常不是 64 位的,但可能在您的环境中。无论如何,32 位的代码也是类似的。 -
所以我需要故意访问更大块的内存,因为编译器也在保留内存访问模式。
-
是的,编译器正在做你告诉它做的事情,你想让它做一些不同的事情,告诉它......
-
这不是UB吗?通过不兼容类型的指针专门访问对象(例如
*pos)。为了不那么标准,结构可以有填充,并且填充不需要为零。 -
@MargaretBloom 这可能不是未定义的行为,因为允许通过
unsigned char指针访问任何类型的对象,而uint8_t几乎必须是unsigned char或不存在。示例中使用的类型uint64_t不能有任何填充位,但一般来说,给定的类型,而不仅仅是结构,是否有填充是实现定义的。
标签: c++ gcc assembly optimization