ARM С++ ABI: значения возврата конструктора/деструктора - программирование
Подтвердить что ты не робот

ARM С++ ABI: значения возврата конструктора/деструктора

Я читал через исходный код Clang и обнаружил что-то интересное в ARM С++ ABI, которое я, похоже, не понимаю для обоснования. Из онлайн-версии документации ARM ABI:

Этот ABI требует, чтобы конструкторы C1 и C2 возвращали это (вместо функций void), так что конструктор C3 может tail вызвать конструктор C1, а конструктор C1 может вызвать вызов C2.

(и аналогично для не виртуальных деструкторов)

Я не уверен, что здесь C1, C2 и C3 здесь... этот раздел предназначен для модификации п. 3.1.5 из общей (то есть Itanium) ABI, но это (по крайней мере, в это онлайн-версия) просто говорится:

Конструкторы возвращают результаты void.

В любом случае, я действительно не могу понять, какова цель этого: как сделать возврат конструктора, это позволяет оптимизировать хвостовой вызов и при каких обстоятельствах?

Насколько я могу судить, единственный раз, когда конструктор мог бы вызывать другого с тем же возвратным значением this, был бы случайным классом с одним базовым классом, тривиальным куском конструктора, без членов с не- тривиальные конструкторы и указатель виртуальной таблицы. На самом деле кажется, что было бы проще, а не сложнее, оптимизировать с помощью хвостового вызова с возвратом void, потому что ограничение одного базового класса можно было бы исключить (в случае с несколькими базовыми классами this, возвращаемый из последнего вызываемого конструктора, не будет указателем this производного объекта).

Что мне здесь не хватает? Есть ли что-то в соглашении вызова ARM, которое делает возврат this необходимым?

4b9b3361

Ответ 1

Хорошо, полезная ссылка от @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/ "complete" - это версия конструктора, которая вместо работы с уже распределенным хранилищем, переданным ему через параметр this, выделяет внутреннюю память (через operator new), а затем вызывает конструктор объектов C1/ "complete", который является обычным конструктором, используемым для всего объекта объекта. Поскольку конструктор C3 должен возвращать указатель this к вновь выделенному и сконструированному объекту, конструктор C1 должен также возвращать указатель this для использования хвостового вызова.

Конструктор базового объекта C2/"- это конструктор, вызываемый производными классами при построении подобъекта базового класса; семантика конструкторов C1 и C2 различается в случае наследования virtual и может быть реализована по-разному для целей оптимизации. В случае наследования virtual конструктор C1 может быть реализован с вызовами конструкторов базового класса virtual, за которыми следует хвостовой вызов конструктора C2, поэтому последний должен также возвращать this, если прежний делает.

Случай деструктора немного отличается, но связан. Согласно ARM ABI:

Аналогично, мы требуем, чтобы D2 и D1 возвращали это так, чтобы D0 не нуждался в сохранении и восстановлении, а D1 может хвост D2 (если нет виртуальных оснований). D0 по-прежнему является функцией пустоты.

При удалении объекта используется D0/ "удаление деструктора", он вызывает дескриптор объекта D1/ "complete object" и вызывает operator delete с указателем this, чтобы освободить память. Наличие D1 деструктора return this позволяет деструктору D0 использовать его возвращаемое значение для вызова operator delete, вместо того, чтобы сохранять его в другом регистре или проливать его на память; аналогичным образом, деструктор базового объекта D2/"должен также возвращать this.

ARM ABI также добавляет:

Мы не нуждаемся в том, чтобы виртуальные деструкторы разворачивали это. Такой удар должен был бы отрегулировать деструкторы результат, предотвращая его от хвоста, вызывающего деструктор, и сводит на нет любую возможную экономию.

Следовательно, для возврата этого могут быть использованы только не виртуальные вызовы деструкторов D1 и D2.

Если я правильно понимаю это, это означает, что оптимизация save-restore-elision может использоваться только тогда, когда D0 вызывает D1 статически (т.е. в случае деструктора не virtual).