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

Is there an easy way to multiply two registers like this in AT&T assembly: %eax * %ebx = %ecx

我正在尝试使用 AT&T/GAS 语法将此 for 循环从 C 语言转换为汇编语言:

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

我有 i 存储在 %eaxj 存储在 %ebx。我遇到的问题是实际乘以 ij,作为指令 imul "reg32", "reg32" 将结果存储在第二个寄存器中,我显然不希望这样。我想要的是能够将结果存储在另一个寄存器中,例如 %ecx,然后使用它来访问数组中索引 i*j 处的值。

当我查找指令 imul 的用法时,似乎没有办法将两个寄存器实际相乘并得到结果存储在第三个寄存器中。当然,我可以做一个循环并做一些加法等等,但这似乎是无效的,而不是解决这个问题的方法。请注意,我对汇编完全陌生(只用了几天),因为我们刚刚开始学习我的 CS 课程的基础知识。

TL;DR

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

When I look up the usage for the instruction imul, there seems to be no way to actually multiply two registers and have the result stored in a third register.

大多数 x86 指令都是如此 -- 大多数算术和逻辑运算都采用两个操作数并将结果存储回源寄存器之一。如果您需要保存其中一个原始值,请将其复制到另一个寄存器。

imul 是一个特别奇怪的 x86 指令,因为它有一个单参数形式,它将源寄存器乘以 eax,并将结果写入 edx:eax。这些寄存器映射不灵活;如果你需要完整的产品,你需要围绕这个分配你的寄存器。

Off course, I could make a loop and do some addition and so on, but that seems ineffective and not the way to do go about this.

这实际上是一个很好的方法 -- 加法比乘法快。一个好的优化编译器可能会按照这些思路做一些事情。

您想要观察的是 i*j 随着 j 的增加而变化的方式。所以,我们假设 i50,那么最初 j = 50,所以 i*j50*50。循环的下一次迭代,j51,所以 i*j50*51,或者,50*(50+1),或者,50*50+50。而接下来的迭代,i*j50*50+50+50,依此类推。

通过保留一个累加器,用 i*i 初始化 outside/before j 循环,并在每个循环迭代中使用一个简单的加法指令进行维护,您可以获得 i*j 没有乘法。

另见 induction variable

强烈怀疑如果你看一下外部i循环(问题中没有显示),你将能够消除初始乘法(这里第一个i*i初始化累加器)。

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;

循环内不需要乘法。