为什么noreturn/__builtin_unreachable会防止尾调用优化。

5
我发现所有主流的编译器在被调用的函数没有返回值(即标记为_Noreturn / [[noreturn]]或在调用后有__builtin_unreachable())时都不会进行尾调用优化。这是故意的行为,还是错失的优化机会?如果是故意的,请问原因是什么?
#ifndef __cplusplus
#define NORETURN _Noreturn
#else
#define NORETURN [[noreturn]]
#endif

void canret(void);
NORETURN void noret(void);

void foo(void) { canret(); }
void bar(void) { noret(); }

C: https://godbolt.org/z/pJfEe- C++: https://godbolt.org/z/-4c78K

示例2:

#ifdef _MSC_VER
#define UNREACHABLE __assume(0)
#else
#define UNREACHABLE __builtin_unreachable()
#endif

void f(void);

void foo(void) { f(); }
void bar(void) { f(); UNREACHABLE; }

https://godbolt.org/z/PFhWKR


1
你能描述一个这很重要的情况吗?是关于性能还是代码大小? - Marc Glisse
3
我正在调查 boost::variant 切换访问代码膨胀的问题,看到了这个问题 https://godbolt.org/z/c__MxS。如果在某处有 noreturn 调用,Clang 会出现问题并在每个分支中都做堆栈调整。https://godbolt.org/z/PR6RMn 如果 GCC 看到 abort 命令也会发生同样的情况。 - Nikita Kniazev
1个回答

10

虽然有可能会严重损害堆栈使用属性,但这是故意的,也许有些争议。因此,我甚至采用了欺骗编译器以认为一个无法返回的函数可以返回的技巧。我的想法是,许多无法返回的函数类似于abort(甚至调用abort),而且很可能运行调试器的人希望能够看到调用发生的位置——这是通过尾递归将丢失的信息。

引用:


1
如果有人想要监视'abort',那么他们也很可能会禁用优化吧?在优化的构建中为了可追溯性而禁用优化似乎是适得其反的。要求进行优化通常意味着您不会尝试调试。或者至少表明您认为优化比可追溯性更重要。 - François Andrieux
2
这很有道理。这是基于事实的信息还是基于观点的? - Eugene Sh.
1
我也认为可能是由于abort/std::terminate, 但是针对每个noreturn函数调用禁用尾递归优化太过不合理。 - Nikita Kniazev
1
你能提供一些证明这个想法的支持吗?因为我也觉得为了支持潜在的 abort() 调用而禁用优化的想法不太合理。 - SergeyA
非常感谢。谢谢分享。 - SergeyA
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接