GCC为什么要在我的机器上创建额外的组装说明?

问题描述 投票:0回答:1

自从我开始使用SSE / AVX内在函数以来已经有一段时间了。我最近开始为矩阵转置编写标题。我使用了很多if constexpr分支,以便编译器始终根据某些模板参数选择最佳指令集。现在,我想通过使用objdump查看本地反汇编来检查是否一切正常。使用Clang时,我得到一个清晰的输出,该输出基本上只包含与所使用的内在函数相对应的汇编指令。但是,如果我使用GCC,则在使用其他说明时反汇编会很is肿。快速检查一下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]

通常,我会取消此警告,这应该不是问题:

Implication of GCC warning: ignoring attributes on template argument (-Wignored-attributes)

处理器是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) 

解决方案:

修复程序集的解决方案是添加编译标志-fno-stack-protector,如Peter Cordes answer所述。问题是std::array(大小为1 ?????? ---必须测试其他大小是否也受到影响),在注释中也被Useless怀疑。

但是,我使用GCC 8.3进行了dist-upgrade到Ubuntu 19.04的升级,如果未设置该标志,问题仍然存在。我想知道以后的GCC版本是否解决了这个问题,或者我是否有导致此问题的本地设置问题。

c++ gcc intrinsics avx
1个回答
6
投票

您的本地GCC默认为-fstack-protector-strong,但Godbolt的GCC安装没有。 mov rax,QWORD PTR fs:0x28是告白的线索; fs:0x28是GCC在线程本地存储中将其堆栈cookie保持不变的位置。 call之后的retcall __stack_chk_fail(但是您在不使用.o来显示重定位的情况下分解了objdump -dr,因此占位符+0偏移看起来仍然是此函数中的目标)。

由于您具有数组(大小为1,没有明显的原因),即使堆栈大小是编译时常量,堆栈保护器也会强行插入。这样就得到了存储堆栈cookie的代码,然后对其进行检查并在堆栈溢出时分支。

在堆栈上以32字节对齐(对于__m256使用数组)需要32字节对齐,并且您的GCC早于GCC8,因此您获得了笨拙的堆栈对齐代码,该代码构建了堆栈的完整副本包含返回地址的框架。 Generated assembly for extended alignment of stack variables

这几乎是一个错过的优化; gcc从未真正溢出或重新加载到那些阵列,因此它可以像优化堆栈一样将其优化,以及不使用堆栈保护器就可以进行堆栈对齐。

最近的GCC在更多情况下为对齐的局部变量优化了内存之后,更擅长优化堆栈的对齐方式,但这一直是AVX代码中永久遗漏的优化方法。幸运的是,在一个循环函数中,成本几乎可以忽略不计。只要小助手可以内联即可。


[Compiling on Godbolt-fstack-protector-strong一起再现您的输出。较新的GCC仍然缺少优化,但是堆栈对齐的指令较少,因为它仅使用RBP作为帧指针并对齐RSP,然后引用相对于已对齐的局部变量RSP。它仍会检查堆栈cookie(在存储和检查之间没有任何说明)。

在您的桌面上,使用-fno-stack-protector进行编译应该很好。

© www.soinside.com 2019 - 2024. All rights reserved.