我一直在探索这个问题,提交了一份GCC错误报告,发现这是一个与MinGW64相关的问题。见GCC Bug#49001。显然,GCC在Windows上不支持32字节的堆栈对齐。这有效地防止使用256位AVX指令。
我调查了几种方法如何处理这个问题。最简单和最令人满意的解决方案是通过未对齐的替代品VMOVUPS等替换对齐的内存访问VMOVAPS/PD/DQA。所以我昨晚学会了Python(顺便说一句,非常好的工具),并拿掉了下面的脚本来完成这项工作输入汇编文件由GCC生成:
import re
import fileinput
import sys
# fix aligned stack access
# replace aligned vmov* by unaligned vmov* with 32-byte aligned operands
# see Intel's AVX programming guide, page 39
vmova = re.compile(r"\s*?vmov(\w+).*?((\(%r.*?%ymm)|(%ymm.*?\(%r))")
aligndict = {"aps" : "ups", "apd" : "upd", "dqa" : "dqu"};
for line in fileinput.FileInput(sys.argv[1:],inplace=1):
m = vmova.match(line)
if m and m.group(1) in aligndict:
s = m.group(1)
print line.replace("vmov"+s, "vmov"+aligndict[s]),
else:
print line,
这种方法非常安全和万无一失。尽管我在罕见场合观察到了表演处罚。当堆栈未对齐时,内存访问跨越缓存线边界。幸运的是,代码的执行速度与大部分时间对齐访问一样快。我的建议:关键循环中的内联函数!
我还尝试使用另一个Python脚本修复每个函数prolog中的堆栈分配,尝试将其始终与32字节的边界对齐。这似乎适用于某些代码,但不适用于其他代码。我必须依靠GCC的良好意愿,它将分配对齐的局部变量(关于堆栈指针),通常它会这样做。情况并非总是如此,特别是当由于必须在函数调用之前保存所有ymm寄存器而导致严重的寄存器溢出时。 (所有的ymm寄存器都是被调用者保存的)。如果有兴趣,我可以发布脚本。
最好的解决方案是修复GCC MinGW64的构建。不幸的是,我不了解其内部工作原理,上周刚开始使用它。
这是否意味着'__attribute__((aligned(32)))'不被尊重呢?例如如果使用'__m256 x __attribute__((aligned(32)))' – nos 2014-06-05 12:57:38
Linux不会将堆栈对齐32。针对Linux的gcc使用'和$ -32,%rsp'(或任何更高的对齐方式)将堆栈对齐到需要溢出'__m256','__m512'或用'alignas(32)声明的任何对象的函数中'或者高于16的任何东西。这似乎是一个奇怪的bug,MinGW gcc不使用相同的序列来保存原始的'rsp'并对齐它。 – 2017-11-07 15:06:25