Почему GCC создает дополнительные инструкции по сборке на моем компьютере? - PullRequest
0 голосов
/ 05 октября 2019

Прошло много времени с тех пор, как я начал работать с внутренними функциями SSE / AVX. Недавно я начал писать заголовок для транспонирования матрицы. Я использовал много веток if constexpr, так что компилятор всегда выбирает оптимальный набор команд в зависимости от некоторых параметров шаблона. Теперь я хотел проверить, все ли работает как положено, посмотрев на локальную разборку с objdump. При использовании Clang я получаю четкий вывод, который в основном содержит только инструкции по сборке, соответствующие используемым встроенным функциям. Однако, если я использую GCC, разборка довольно раздута с дополнительными инструкциями. Быстрая проверка Godbolt показывает мне, что этих дополнительных инструкций в разборке GCC не должно быть.

Вот небольшой пример:

#include <x86intrin.h>
#include <array>

std::array<__m256, 1> Test(std::array<__m256, 1> a)
{
    std::array<__m256, 1> b;

    b[0] = _mm256_unpacklo_ps(a[0], a[0]);
    return b;
}

Я компилирую с -march=native -Wall -Wextra -Wpedantic -pthread -O3 -DNDEBUG -std=gnu++1z. Затем я использую objdump -S -Mintel libassembly.a > libassembly.dump в объектном файле. Для Clang (6.0.0) результат будет:

In archive libassembly.a:

libAssembly.cpp.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_Z4TestSt5arrayIDv8_fLm1EE>:
   0:   c4 e3 7d 04 c0 50       vpermilps ymm0,ymm0,0x50
   6:   c3                      ret    

, который совпадает с возвращением Godbolt: Godbolt - Clang 6.0.0

Для GCC (7.4) вывод:

In archive libassembly.a:

libAssembly.cpp.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_Z4TestSt5arrayIDv8_fLm1EE>:
   0:   4c 8d 54 24 08          lea    r10,[rsp+0x8]
   5:   48 83 e4 e0             and    rsp,0xffffffffffffffe0
   9:   c5 fc 14 c0             vunpcklps ymm0,ymm0,ymm0
   d:   41 ff 72 f8             push   QWORD PTR [r10-0x8]
  11:   55                      push   rbp
  12:   48 89 e5                mov    rbp,rsp
  15:   41 52                   push   r10
  17:   48 83 ec 28             sub    rsp,0x28
  1b:   64 48 8b 04 25 28 00    mov    rax,QWORD PTR fs:0x28
  22:   00 00 
  24:   48 89 45 e8             mov    QWORD PTR [rbp-0x18],rax
  28:   31 c0                   xor    eax,eax
  2a:   48 8b 45 e8             mov    rax,QWORD PTR [rbp-0x18]
  2e:   64 48 33 04 25 28 00    xor    rax,QWORD PTR fs:0x28
  35:   00 00 
  37:   75 0c                   jne    45 <_Z4TestSt5arrayIDv8_fLm1EE+0x45>
  39:   48 83 c4 28             add    rsp,0x28
  3d:   41 5a                   pop    r10
  3f:   5d                      pop    rbp
  40:   49 8d 62 f8             lea    rsp,[r10-0x8]
  44:   c3                      ret    
  45:   c5 f8 77                vzeroupper 
  48:   e8 00 00 00 00          call   4d <_Z4TestSt5arrayIDv8_fLm1EE+0x4d>

Как видите, существует множество дополнительных инструкций. В отличие от этого, Godbolt не включает в себя все эти дополнительные инструкции: Godbolt - GCC 7.4

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

Приветствую и заранее благодарю.

РЕДАКТИРОВАТЬ

Чтобы избежать дальнейших недоразумений, я только что скомпилировал, используя:

gcc-7 -I/usr/local/include -O3 -march=native -Wall -Wextra -Wpedantic -pthread -std=gnu++1z -o test.o -c /<PathToFolder>/libAssembly.cpp

Вывод остается прежним. Я не уверен, что это уместно, но выдает предупреждение: warning: ignoring attributes on template argument ‘__m256 {aka __vector(8) float}’ [-Wignored-attributes]

Обычно я пересекаю это предупреждение, и оно не должно быть проблемой:

Значение GCCпредупреждение: игнорирование атрибутов в аргументе шаблона (-Wignored-attribute)

Процессор Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz

Вот gcc -v:

gcc-7 -v
Using built-in specs.
COLLECT_GCC=gcc-7
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.4.0-1ubuntu1~18.04.1' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1) 

1 Ответ

8 голосов
/ 05 октября 2019

Использование -fno-stack-protector


Ваш локальный GCC по умолчанию установлен на -fstack-protector-strong, но установка GCC Годболта - нет.

mov rax,QWORD PTR fs:0x28 - контрольная подсказка ;Локальное хранилище в потоке fs:40 aka fs:0x28 - это место, где GCC поддерживает постоянный размер файла cookie стека. call после ret равно call __stack_chk_fail (но вы разбирали .o без использования objdump -dr для отображения перемещений, поэтому смещение заполнителя +0 просто выглядело как цель в этой функции).

Так как у вас есть массивы (или класс, содержащий массив), сильный защитник стека начинает работать, даже если их размеры являются константами времени компиляции. Таким образом, вы получаете код для хранения стека cookie, затем проверяете его и переходите на переполнение стека. (Даже массива размера 1 в этом MVCE достаточно, чтобы вызвать это.)

Создание массивов в стеке с 32-байтовым выравниванием (для __m256) требует 32-байтового выравнивания, и ваш GCC старшечем GCC8, так что вы получите смехотворно неуклюжий код выравнивания стека, который создает полную копию кадра стека, включая адрес возврата. Сгенерированная сборка для расширенного выравнивания переменных стека (Для ясности, GCC8 все еще выравнивает стек здесь, просто тратя на него меньше инструкций.)

Это в значительной степени пропущенная оптимизация;На самом деле gcc никогда не разливает и не перезагружает эти массивы, поэтому он мог бы просто оптимизировать их вместе с выравниванием стека, как это было без защитника стека.

Более поздние GCC лучше оптимизируют выравнивание стека после оптимизациив большинстве случаев отводите память для выровненных локальных объектов, но это была постоянная пропущенная оптимизация в коде AVX. К счастью, затраты на функцию, которая зацикливается, довольно незначительны;до тех пор, пока небольшие вспомогательные функции встроены.


Компиляция на Godbolt с -fstack-protector-strong воспроизводит ваш вывод. Более новый GCC, включая текущий транк pre-10,и в том, и в другом случае пропущены оптимизации, но выравнивание стека стоит меньше инструкций, поскольку оно просто использует RBP в качестве указателя кадра и выравнивает RSP, а затем ссылается на локальные объекты относительно выровненного RSP. Он по-прежнему проверяет файлы cookie стека (без каких-либо инструкций между их сохранением и проверкой).

На рабочем столе компиляция с -fno-stack-protector должна привести к хорошему асму.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...