首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么GCC要在我的机器上创建额外的装配指令?

为什么GCC要在我的机器上创建额外的装配指令?
EN

Stack Overflow用户
提问于 2019-10-05 12:40:03
回答 1查看 321关注 0票数 0

自从我开始使用SSE/AVX内部函数以来,已经有一段时间了。最近,我开始为矩阵转换编写一个标题。我使用了大量的if constexpr分支,因此编译器总是根据某些模板参数选择最优的指令集。现在,我想通过查看使用objdump的本地反汇编来检查是否一切都像预期的那样工作。在使用Clang时,我得到了一个清晰的输出,它基本上只包含与所使用的内在函数相对应的装配指令。然而,如果我使用GCC,拆卸是相当臃肿的额外指示。快速检查一下哥德波特,我就知道GCC拆卸中的额外指令不应该在那里。

下面是一个小例子:

代码语言:javascript
复制
#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),其结果是:

代码语言:javascript
复制
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    

这与戈德波特返回的内容相同:哥德波特- Clang 6.0.0

GCC (7.4)的产出是

代码语言:javascript
复制
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>

正如你所看到的,有很多额外的指令。与此形成对比的是,戈德波特并没有包括所有这些额外的指令:天栓- 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警告的含义:忽略模板参数上的属性(-忽略-属性)

处理器是Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz

这是gcc -v

代码语言:javascript
复制
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) 
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-10-05 17:07:12

-fno-stack-protector 使用

你当地的GCC默认为-fstack-protector-strong,但的GCC安装没有。

mov rax,QWORD PTR fs:0x28 是告密线索;fs:40 aka fs:0x28的线程本地存储是GCC保持堆栈cookie不变的地方。callret之后是call __stack_chk_fail (但是您不使用objdump -dr来显示重定位,就可以分解.o,所以占位符+0偏移量看起来仍然是这个函数中的一个目标)。

由于有数组(或包含数组的类),即使它们的大小是编译时常量,堆栈保护程序也会启动。因此,您可以获得存储堆栈cookie的代码,然后检查它并在堆栈溢出时分支。(即使这个MVCE中的大小为1的数组也足以触发该数组。)

在堆栈上使用32字节对齐(用于__m256)的数组需要32字节对齐,而GCC比GCC8更老,所以您将得到一个异常笨重的堆栈对齐代码,该代码构建堆栈帧的完整副本,包括一个返回地址。用于堆栈变量扩展对齐的生成程序集 (明确地说,GCC8仍然在这里对齐堆栈,只是在上面浪费了更少的指令)。

这几乎是一个错过了优化;gcc从来没有真正溢出或重新加载到这些数组,所以它可以只是优化了他们,以及堆栈对齐,就像它没有堆栈保护器。

最近GCC在更多情况下优化了对齐局部变量的内存后,更好地优化了对置堆栈对齐,但这是AVX代码中一个持久的遗漏优化。幸运的是,在循环函数中,成本可以忽略不计;只要小助手函数是内联的。

带有-fstack-protector-strong 关于地脚螺栓的编制 关于地脚螺栓的编制会复制您的输出。较新的GCC (包括当前主干道10)仍然没有进行优化,但堆栈对齐所需的指令较少,因为它只使用RBP作为帧指针并对RSP对齐,然后引用相对于对齐的RSP的局部变量。它仍然检查堆栈cookie (在存储它和检查它之间没有指令)。

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

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58248391

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档