【发布时间】:2021-05-23 16:14:02
【问题描述】:
这与Why can't GCC generate an optimal operator== for a struct of two int32s? 有关。我在 godbolt.org 上玩这个问题的代码,发现了这种奇怪的行为。
struct Point {
int x, y;
};
bool nonzero_ptr(Point const* a) {
return a->x || a->y;
}
bool nonzero_ref(Point const& a) {
return a.x || a.y;
}
对于nonzero_ptr,clang -O3(所有版本)产生这个或类似的代码:
mov al, 1
cmp dword ptr [rdi], 0
je .LBB0_1
ret
.LBB0_1:
cmp dword ptr [rdi + 4], 0
setne al
ret
这严格实现了 C++ 函数的短路行为,仅当 x 字段为零时才加载 y 字段。
对于nonzero_ref,clang 3.6 及更早版本生成与nonzero_ptr 相同的代码,但clang 3.7 到11.0.1 生成
mov eax, dword ptr [rdi + 4]
or eax, dword ptr [rdi]
setne al
ret
无条件加载y。当参数是指针时,没有任何版本的 clang 愿意这样做。为什么?
我能想到的唯一情况(在 x64 平台上)分支代码的行为会明显不同,这是在 [rdi+4] 处没有映射内存时,但我仍然不确定为什么 clang 会考虑这种情况对于指针而不是引用很重要。我最好的猜测是,有一些语言法律论点认为引用必须是“完整对象”,而指针不一定是:
char* p = alloc_4k_page_surrounded_by_guard_pages();
int* pi = reinterpret_cast<int*>(p + 4096 - sizeof(int));
Point* ppt = reinterpret_cast<Point*>(pi); // ok???
ppt->x = 42; // ok???
Point& rpt = *ppt; // UB???
但如果规范暗示,我不知道如何。
【问题讨论】:
-
我怀疑参考版本更优化
-
看起来像Clang wants
align 4和dereferenceable(8)来优化代码,不喜欢dereferenceable_or_null(8)而不是dereferenceable(8)。
标签: c++ clang language-lawyer x86-64 pass-by-reference