c++ - 使用汇编代码从内存复制到 C++ 中的寄存器

标签 c++ assembly cpu-registers

我在将 C++ 程序转换为汇编程序时遇到问题 我必须为

这是我的C++代码

for(int i=0;i<rows-4;i++,a+=4,b+=4,c+=4,d+=4,e+=4,f+=4,x+=4,o+=4){
  for(int j=0;j<cols-4;j++,a++,b++,c++,d++,e++,f++,x++,o++){
    *o=*a>*x;
    *o=*b>*x|(*o<<1);
    *o=*c>*x|(*o<<1);
    *o=*d>*x|(*o<<1);
    *o=*e>*x|(*o<<1);
    *o=*f>*x|(*o<<1);
    }
}

o 是指向输出数据的指针,而 a、b、c、d、e、f 和 x 是指向输入数据的指针。我想要的只是将输入数据的比较保存到单个变量,但是当处理的数据很大时,上面的代码效率不高。与将临时数据保存在寄存器中相比,程序需要更多次将数据保存到内存中。

所以我想做的就是让这个过程在寄存器中完成。我试过的是我将 x 引用的数据存储在 EBX 中,将 EBX 与 ECX 进行比较,ECX 保存 a 引用的值(和 b、c、d、e、f 顺序),将比较结果保存到 EAX 并移位EAX 寄存器向左,以便所有比较将存储在一个变量中。在所有 6 次比较都已处理后,ECX 中的值被复制到内存中。

这就是我所做的,我的程序可以运行两倍,但我得到的所有值都为零。也许我做错了?

      __asm__(
"xorl %%eax,%%eax;"
"xorl %%ebx,%%ebx;"
"xorl %%ecx,%%ecx;"

"movl %1, %%ebx;"

//start here
"movl %2,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .one;"
"orl $0x1,%%eax;"

".one:;"
"shll $1,%%eax;"
"movl %3,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .two;"
"orl $0x1,%%eax;"

".two:;"
"shll $1,%%eax;"
"movl %4,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .three;"
"orl $0x1,%%eax;"

".three:;"
"shll $1,%%eax;"
"movl %5,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .four;"
"orl $0x1,%%eax;"

".four:"
"shll $1,%%eax;"
"movl %6,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .five;"
"orl $0x1,%%eax;"

".five:"
"shll $1,%%eax;"
"movl %7,%%ecx;"
"cmp %%ebx,%%ecx;"
"jnz .six;"
"orl $0x1,%%eax;"

".six:"
//output
"movl %%eax,%0;"

:"=r"(sett)
:"r"((int)*x),"r"((int)*a) ,"r"((int)*b) ,"r"((int)*c) ,"r"((int)*d),"r"((int)*e),"r"((int)*f) /* input */
  );

最佳答案

几个选项:

1) 扔掉你手工编写的汇编代码。你说 C 代码很慢,告诉我们慢了多少。我看不出如何以任何有意义的方式测量差异,因为 asm 版本甚至没有产生正确的结果。换句话说,尝试 asm("nop;");,这是产生错误结果的更快方法。

2) 重写您的 C 代码以仅读取一次 *x;将结果保存在一个临时变量中,最后只写入*o

3) 如果适合你的语义(并且你的编译器支持)用restrict/__restrict/__restrict__装饰你的指针(来自C99 ,在 C++ 中通常作为扩展提供),因此编译器知道当您写入 *o 时输入变量不会发生变化。

4) 编译器相当擅长自动展开循环。它可能需要命令行选项、#pragma 指令或扩展/属性的组合。

编辑

这就是我重写它以使用临时对象的意思:

for(int i=0;i<rows-4;i++,a+=4,b+=4,c+=4,d+=4,e+=4,f+=4,x+=4,o+=4){
    for(int j=0;j<cols-4;j++,a++,b++,c++,d++,e++,f++,x++,o++){
        uint32_t tmp_x = *x;
        *o = (*a > tmp_x ? 0x20 : 0)
          |  (*b > tmp_x ? 0x10 : 0)
          |  (*c > tmp_x ? 0x08 : 0)
          |  (*d > tmp_x ? 0x04 : 0)
          |  (*e > tmp_x ? 0x02 : 0)
          |  (*f > tmp_x ? 0x01 : 0);
    }
}

这有什么区别?在原始版本中,x 在每次赋值时都会被读取。编译器不知道 ox 指向不同的位置;在最坏的情况下,编译器每次必须再次从x读取,因为通过写入o中的值x 可能会发生变化。

当然,这段代码有不同的语义:如果你真的让 o 成为另一个指针的别名,它会做一些与原来不同的事情。

关于c++ - 使用汇编代码从内存复制到 C++ 中的寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18157952/

相关文章:

c++ - 使用指针和普通变量调用基类函数的区别

c - 处理器如何知道断点?

assembly - 哪个 x86 寄存器表示 movsb 指令中的源位置?

调试 assembly

c++ - boost::shared_ptr 和继承

c++ - 找不到 C++ 中的重载 << 运算符

c++ - 不访问列表函数的 STL 列表数组

c++ - 使用 SSE 指令

linux - 换行字节 0Ah 被 x86_64 系统调用打印程序忽略

linux - VBAR_EL 寄存器是否通过 ARM 中的上下文切换保存?