我一直在阅读Clang源代码并发现了一些关于ARM C ++ ABI的有趣内容,我似乎无法理解其中的理由。来自ARM ABI documentation的在线版本:
这个ABI要求C1和C2构造函数返回 this (而不是void函数),以便C3构造函数 可以尾调用C1构造函数,C1构造函数可以尾调用C2。
(对于非虚拟析构函数也类似)
我不确定C1
,C2
和C3
在这里引用了什么......这一部分是对通用的§3.1.5的修改(即Itanium)ABI,但该部分(至少在this online verison中)只是声明:
构造函数返回 void 结果。
无论如何,我真的无法弄清楚这是什么目的:如何使构造函数返回这个允许尾调用优化,以及在什么情况下?
据我所知,构造函数可以尾部调用具有相同this
返回值的另一个的唯一时间是具有单个基类的派生类,一个简单的构造函数体,没有成员的情况非平凡的构造函数,没有虚拟表指针。实际上,使用void
返回来优化尾部调用似乎实际上更容易,而不是更难,因为这样可以消除单个基类的限制(在多基类的情况下,从最后一个被调用的构造函数返回的this
指针不是派生对象的this
指针。)
我在这里缺少什么? ARM调用约定是否有必要使this
返回?
答案 0 :(得分:9)
好的,来自@Michael的有用链接使这一切清楚...... C1
,C2
和C3
引用了“完整对象构造函数”的名称修改,“基础对象构造函数“和”完整对象分配构造函数“,分别来自Itanium ABI:
<ctor-dtor-name> ::= C1 # complete object constructor
::= C2 # base object constructor
::= C3 # complete object allocating constructor
::= D0 # deleting destructor
::= D1 # complete object destructor
::= D2 # base object destructor
C3
/“完整对象分配构造函数”是构造函数的一个版本,它不是在通过this
参数传递给它的已经分配的存储上运行,而是在内部分配内存(通过{{ 1}})然后调用operator new
/“完整对象构造函数”,这是用于完整对象案例的常规构造函数。由于C1
构造函数必须将C3
指针返回到新分配和构造的对象,因此this
构造函数还必须返回C1
指针,以便尾部调用为使用
this
/“基础对象构造函数”是构造基类子对象时由派生类调用的构造函数; C2
和C1
构造函数的语义在C2
继承的情况下有所不同,并且为了优化目的也可以以不同方式实现。在virtual
继承的情况下,可以通过调用virtual
基类构造函数,然后对C1
构造函数进行尾调用来实现virtual
构造函数,因此后者应该如果是前者,也会返回C2
。
析构函数情况略有不同但相关。根据ARM ABI:
同样,我们要求D2和D1返回此值,以便D0不需要保存和恢复此,D1可以尾调用D2(如果没有虚拟碱基)。 D0仍然是一个无效函数。
删除对象时使用this
/“删除析构函数”,它调用D0
/“完整对象析构函数”并使用D1
指针调用operator delete
之后释放记忆。让this
析构函数返回D1
允许this
析构函数使用其返回值来调用D0
,而不必将其保存到另一个寄存器或将其溢出到内存中;同样,operator delete
/“基础对象析构函数”也应该返回D2
。
ARM ABI还补充说:
我们不需要thunks到虚拟析构函数来返回 this 。这样的thunk必须调整析构函数 结果,阻止它尾部调用析构函数,并使任何可能的保存无效。
因此,只能依赖D1和D2析构函数的非虚拟调用来返回 this 。
如果我理解正确,则表示只有在this
静态调用D0
时才会使用此保存 - 恢复 - 省略优化(即非D1
的情况析)。