内联gcc程序集和局部变量(双)
Inline gcc assembly and local variables (double)
我正在尝试使用gcc/g++的内联asm指令(我不得不说,我以前在MSVC上使用过英特尔语法,这很简单)。我在玩双值,下面的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运行时):
程序接收到信号SIGBUS,总线错误。0x00000000004008e1在main中(argc=<读取变量时出错:无法访问地址0x400fffffffffec>处的内存;,argv=<读取变量时出错:无法访问地址0x400fffffffffe0>处的内存)在asm_test.cpp:2828 b=my_func2(/a);
我做错了什么?在my_func2的最后一行中,我指定内存也被破坏了,我不明白。。。我在哪里可以找到如何使用臭名昭著的AT&T语法
我使用g++ -g -o asm_test asm_test.cpp
,g++版本g++(Ubuntu/Linaro 4.6.3-1ubuntu5)4.6.3在UbuntuLinux scv 3.2.0-48-generic#74 Ubuntu SMP Thu Jun 6 19:43:26 UTC 2013 x86_64 x86_66 x86_64GNU/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
这里的错误是使用movupd
时必须小心。使用此指令,您实际上可以将128位内存复制到和中。
碰巧第一个函数也可以复制这些值,但第二个函数在ret
变量中只有的64位空间。正如预期的那样,这会腐蚀堆栈,导致未定义的行为
用movlpd
(或movhpd
)代替movupd
,事物就会发挥魅力。
我还在敲打正确的寄存器吗?
以下代码在使用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;
}
gcc内联程序集并不特别喜欢如果有单独的行asm()
语句,而这些实际上并不独立。你最好把上面的代码写成:
#include <xmmintrin.h> // for __m128d
static void my_func(const double *in, double *out) {
asm("movupd %1, %%xmm0n"
"movupd %1, %%xmm1n"
"addpd %%xmm1, %%xmm0n"
"movupd %%xmm0, %0"
: "=rm"(*(__m128d*)out)
: "rm"(*(__m128d*)in)
: "%xmm0", "%xmm1");
}
static double my_func2(const double *in) {
double ret;
asm("movupd %1, %%xmm0n"
"movupd %1, %%xmm1n"
"addpd %%xmm1, %%xmm0n"
"movlpd %%xmm0, %0"
: "=xm"(ret)
: "rm"(*(__m128d*)in)
: "%xmm0", "%xmm1");
return ret;
}
因为这让编译器可以选择把东西放在哪里(mem或reg)。对于您的源代码,这将以下两个块内联到main()
:中
1c:66 0f 10 44 24 10 movupd 0x10(%rsp),%xmm022:66 0 f 10 4c 24 10 movupd 0x10(%rsp),%xmm128:66 0f 58 c1 addpd%xmm1,%xmm02c:66 0f 11 44 24 20 movupd%xmm0,0x20(%rsp)[…]63:66 0 f 10 44 24 10 movupd 0x10(%rsp),%xmm069:66 0 f 10 4c 24 10 movupd 0x10(%rsp),%xmm16f:66 0f 58 c1 addpd%xmm1,%xmm073:66 0f 13 44 24 08 movlpd%xmm0,0x8(%rsp)
不过,这不是最佳的(_N)。。。如果您将其更改为:
static void my_func(const double *in, double *out) {
asm volatile("movapd %1, %0n"
"addpd %1, %0"
: "=xm"((__m128d*)out)
: "x"(*(__m128d*)in));
}
把变量放在哪里就交给编译器了。编译器检测到它可以完全不进行加载/存储。。。因为它被内联为:
18:66 0f 28 c1 movpd%xmm1,%xmm01c:66 0f 58 c1 addpd%xmm1,%xmm0因为编译器识别出它在寄存器中拥有所有变量/希望所有返回都在寄存器中。
尽管使用汇编根本没有必要这样做;有了一个不错的编译器(你的gcc会做的),普通的C/C++版本,
static void my_func(const double *in, double *out) {
out[0] = in[0] + in[0];
out[1] = in[1] + in[1];
}
很可能会变成同样高效的代码。
- 使用的未初始化局部变量'Quick'
- 修复未初始化的局部变量错误
- 局部变量保留函数中的值
- 如何使用 C++ 中的继承函数访问派生类中的局部变量
- 将引用分配给局部变量,如果局部变量超出范围,它会超出范围吗?
- Gnuplot_i.hpp C++接口绘制局部变量而不是文件
- 如何在函数外部访问函数中局部变量的值?
- 赋予全局变量而不是局部变量优先级的函数 - (异常行为)
- C++中静态方法的局部变量范围
- 未初始化的局部变量错误甚至认为我初始化了它(C++)
- 离开范围后如何保护局部变量的值?
- 局部变量名称冲突
- C++ lambda:如何'freeze'局部变量的值?
- 我应该使我的局部变量常量还是可移动的
- 获取具有静态局部变量的绑定/推断捕获 lambda 的函数指针
- 如何在Qt(gcc 64位)中禁用自动初始化局部变量
- 当局部变量被复制到全局变量时,C++GCC优化速度会减慢
- 内联gcc程序集和局部变量(双)
- 在不同优化级别访问gcc/g++中的局部变量与全局变量的速度
- GCC 中的断言和未使用的局部变量警告不能很好地混合?