17

我一直在阅读 Clang 源代码,并发现了一些关于 ARM C++ ABI 的有趣之处,我似乎无法理解其理由。来自ARM ABI 文档的在线版本:

此 ABI 要求 C1 和 C2 构造函数返回this(而不是 void 函数),以便 C3 构造函数可以尾调用 C1 构造函数,而 C1 构造函数可以尾调用 C2。

(对于非虚拟析构函数也是如此)

我不确定这里是什么C1、、C2C3参考……本节是对通用(即安腾)ABI 的 §3.1.5 的修改,但该节(至少在此在线版本中)只是说明:

构造函数返回void结果。

无论如何,我真的不知道这样做的目的是什么:如何让构造函数返回this允许尾调用优化,以及在什么情况下?

据我所知,唯一一次构造函数可以尾调用另一个具有相同this返回值的情况是派生类具有单个基类、普通构造函数体、没有具有非普通构造函数的成员和没有虚拟的情况表指针。实际上,使用带有返回的尾调用进行优化似乎更容易,而不是更难,void因为这样可以消除单个基类的限制(在多个基类的情况下,this从最后调用的构造函数不会是this派生对象的指针)。

我在这里想念什么?ARM 调用约定是否有this必要进行返回?

4

1 回答 1

11

C1好的,来自@Michael 的有用链接使这一切都变得清晰C2...... C3ABI:

  <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内部(通过operator new)分配内存,然后调用C1/“完整对象构造函数”,即用于完整对象情况的普通构造函数。由于C3构造函数必须返回this指向新分配和构造的对象C1的指针,因此构造函数还必须返回this指针才能使用尾调用。

/ “C2基类构造函数”是派生类在构造基类子对象时调用的构造函数;C1C2构造函数的语义在virtual继承的情况下是不同的,并且为了优化目的也可以以不同的方式实现。在virtual继承的情况下,C1构造函数可以通过调用virtual基类构造函数然后尾调用C2构造函数来实现,因此如果前者调用,后者也应该返回this

析构函数的情况略有不同但相关。根据 ARM ABI:

类似地,我们要求 D2 和 D1 返回这个,这样 D0 就不需要保存和恢复这个,并且 D1 可以尾调用 D2(如果没有虚拟碱基)。D0 仍然是一个 void 函数。

/ “ D0deleting destructor”在删除对象时使用,它调用D1/“complete object destructor”,然后operator deletethis指针调用以释放内存。让D1析构函数返回this允许D0析构函数使用其返回值来调用operator delete,而不必将其保存到另一个寄存器或将其溢出到内存中;同样,D2/"base object destructor" 也应该返回this

ARM ABI 还添加了:

我们不需要对虚拟析构函数的 thunk 来返回this。这样的重击必须调整析构函数的结果,防止它尾调用析构函数,并取消任何可能的保存。

因此,只能依靠 D1 和 D2 析构函数的非虚拟调用来返回this

如果我理解正确,这意味着这种保存-恢复-省略优化只能在静态D0调用时使用D1(即在非virtual析构函数的情况下)。

于 2013-03-16T19:43:36.037 回答