C++ 如果在没有RVO的情况下返回类/结构,会发生什么?

C++ 如果在没有RVO的情况下返回类/结构,会发生什么?,c++,assembly,x86,calling-convention,C++,Assembly,X86,Calling Convention,我理解RVO通常是可取的,但当它无法应用时会发生什么?具体来说,生成的汇编代码如何将本地类/结构实例返回给调用方 MyClass被调用方(){ 我的a级; /*RVO已禁用*/ 返回a; } void caller(){ /*RVO已禁用*/ MyClass b=否_rvo(); } 在这种情况下,堆栈是什么样子的?caller()和callee()是否分别为堆栈上的a和b分配空间,然后a复制到b?如果是这样,则callee()末尾的RET语句是否完全减少了指向它之前所在位置的堆栈指针,或者a

我理解RVO通常是可取的,但当它无法应用时会发生什么?具体来说,生成的汇编代码如何将本地类/结构实例返回给调用方

MyClass被调用方(){
我的a级;
/*RVO已禁用*/
返回a;
}
void caller(){
/*RVO已禁用*/
MyClass b=否_rvo();
}

在这种情况下,堆栈是什么样子的?
caller()
callee()
是否分别为堆栈上的
a
b
分配空间,然后
a
复制到
b
?如果是这样,则
callee()
末尾的RET语句是否完全减少了指向它之前所在位置的堆栈指针,或者
a
的堆栈内存是否在复制操作后释放?

这取决于调用约定。但所有x86 32位和64位调用约定都会做出相同的选择,并将指向返回值的指针作为“隐藏”的第一个参数传递

被调用方
仍然可以优化
a
(除非有其他方法阻止这种情况发生),并直接存储到返回值指针中

非平凡的可复制类型可能需要在某个时刻运行复制构造函数,并为
a
运行析构函数


但是re:您的实际问题是,启用优化后,调用者将传递
&b
作为返回值指针

在禁用优化的情况下,我想我已经看到一些编译器为一个单独的临时返回值创建空间,然后从那里复制,这对于可复制的类型来说是非常多余的

理论上,返回值指针可以指向堆栈内存以外的其他位置,例如,如果分配给
静态MyClass arr[10]
的元素


但是,它不能指出任何东西,代码> CALLY//Cord>可以访问任何其他方式,因为在C++抽象机中,返回值是一个独立的对象,在函数返回之前,没有任何东西可以指向。使用

\uuuu属性(noinline,noclone))
这样您就可以在启用优化的情况下编译,并且仍然可以看到函数调用。如果我开始讨论这个问题,我可能会在我的答案中添加一个例子。另外,如果返回值是一个只包含寄存器大小的整数(或更小)IIRC的结构,那么它只是放在x86_64上的rax中。@MatteoItalia:哦,是的,在x86-64系统V中,最多16个字节的小结构返回到RDX:rax中。或者如果它只是FP变量,然后压缩到xmm1:xmm0的每个低8字节中。或完整xmm0中的
\uuu m128
。细节会因打电话到那里而有所不同。我忘记了非平凡的可复制对象会发生什么,以及它们是否得到特殊处理。最后一句话很有趣。IIUC您是说返回值指针不能指向,比如说,全局变量,因为
被调用方
无法知道它们彼此的别名?但是,如果编译器能够分析被调用方的定义,并发现在修改返回值之前没有使用/读取该值,该怎么办?@Acorn:哦,是的,IPA(过程间优化)可以允许许多通常不安全的事情,例如,依赖于函数不会实际破坏易失性寄存器。ABI/调用约定必须在没有该约定的情况下工作,编译器在库中调用函数时也必须这样做。(或在没有链接时间优化的情况下,其他翻译单元中的任何函数)。但是是的,你对问题的理解是正确的;那将是那种会破裂的东西。