我正在使用基于GCC 4.6.1的MinGW64构建Windows 64bit目标。我正在玩新的英特尔AVX指令。我的命令行参数是-march=corei7-avx -mtune=corei7-avx -mavx
。
但是当我在堆栈上分配局部变量时,我开始遇到分段错误错误。 GCC使用对齐的移动VMOVAPS
和VMOVAPD
来移动__m256
和__m256d
,这些指令需要32字节对齐。但是,Windows 64bit的堆栈只有16字节对齐。
如何将GCC的堆栈对齐更改为32个字节?
我尝试使用-mstackrealign
但无济于事,因为它只对齐16个字节。我也无法使__attribute__((force_align_arg_pointer))
工作,无论如何它都与16个字节对齐。我无法找到任何其他可以解决此问题的编译器选项。非常感谢任何帮助。
编辑:
我尝试使用-mpreferred-stack-boundary=5
,但GCC表示此目标不支持5。我没有想法。
答案 0 :(得分:15)
我一直在探索这个问题,提交了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版本。不幸的是,我不知道它的内部工作,上周就开始使用它了。
答案 1 :(得分:1)
你可以通过
获得你想要的效果当malloc()没有适当地对齐堆上的东西时,你可以使用相同的技术。
E.g。
void foo() {
struct I_wish_these_were_32B_aligned {
vec32B foo;
char bar[32];
}; // not - no variable definition, just the struct declaration.
unsigned char a[sizeof(I_wish_these_were_32B_aligned) + 32)];
unsigned char* a_aligned_to_32B = align_to_32B(a);
I_wish_these_were_32B_aligned* s = (I_wish_these_were_32B_aligned)a_aligned_to_32B;
s->foo = ...
}
,其中
unsigned char* align_to_32B(unsiged char* a) {
uint64_t u = (unit64_t)a;
mask_aligned32B = (1 << 5) - 1;
if (u & mask_aligned32B == 0) return (unsigned char*)u;
return (unsigned char*)((u|mask_aligned_32B) + 1);
}
答案 2 :(得分:1)
在我的函数中使用AVX时,我遇到了同样的问题。这也是由于堆栈错位。鉴于这是一个编译器问题(以及可能有帮助的选项在Windows中不可用),我通过以下方式解决了堆栈使用问题:
使用静态变量(请参阅此issue)。鉴于它们未存储在堆栈中,您可以在声明中使用__attribute__((align(32)))
强制对齐它们。例如:static __m256i r __attribute__((aligned(32)))
。
内联接收/返回AVX数据的功能/方法。您可以通过将inline
和__attribute__((always_inline))
添加到函数原型/声明中来强制GCC内联您的函数/方法。内联函数会增加程序的大小,但它们也会阻止函数使用堆栈(因此,避免了堆栈对齐问题)。示例:inline __m256i myAvxFunction(void) __attribute__((always_inline));
。
请注意,静态变量的使用不是线程安全的,如参考文献中所述。如果您正在编写多线程应用程序,则可能需要为关键路径添加一些保护。