我尝试构建一个使用pthreads和__m128 SSE类型的应用程序。根据GCC手册,默认堆栈对齐是16个字节。为了使用__m128,要求是16字节对齐。
我的目标CPU支持SSE。我使用的GCC编译器不支持运行时堆栈重组(例如-mstackrealign)。我不能使用任何其他GCC编译器版本。
我的测试应用程序如下:
#include <xmmintrin.h>
#include <pthread.h>
void *f(void *x){
__m128 y;
...
}
int main(void){
pthread_t p;
pthread_create(&p, NULL, f, NULL);
}
应用程序生成异常并退出。经过简单的调试(printf“%p”,&amp; y)后,我发现变量y不是16字节对齐的。
我的问题是:如何在不使用任何GCC标志和属性(它们没有帮助)的情况下正确地重新对齐堆栈(16字节)?我应该在这个线程函数f()中使用GCC内联汇编程序吗?
答案 0 :(得分:7)
在堆栈上分配一个比sizeof(__m128)
大15个字节的数组,并使用该数组中的第一个对齐地址。如果需要多个,请将它们分配到一个具有15个字节边距的数组中进行对齐。
我不记得分配一个unsigned char
数组是否可以使您免受编译器严格别名优化的影响,或者它是否仅适用于其他方式。
#include <stdint.h>
void *f(void *x)
{
unsigned char y[sizeof(__m128)+15];
__m128 *py = (__m128*) (((uintptr_t)&y) + 15) & ~(uintptr_t)15);
...
}
答案 1 :(得分:3)
这不应该首先发生,但要解决问题,你可以尝试:
void *f(void *x)
{
__m128 y __attribute__ ((aligned (16)));
...
}
答案 2 :(得分:1)
另一个解决方案是,使用填充函数,首先对齐堆栈,然后调用f
。因此,不要直接调用f
,而是调用pad
,先填充堆栈,然后使用对齐的堆栈调用foo
。
代码如下所示:
#include <xmmintrin.h>
#include <pthread.h>
#define ALIGNMENT 16
void *f(void *x) {
__m128 y;
// other stuff
}
void * pad(void *val) {
unsigned int x; // to get the current address from the stack
unsigned char pad[ALIGNMENT - ((unsigned int) &x) % ALIGNMENT];
return f(val);
}
int main(void){
pthread_t p;
pthread_create(&p, NULL, pad, NULL);
}
答案 3 :(得分:0)
我已经解决了这个问题。 这是我的解决方案:
void another_function(){
__m128 y;
...
}
void *f(void *x){
asm("pushl %esp");
asm("subl $16,%esp");
asm("andl $-0x10,%esp");
another_function();
asm("popl %esp");
}
首先,我们将堆栈增加16个字节。其次,我们使最不重要的半字节等于0x0。我们使用push / pop操作数保留堆栈指针。我们调用另一个函数,它具有16字节对齐的所有自己的局部变量。所有嵌套函数的局部变量也都是16字节对齐的。
它有效!
答案 4 :(得分:0)
很抱歉复活旧帖...
对于使用比OP更新的编译器的用户,OP会提到-mstackrealign
选项,这会将我引导至__attribute__((force_align_arg_pointer))
。如果您的函数正在优化以使用SSE,但%ebp
未对齐,则会根据您的需要透明地执行运行时修复。我还发现这只是i386
上的一个问题。 x86_64
ABI保证参数与16个字节对齐。
__attribute__((force_align_arg_pointer))
void i_crash_when_not_aligned_to_16_bytes() {
...
}
为那些可能想要了解更多信息的人提供的文章:http://camel.apache.org/message-endpoint.html