【发布时间】:2015-11-12 16:31:11
【问题描述】:
我有一个大学监督任务,要在汇编程序中编写一个函数,
需要三个 32 位无符号数 a、b、d 和
返回结果(a * b)/d
这个函数的c声明是:
unsigned int muldiv(const unsigned int a,
const unsigned int b,
const unsigned int d);
请注意,我们要确保不会发生不必要的上溢或下溢。例如,
如果 a = 2^31, b = 2^31, d = 2^31,
答案应该是 2^31,尽管 a * b 会溢出。 (请参阅下面的更多说明)
现在我用c写了一个简单的函数,它可以工作,然后编译成机器码,然后反汇编成汇编代码,最后删除了一些不必要的指令。
我的最后一段汇编代码是:
muldiv:
imulq %rsi, %rax
xorl %edx, %edx
divq %rcx
ret
在编译为可执行代码并检查多个测试用例时有效。但是,我不明白,这段代码发生了什么。
因此,谁能解释一下为什么这段代码有效(或者可能无效?),特别是:
- 为什么divq %rcx 指令只使用一个寄存器?我假设这是除法部分,那么它如何知道要使用哪个 两个 参数?
- 它怎么知道当我从另一个地方调用 muldiv 时,参数 a、b 和 d 存储在寄存器 %rsi / %rax / e.t.c 中,而不是其他地方?
为什么需要xorl %edx, %edx?删除后,我收到运行时错误。
如果机器只能对 32 位数字进行运算,它如何仅使用一条指令对 long long 数字进行乘法运算?
上溢和下溢的说明: 这个函数应该返回结果,就好像我们在处理 无符号 64 位数字。 c中的代码如下:
// NOTE: when compiled to assembly code, I removed A LOT of instructions,
// but it still works
unsigned int muldiv(const unsigned int a,
const unsigned int b,
const unsigned int d) {
const unsigned long long la = a;
const unsigned long long lb = b;
const unsigned long long ld = d;
const unsigned long long ab = la * lb;
const unsigned long long ab_over_d = ab / ld;
return (unsigned int) ab_over_d;
}
当以这种方式调用时,它起作用了:
#include "muldiv.h"
int main(void) {
unsigned int a = (1 << 31);
unsigned int b = (1 << 31);
unsigned int d = (1 << 31);
unsigned int result = muldiv(a, b, d);
printf("%u\n", result); // prints (1 << 31), which is correct.
return 0;
}
【问题讨论】:
-
你读过英特尔程序员参考手册吗?这是一本很长但很有趣的书……并回答了你的一些问题。您还需要了解您正在使用的 ABI。我会找到一些链接... (intel.com/content/dam/www/public/us/en/documents/manuals/…)