问题描述
有一个小的func
函数,用于将存储块与静态const调零数组进行比较。这是一个说明问题的原始示例:
#include <cstring>
#include <memory>
#define MAX_BYTES (256)
inline int my_memcmp(const void * mem1,const void * mem2,const size_t size)
{
const auto *first = reinterpret_cast<const uint8_t *>(mem1);
const auto *second = reinterpret_cast<const uint8_t *>(mem2);
if (size < 8)
{
for (int i = 0; i < size; ++i) {
if (*first != *second) return (*first > *second) ? 1 : -1;
++first; ++second;
}
return 0;
}
return std::memcmp(mem1,mem2,size);
}
bool func(const uint8_t* in,size_t size)
{
size_t remain = size;
static const uint8_t zero_arr[MAX_BYTES] = { 0 };
while (remain >= MAX_BYTES)
{
if (my_memcmp(in,zero_arr,MAX_BYTES) != 0)
{
return false;
}
remain -= MAX_BYTES;
in += MAX_BYTES;
}
return true;
}
- 编译器:gcc 9.1及更高版本
- 编译器标志:
-fno-inline -O3
- Godbolt拆卸链接:https://godbolt.org/z/P8vKGq
- Godbolt程序执行链接:https://godbolt.org/z/qr8f16
如果我使用-fno-inline
编译器标志,则编译器会尝试优化上面的代码,并仅为my_memcmp
函数生成两行代码,但是似乎总是返回0:
my_memcmp(void const*,void const*,unsigned long) [clone .constprop.0]:
movzx eax,BYTE PTR [rdi]
ret
直到我添加-fno-inline
后,问题才能重现(我在编译覆盖率测试代码时遇到了这个问题,因此我需要添加no-inline以使报告更加清晰。)发现gcc 8没有这个问题。是否有合理的解释,或者仅仅是GCC 9和10中的错误?
解决方法
这是GCC错误95189,https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189
基本上,如果其中一个缓冲区具有已知的内容,则GCC可以为memcmp发出专用代码,但是如果遇到零字节,则该专用功能将无法正常工作(因为它专用于其他功能,如strcmp)。 >
它似乎已经在GCC主开发分支(主干)上得到了修复,但是该修复尚未向后移植到9.x和10.x版本分支。
此C语言中的最小副本在-O2处错误编译,该错误的注释中提到了类似的示例:
int f(const char *p)
{
return __builtin_memcmp(p,"\0\0\0",4);
}