装配偶数或奇数错误

时间:2016-01-13 10:55:53

标签: assembly

我正在尝试在Assembly中编写一个函数,它将返回数组中偶数元素的总和。这是我的代码

       int sum(int *a, int n)
       {
          int S = 0;
          _asm {

          mov eax, [ebp + 8]
          mov edx, 0
          mov ebx, [ebp + 12]
          mov ecx, 0
          for1: cmp ecx, ebx
          jge endfor
          and [eax + ecx * 4], 1
          jz even
          inc ecx
          jmp for1

          even: add edx, [eax + ecx * 4]            
          inc ecx
          jmp for1
          endfor: mov S, edx
          }
      return S;
      }

但它不起作用。有人知道这是什么问题,我该如何解决?感谢。

2 个答案:

答案 0 :(得分:4)

只是一些猜测(特别是因为我不知道你使用的是什么编译器,而且你不清楚“不工作”是什么意思):

      mov eax, [ebp + 8]
      mov edx, 0
      mov ebx, [ebp + 12]
      mov ecx, 0

我认为这应该将两个参数an加载到寄存器中。你怎么知道补偿是否正确?是否可以直接简单地引用这些名称?

      and [eax + ecx * 4], 1

这会破坏输入数组中的元素(如果是奇数则将其设置为1,如果是偶数则将其设置为0),这可能是您不想要的。您应该使用test指令(非破坏性的)而不是and

      even: add edx, [eax + ecx * 4]

这将添加0,因为您已通过上面提到的[eax + ecx * 4]指令将and设置为0。基于此,我希望你的函数总是返回0。

答案 1 :(得分:0)

如果你想自己抓住堆栈中的args,为什么甚至使用inline asm?让编译器给你args(所以它在内联后仍然可以工作),或者将纯asm写在一个单独的文件中。

这里有一个很好的函数版本。注意缩进和更高效的代码(分支结构,并加载到寄存器中,而不是在内存中进行比较,然后用作添加的内存操作数。如果条件预计不太可能,则为test或但是,带有内存操作数的cmp会有意义。)

int sum(const int *a, int n)
{
  int S;

  _asm {
      mov    ecx, n
      xor    eax,eax              // Sum = 0
      test   ecx,ecx
      jz   .early_out             // n==0 case
      mov    esi, a
      lea    edi, [esi + 4*edi]   // end pointer

  .sum_loop:
      // Assume even/odd is unpredictable, so do it branchlessly:
      xor    ecx,ecx
      mov    edx, [esi]           // lodsd could be used here (to load eax: keep the total somewhere else)
      test   edx, 1               // ZF cleared for odd numbers only
      cmovz  ecx, edx             // ecx=even:a[i]  odd:0
      add    eax, ecx             // add zero or a[i]

      add    esi, 4
      cmp    esi, edi             // pointer < end pointer
      jb  .sum_loop
  .early_out:
      mov S, eax     // MSVC inline asm is super dump: we can't just tell it the result is in eax
  }

  return S;
}

使用分支,简单的方法是

.sum_loop:
    mov   edx, [esi]
    test  edx, 1
    jnz .odd                    // conditionally skip the add
    add   eax, edx
.odd:
    add    esi, 4
    cmp    esi, edi             // pointer < end pointer
    jb  .sum_loop

你的原始分支结构(复制奇数/偶数分支的循环尾部)对于某些情况是一种有用的技术,但是在这里它们在添加到总数后它们再次相同时不再存在。

在asm中,在底部写入带有条件分支的循环。检查在进入循环之前意味着零迭代的条件。你不希望一个未被采取的cmp / jcc 成为一个无条件的分支。

从Haswell开始,

lodsd与英特尔的mov eax, [esi] / add esi, 4一样高效。它在早期的英特尔和AMD上都有3个微处理器,所以它以牺牲速度为代价来节省代码大小。

有关指南和内容,请参阅