Почему clang не исключает копию в этой функции с типом автоматического возврата?C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Почему clang не исключает копию в этой функции с типом автоматического возврата?

Сообщение Anonymous »

Я обнаружил случай, когда clang 8.x не удаляет копию шаблонного объекта класса, с которым у gcc и msvc нет проблем. В моем реальном приложении эта лишняя копия стоит довольно дорого, поэтому я пытаюсь разобраться в этом и в конечном итоге лучше понять, когда исключение копирования выполняется, а когда не выполняется в C++17.

Проблема показана в фрагменте кода ниже. Функция, объявленная с типом автоматического возврата, которая возвращает именованный объект класса, имеет в своем теле дополнительную конструкцию копирования. Если возврат перекодируется для возврата безымянного временного объекта, происходит исключение. Если функция перекодируется так, чтобы явно возвращать экземпляр класса (вместо auto), происходит исключение.

Если структура A не имеет параметра шаблона, то также генерируется полностью исключенный код.

Проблема показывает, все ли является noException или разрешено ли встраивание (NOINLINE позволяет увидеть проблему в Godbolt без необходимости выполнения кода).

Код: Выделить всё

// compiled with -O2 -std=c++17
#if defined(_MSC_VER) && !defined(__clang__)
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE __attribute__((noinline))
#endif

template
struct A {
int data = 0;
NOINLINE explicit A(int data_) noexcept : data(data_) { }
NOINLINE ~A() noexcept { }
NOINLINE A(const A& other) noexcept : data(other.data) { }
};

template 
NOINLINE auto return_auto_A_nrvo(const A& a) noexcept {
/* clang 6.0 thru 8.0 doesn't elide copy of 'result':
gcc and msvc elide the copy as expected.
mov     r14, rsp
mov     rdi, r14
call    A::A(A const&)
mov     rdi, rbx
mov     rsi, r14
call    A::A(A const&)
mov     rdi, r14
call    A::~A() [base object destructor]

* return A(a); is fully optimized
*/
A result(a);
return result;
}

template 
NOINLINE A return_A_nrvo(const A& a) noexcept {
// NRVO with explicit return type: fully optimized
A result(a);
return result;
}

template 
NOINLINE auto return_auto_A_rvo(const A& a) noexcept {
// RVO: fully optimized
return A(a);
}

NOINLINE int main() {
auto a1 = A(42);
auto a2 = return_auto_A_nrvo(a1);
auto a3 = return_A_nrvo(a1);
auto a4 = return_auto_A_rvo(a1);

return a2.data + a3.data + a4.data;
}
Комментарии в функции return_auto_A_nrvo() показывают код, сгенерированный clang с незачеркнутой копией. Все остальные варианты генерируют полностью исключенный код. Копия также опускается, если класс A не имеет параметров шаблона.

Эта ссылка Godbolt показывает код, сгенерированный GCC, clang и msvc: https://www.godbolt.org/z/FDAvQO.

Возможно, это просто ошибка/упущенная возможность оптимизации, которую clang упускает, а бренды G и M - нет. Если это так, я постараюсь найти подходящее место, чтобы опубликовать это, чтобы ребята из clang могли это исправить. Но я чувствую, что здесь может быть что-то более глубокое, например, фундаментальное различие между возвратом auto и возвратом объекта шаблонного класса. Я считаю, что C++17 гарантирует, что unnamed-RVO всегда будет возникать, но использование именованного-RVO, как в моем случае, не гарантировано - я хотел бы понять, почему это так (и почему это применимо здесь).

Подробнее здесь: https://stackoverflow.com/questions/562 ... eturn-type
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»