首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么Clang会为引用和非空指针参数生成不同的代码?

为什么Clang会为引用和非空指针参数生成不同的代码?
EN

Stack Overflow用户
提问于 2021-02-21 10:01:14
回答 1查看 228关注 0票数 9

这与Why can't GCC generate an optimal operator== for a struct of two int32s?相关。我在godbolt.org上玩弄这个问题的代码时,注意到了这个奇怪的行为。

代码语言:javascript
复制
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;
}

https://godbolt.org/z/e49h6d

对于nonzero_ptr,clang -O3 (所有版本)会生成以下或类似的代码:

代码语言:javascript
复制
    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,Clang3.6和更早版本会生成与nonzero_ptr相同代码,但Clang3.7到11.0.1会生成

代码语言:javascript
复制
    mov     eax, dword ptr [rdi + 4]
    or      eax, dword ptr [rdi]
    setne   al
    ret

它会无条件地加载y。当参数是指针时,任何版本的clang都不愿意这样做。为什么?

我能想到的(在x64平台上)分支代码行为明显不同的唯一情况是在[rdi+4]没有内存映射的情况下,但我仍然不确定为什么clang会认为这种情况对于指针而不是引用很重要。我最好的猜测是,有一些语言律师的论点,引用必须是“完整的对象”,而指针不必是:

代码语言:javascript
复制
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???

但如果规范中暗示了这一点,我就看不出怎么做了。

EN

回答 1

Stack Overflow用户

发布于 2021-02-21 13:43:35

我相信从标准C++的角度来看,编译器可以为两者生成相同的代码,因为标准中没有像您构建的那样的“部分对象”的规定。事实上,它不是一个简单的优化错过了。

可以比较像a->x || b->y这样的代码,编译器确实必须发出分支,因为只要a->x为非零,调用者就可以合法地为b传递空或无效的指针。另一方面,如果ab是引用,那么根据标准,a.x || b.y应该不需要分支,因为它们必须始终是对有效对象的引用。因此,nonzero_ptr中的“遗漏优化”可能只是编译器没有注意到它可以利用a->xa->y中的指针是同一个指针这一事实。

或者,clang作为一个扩展,可能会试图生成代码,当您使用非标准功能创建只有一些成员可以访问的对象时,这些代码仍然可以工作。事实上,这只适用于指针,而不适用于引用,这可能是该扩展的一个错误或限制,但我不认为这是任何类型的一致性违规。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66298438

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档