我正在尝试使用gcc / g ++的内联 asm 指令(我不得不说我以前在 MSVC 上使用了英特尔语法,那是 breeze )。 我正在玩 double 值,以下 my_func2 似乎在执行后崩溃:
#include <iostream>
void my_func(const double *in, double *out) {
asm("mov %0, %%r8" : : "r"(in));
asm("movupd (%%r8), %%xmm0" :);
asm("movupd (%%r8), %%xmm1" :);
asm("addpd %%xmm1, %%xmm0" :);
asm("movupd %%xmm0, (%0)" : : "r"(out) : "%r8", "%xmm0", "%xmm1");
}
double my_func2(const double *in) {
double ret = 0.0;
asm("mov %0, %%r8" : : "r"(in));
asm("movupd (%%r8), %%xmm0" :);
asm("movupd (%%r8), %%xmm1" :);
asm("addpd %%xmm1, %%xmm0" :);
asm("movupd %%xmm0, %0" : "=m"(ret) : : "memory", "%r8", "%xmm0", "%xmm1");
return ret;
}
int main(int argc, char *argv[]) {
const double a = 1.0;
double b = 0.0;
my_func(&a, &b);
std::cout << "b:" << b << std::endl;
b = my_func2(&a);
std::cout << "b:" << b << std::endl;
}
我得到的错误是具体的(当我使用 gdb 运行时):
Program received signal SIGBUS, Bus error. 0x00000000004008e1 in main (argc=<error reading variable: Cannot access memory at address 0x400fffffffffffec>, argv=<error reading variable: Cannot access memory at address 0x400fffffffffffe0>) at asm_test.cpp:28 28 b = my_func2(&a);
我做错了什么?在 my_func2 的最后一行,我已经指定内存也被破坏了,我不明白......
我在哪里可以找到一个很好的指南如何使用臭名昭着的AT&amp; T语法?
我在Ubuntu上编译:{{1}}, g ++ 版本 g ++(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3 Linux scv 3.2.0 -48-generic#74-Ubuntu SMP Thu Jun 6 19:43:26 UTC 2013 x86_64 x86_64 x86_64 GNU / Linux 。
我找到http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html和http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html,你会推荐更多吗?
谢谢,
EMA
答案 0 :(得分:2)
这里的错误是使用movupd
时必须小心。使用此指令,实际上可以在和输出中复制128位内存。
第一个函数偶然可以复制这些值,但第二个函数在ret
变量中只有 64位空间。正如预期的那样,这会破坏堆栈,产生不确定的行为?
将movupd
替换为movlpd
(或movhpd
),可以使用魅力。
我还在破坏正确的寄存器吗?
使用g++ -O3 -o asm_test asm_test.cpp
void my_func(const double *in, double *out) {
asm ("mov %0, %%r8" : : "r"(in));
asm ("movhpd (%%r8), %%xmm0" :);
asm ("movhpd (%%r8), %%xmm1" :);
asm ("addpd %%xmm1, %%xmm0" :);
asm ("movhpd %%xmm0, (%0)" : : "r"(out) : "memory", "%r8", "%xmm0", "%xmm1");
}
double my_func2(const double *in) {
double ret;
asm("mov %0, %%r8" : : "r"(in));
asm("movlpd (%%r8), %%xmm0" :);
asm("movlpd (%%r8), %%xmm1" :);
asm("addpd %%xmm1, %%xmm0" :);
asm("movlpd %%xmm0, %0" : "=m"(ret) : : "memory", "%r8", "%xmm0", "%xmm1");
return ret;
}
答案 1 :(得分:0)
asm()
因为这可以让编译器选择放置内容的地方(mem或reg)。对于您的来源,这会将以下两个块内联到#include <xmmintrin.h> // for __m128d
static void my_func(const double *in, double *out) {
asm("movupd %1, %%xmm0\n"
"movupd %1, %%xmm1\n"
"addpd %%xmm1, %%xmm0\n"
"movupd %%xmm0, %0"
: "=rm"(*(__m128d*)out)
: "rm"(*(__m128d*)in)
: "%xmm0", "%xmm1");
}
static double my_func2(const double *in) {
double ret;
asm("movupd %1, %%xmm0\n"
"movupd %1, %%xmm1\n"
"addpd %%xmm1, %%xmm0\n"
"movlpd %%xmm0, %0"
: "=xm"(ret)
: "rm"(*(__m128d*)in)
: "%xmm0", "%xmm1");
return ret;
}
:
1c: 66 0f 10 44 24 10 movupd 0x10(%rsp),%xmm0 22: 66 0f 10 4c 24 10 movupd 0x10(%rsp),%xmm1 28: 66 0f 58 c1 addpd %xmm1,%xmm0 2c: 66 0f 11 44 24 20 movupd %xmm0,0x20(%rsp) [ ... ] 63: 66 0f 10 44 24 10 movupd 0x10(%rsp),%xmm0 69: 66 0f 10 4c 24 10 movupd 0x10(%rsp),%xmm1 6f: 66 0f 58 c1 addpd %xmm1,%xmm0 73: 66 0f 13 44 24 08 movlpd %xmm0,0x8(%rsp)
这不是最佳的,但是......如果你把它改为:
main()
将它留给编译器放置变量的位置。编译器检测到它可以完全没有进行加载/存储......因为它被简单地内联为:
18: 66 0f 28 c1 movapd %xmm1,%xmm0 1c: 66 0f 58 c1 addpd %xmm1,%xmm0因为编译器认识到它在寄存器中得到所有变量/希望寄存器中的所有返回。
虽然使用装配完全不需要它;使用一个不错的编译器(你的gcc会做)普通的C / C ++版本,
static void my_func(const double *in, double *out) {
asm volatile("movapd %1, %0\n"
"addpd %1, %0"
: "=xm"((__m128d*)out)
: "x"(*(__m128d*)in));
}
最有可能变成效率不高的代码。