GCC内联汇编中的简单while循环

时间:2013-08-26 09:20:34

标签: c++ gcc inline-assembly gas

我想使用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的{​​{1}}类型的数组并进行零初始化。循环遍历数组,增量为{​​{1}}。每个被触摸的元素都设置为ARR_LEN

嗯,这是我在GAS的第一次尝试:

STEP_SIZE

正如评论中所提到的,这个汇编代码确实更像是一个SOME_VALUE循环,但事实上确实做了同样的工作。

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

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

我不是ASM的专家,虽然我还在大学时就学会了。请注意,我不是在寻找优化 - 我只是想了解内联汇编的工作原理。所以这是我的问题:我的尝试有什么根本性的错误,或者我在这里犯了一个更微妙的错误?提前谢谢。

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

更新

到目前为止,我已经尝试过在此提供的所有建议。特别是我试过

  • 使用“q”操作数约束而不是“r”,有时它可以工作,有时却没有,
  • 改为编写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,仍然相同。

与此同时,我非常了解官方documentation,但我仍然看不到我的错误。

3 个答案:

答案 0 :(得分:2)

您正在修改不允许的输入操作数(aptr)。约束它与输出操作数匹配或将其更改为输入/输出操作数。

答案 1 :(得分:2)

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

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

    #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;
    
    }
    

答案 2 :(得分:1)

一个适用于x86和x64的答案怎么样(虽然它确实假设long总是4个字节,一个Windows)? OP的主要变化是使用&#34; + r&#34;和(临时)。

#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;
}