【发布时间】:2021-07-20 15:59:18
【问题描述】:
目前,在 C++ 编译器中,尾调用优化的规则之一是返回类型必须是可简单破坏的。 (基于分析 GCC、Clang 中继行为。MSVC 对任何非平凡类型都有问题)。
这个要求还有必要吗?由于 C++17 返回值优化是强制性的,因此该函数似乎仍然可以使用跟踪调用优化,即使返回类型很重要。这里有什么问题,阻止编译器这样做?
@edit,代码示例:
#include <string>
bool h();
std::string g() {
std::string s1 = "a", s2 = "b";
if (h()) return s1;
else return s2;
}
std::string f() {
return g(); // <= here I'd expect call-tail optimization due to RVO, since it is prvalue
}
https://godbolt.org/z/YYfMr6xdd
如果我对程序集的理解正确,应该可以将 f() 函数替换为 jump。
【问题讨论】:
-
如果 我 理解正确
f可以替换为g(内联)(我猜非强制性)。而g没有 NRVO(或 RVO),因为它有多个返回路径。 -
gcc 和 clang 都保存和恢复 $rdi -> $rax。也许他们不认为
g正确设置了 $rax? -
这与 trivially-destructible 没有直接关系,而是与 ABI 是否允许通过寄存器有关。如果类型可以简单地破坏但足够大,则尾调用也没有优化:godbolt.org/z/TbrM74j7b
-
@dyp,是的,这是我第一次听到与 (N)RVO 相关的琐碎破坏。我认为不平凡不是阻止 (N)RVO 的借口。隐含的想法是,如果构造首先被省略,则省略的破坏没有副作用或不重要。
-
我几乎可以肯定,在寄存器中传递这些值取决于类型的微不足道的可破坏性。 Clang [[clang::musttail]] 属性返回一个错误,该类型必须是可简单破坏的,并且在许多可简单重定位/位复制可移动的提案中使用相同的措辞。经过一些研究,我在 Herb Sutter/Niall Douglas 的谈话中听到了同样的话,如果我理解正确的话,这种小值 ABI 的低效率是由非平凡的析构函数引起的。
标签: c++ c++17 tail-call-optimization return-value-optimization