我正在尝试在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;
}
但它不起作用。有人知道这是什么问题,我该如何解决?感谢。
答案 0 :(得分:4)
只是一些猜测(特别是因为我不知道你使用的是什么编译器,而且你不清楚“不工作”是什么意思):
mov eax, [ebp + 8]
mov edx, 0
mov ebx, [ebp + 12]
mov ecx, 0
我认为这应该将两个参数a
和n
加载到寄存器中。你怎么知道补偿是否正确?是否可以直接简单地引用这些名称?
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个微处理器,所以它以牺牲速度为代价来节省代码大小。
有关指南和内容,请参阅x86。