在AT&T汇编中,有没有一种简单的方法可以像这样将两个寄存器相乘:%eax * %ebx = %ecx

3

我正在尝试使用AT&T/GAS语法将这个C语言for循环翻译成汇编:

for(int j = i; i*j < N; j++) {
    A[i*j] = 0;
}

我将 i 存储在 %eax 中,将 j 存储在 %ebx 中。我的问题是如何实际上将 ij 相乘,因为指令 imul "reg32", "reg32" 将结果存储在第二个寄存器中,显然我不想要这个。我想要的是能够将结果存储在另一个寄存器中,比如说 %ecx,然后使用它来访问数组中索引为 i*j 的值。

当我查看指令 imul 的用法时,似乎没有办法将两个寄存器相乘并将结果存储在第三个寄存器中。当然,我可以制作一个循环并进行一些加法等操作,但这似乎是低效的并且不是解决问题的正确方式。请注意,我完全是汇编新手(只用了几天),因为我们刚刚开始学习基础知识。

TL;DR

有什么最好的方法可以像这样相乘存储在两个寄存器中的值:%eax * %ebx = %ecx?


指令imul "reg32","reg32"将结果存储在第二个寄存器中,这显然不是我想要的。为什么很明显你不想要它?正确的方法是imul%eax,%ebx,然后,如果需要在%ecx中获得结果,则可以执行mov%eax,%ecx。如果需要保留%eax中的值,则可以保存它(例如,在堆栈上)。当我查找指令imul的用法时,似乎没有办法实际上将两个寄存器相乘并将结果存储在第三个寄存器中。这是真的。但是绕过它并不麻烦。 - lurker
3个回答

3

x86是一种双操作数结构,大多数指令都需要两个操作数,覆盖其中一个。如果要将结果写入第三个操作数而不是覆盖其中一个源操作数,则标准解决方案是先将一个操作数移动到目标位置,然后使用目标位置和另一个操作数执行双操作数指令。例如,要将eaxebx相乘,并将结果放在ecx中,则应执行以下操作:

mov %ebx, %ecx
imul %eax, %ecx

虽然其他人已经指出了,对于你的循环来说最好彻底放弃乘法,相反要认识到你可以通过加法来满足。你的循环

for (int j = i; i*j < N; j++) {
    A[i*j] = 0;
}

可以重写为

A_ = A + i * i;
N_ = N - i * i;
for (j = 0; j < N_; j += i)
    A_[j] = 0;

要求循环内不需要进行乘法计算。


2
当我查找imul指令的用法时,似乎没有办法实际上将两个寄存器相乘并将结果存储在第三个寄存器中。
这对大多数x86指令都是正确的——大多数算术和逻辑操作需要两个操作数,并将结果存储回其中一个源寄存器。如果您需要保存其中一个原始值,请将其复制到另一个寄存器中。
imul是一种特别奇怪的x86指令,它具有一种参数形式,该形式将源寄存器乘以eax,并将结果写入edx:eax。这些寄存器映射不够灵活;如果需要完整的乘积,则需要围绕此分配寄存器。
当然,我可以制作一个循环,并进行一些加法等操作,但这似乎是低效的做法。
这实际上是一个好方法——加法比乘法更快。一个优化良好的编译器可能会采取类似的方法。

imul有一种单操作数形式,但通常只有在需要高位结果时才使用该形式。它速度较慢(需要额外的uop来拆分结果并将高位写入EDX),并且通常需要额外的指令来获取/写入累加器中的数据。如果您只想要一个正常的截断32x32 => 32位乘法,请使用双操作数形式。(或使用立即数的三操作数形式imul r32,r/m32,imm)您的措辞似乎暗示您实际上会使用imul r/m32而不是imul r32,r/m32,但您只会在16位代码中使用8086兼容性。https://www.felixcloutier.com/x86/imul - Peter Cordes

2
你想要观察的是当增加j时,i*j如何变化。假设i是50,则最初j=50,因此i*j为50*50。循环的下一次迭代中,j为51,所以i*j为50*51,即50*(50+1),或50*50+50。接下来的迭代中,i*j为50*50+50+50,依此类推。
通过在j循环之外/之前初始化一个累加器,并在每个循环迭代中维护一个简单的加法指令,可以获得i*j的值而不需要乘法。
另请参见归纳变量
强烈怀疑,如果您查看外部的i循环(问题中未显示),您将能够消除初始乘法(这里是第一个i*i用于初始化累加器)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接