【问题标题】:Assembly Power(A,b) function装配幂(A,b) 函数
【发布时间】:2016-10-18 17:56:33
【问题描述】:

我正在尝试基于此 c 代码制作一个汇编 power(a, b) 函数:

int power(int x, int y)
{
    int z;

    z = 1;
    while (y > 0) {
        if ((y % 2) == 1) {
            y = y - 1;
            z = z * x;
        } else {
            y = y / 2;
            x = x * x;
        }
    }

    return z;
}

虽然,由于某种原因,它只能得到一些正确的输出,我无法弄清楚问题出在哪里。这是我的汇编代码:

;* int power(int x, int y);                                                  *
;*****************************************************************************
%define y [ebp+12]
%define x [ebp+8]
%define z [ebp-4]



 power:
push   ebp
mov    ebp,esp
sub    esp,16
mov    byte[ebp-4], 1
jmp    L3;

L1:
mov    eax,[ebp+12]
and    eax,0x1
test   eax,eax
je     L2;
sub    byte[ebp+12],1
mov    eax,[ebp-4]
imul   eax, [ebp+8]
mov    [ebp-4],eax
jmp    L3;

L2:
mov    eax,[ebp+12]
mov    edx,eax
shr    edx,31
 add    eax,edx
sar    eax,1
 mov    [ebp+12],eax
mov    eax,[ebp+8]
imul   eax,[ebp+8]
mov    [ebp+8],eax

L3:
cmp    byte[ebp+12],0
jg     L1;
mov    eax,[ebp-4]
leave  
ret 

当我运行它时,这是我的输出:

power(2, 0) returned -1217218303 - incorrect, should return 1
power(2, 1) returned 2 - correct
power(2, 2) returned -575029244 - incorrect, should return 4
power(2, 3) returned 8 - correct

谁能帮我找出我哪里出错了,或者纠正我的代码,任何帮助将不胜感激。

【问题讨论】:

  • 学习使用调试器并注释您的代码,尤其是在您希望其他人提供帮助时。
  • 您是否意识到if ((y % 2) == 1) 重写为if ((y & 1) != 0) 时更容易在汇编程序中编码?不需要比较,只需要一个测试和一个分支。
  • @WeatherVane 大多数编译器应该足够聪明,可以通过适当的移位/掩码替换除法/模数的 2 次幂。
  • @dbush 是一个这么聪明的汇编程序吗?我的意思是这样写会更容易翻译。
  • 我可以看到减量现在在做什么。它导致循环的下一次迭代转到else 部分,因此除法/平方被延迟到下一次迭代。不过,这似乎是一个杂项,因为这意味着对每个设置位的条件进行额外的评估。

标签: c assembly x86 nasm


【解决方案1】:

您正在存储一个字节,然后读回该字节 + 3 个垃圾字节。

%define z [ebp-4]    ; what's the point of this define if you write it out explicitly instead of  mov eax, z?
                     ; should just be a comment if you don't want use it

mov    byte[ebp-4], 1     ; leaves [ebp-3..ebp-1] unmodified
...
mov    eax, [ebp-4]       ; loads 4 bytes, including whatever garbage was there

使用调试器会告诉您此时您在 EAX 中得到了垃圾。您可以通过使用movzx eax, byte [ebp-4] 来修复它,或者首先存储 4B。

或者,更好的是,根本不要使用任何额外的堆栈内存,因为您可以使用 ECX,甚至无需在通常的 32 位调用约定中保存/恢复它。将您的数据保存在寄存器中,这就是它们的用途。


您使用[ebp+16] 的“解决方案”是写入调用者拥有的堆栈空间。你很幸运,我猜它恰好将高 3 个字节归零,并且破坏它不会导致崩溃。


and    eax,1
test   eax,eax

是多余的:test eax, 1 就足够了。 (或者如果你想销毁 eax,and eax, 1 根据结果设置标志)。

您的代码中还有大量其他低效之处。

【讨论】:

  • 感谢您的意见。
猜你喜欢
  • 2014-08-05
  • 1970-01-01
  • 2021-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多