5

我想使用 GCC 扩展的内联 ASM 编写以下循环:

long* arr = new long[ARR_LEN]();
long* act_ptr = arr;
long* end_ptr = arr + ARR_LEN;

while (act_ptr < end_ptr)
{
    *act_ptr = SOME_VALUE;
    act_ptr += STEP_SIZE;
}

delete[] arr;

long分配一个具有长度的类型数组ARR_LEN并进行零初始化。循环以 为增量遍历数组STEP_SIZE。每个触摸的元素都设置为SOME_VALUE

好吧,这是我在 GAS 中的第一次尝试:

long* arr = new long[ARR_LEN]();

asm volatile
(
    "loop:"
    "movl %[sval], (%[aptr]);"
    "leal (%[aptr], %[incr], 4), %[aptr];"
    "cmpl %[eptr], %[aptr];"
    "jl loop;"
    : // no output
    : [aptr] "r" (arr),
      [eptr] "r" (arr + ARR_LEN),
      [incr] "r" (STEP_SIZE),
      [sval] "i" (SOME_VALUE)
    : "cc", "memory"
);

delete[] arr;

正如评论中提到的,这个汇编代码确实更像是一个do {...} while循环,但实际上它确实做了同样的工作。

这段代码的奇怪之处在于,它起初对我来说很好用。但是当我后来试图让它在另一个项目中工作时,它似乎什么也做不了。我什至制作了一些工作项目的 1:1 副本,再次编译......结果仍然是随机的。

也许我对输入​​操作数采取了错误的约束,但我实际上已经尝试了几乎所有的约束,而且我没有真正的想法。特别让我感到困惑的是,它在某些情况下仍然有效。

我不是 ASM 方面的专家,尽管我在大学时就学会了它。请注意,我不是在寻找优化——我只是想了解内联汇编是如何工作的。所以这是我的问题:我的尝试有什么根本上的错误,还是我在这里犯了一个更微妙的错误?提前致谢。

(使用 g++ MinGW Win32 x86 v.4.8.1)

更新

到目前为止,我已经尝试了这里提供的每一个建议。特别是我试过

  • 使用“q”操作数约束而不是“r”,有时有效,有时无效,
  • ... : [aptr] "=r" (arr) : "0" (arr) ...而是写,同样的结果,
  • 甚至... : [aptr] "+r" (arr) : ...,还是一样。

同时,我对官方文档非常了解,但我仍然看不到我的错误。

4

3 回答 3

2

aptr您正在修改不允许的输入操作数 ( )。要么限制它匹配输出操作数,要么将其更改为输入/输出操作数。

于 2013-08-30T09:09:23.143 回答
2

这是具有预期行为的完整代码。

  • 请注意,代码是为 64 位机器编写的。因此,例如%%rbx使用for来代替%%ebx数组的基地址。出于同样的原因leaqcmpq应该使用 and 代替lealand cmpl
  • movq应该使用,因为数组的类型是long.
  • long在 64 位机器上,类型是 8 字节而不是 4 字节。
  • jl在问题中应更改为jg.
  • 不能使用寄存器标签,因为它们将被编译器替换为所选寄存器的 32 位版本(例如,ebx)。
  • "r"不能使用约束。"r"表示可以使用任何寄存器,但不能接受任何寄存器组合leaq。看这里:x86 寻址模式

    #include <iostream>
    
    using namespace std;
    
    int main(){
    
      int ARR_LEN=20;
      int STEP_SIZE=2;
      long SOME_VALUE=100;
    
      long* arr = new long[ARR_LEN];
    
      int i;
    
    
      for (i=0; i<ARR_LEN; i++){
        arr[i] = 0;
      }
    
      __asm__ __volatile__
      (
        "loop:"
        "movq %%rdx, (%%rbx);"
        "leaq (%%rbx, %%rcx, 8), %%rbx;"
        "cmpq %%rbx, %%rax;"
        "jg loop;"
        : // no output
        : "b" (arr),
          "a" (arr+ARR_LEN),
          "c" (STEP_SIZE),
          "d" (SOME_VALUE)
        : "cc", "memory"
      );
    
      for (i=0; i<ARR_LEN; i++){
        cout << "element " << i << " is " << arr[i] << endl;
      }
    
      delete[] arr;
    
      return 0;
    
    }
    
于 2014-11-09T01:27:02.077 回答
1

一个适用于 x86 和 x64 的答案怎么样(尽管它确实假设 long 总是 4 个字节,就像 Windows 一样)?OP 的主要变化是使用“+r”和(temp)。

#include <iostream>

using namespace std;

int main(){

  int ARR_LEN=20;
  size_t STEP_SIZE=2;
  long SOME_VALUE=100;

  long* arr = new long[ARR_LEN];

  for (int i=0; i<ARR_LEN; i++){
    arr[i] = 0;
  }

  long* temp = arr;

   asm volatile (
      "loop:\n\t"
      "movl %[sval], (%[aptr])\n\t"
      "lea (%[aptr], %[incr], %c[size]), %[aptr]\n\t"
      "cmp %[eptr], %[aptr]\n\t"
      "jl loop\n\t"
      : [aptr] "+r" (temp)
      : [eptr] "r" (arr + ARR_LEN),
        [incr] "r" (STEP_SIZE),
        [sval] "i" (SOME_VALUE),
        [size] "i" (sizeof(long))
      : "cc", "memory"
   );

  for (int i=0; i<ARR_LEN; i++){
    cout << "element " << i << " is " << arr[i] << endl;
  }

  delete[] arr;

  return 0;
}
于 2014-11-09T05:37:27.517 回答