c - linux 汇编程序如何乘除 64 位数字?

标签 c assembly

我有一个大学监督分配用汇编程序编写一个函数,它

将取三个 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 位数字进行运算,它如何仅使用一条指令对长整数进行乘法运算?

上溢和下溢的澄清: 此函数应返回结果,就好像我们正在对无符号 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;
}

最佳答案

why divq %rcx instruction uses only one register? I assume this is the division part, so how does it know which two arguments to use?

它使用来自寄存器对 rdx:rax 的隐式 128 位被除数。操作说明见指令集引用。

how does it know that when I call muldiv from another place, the arguments a, b and d are stored in the registers %rsi / %rax / e.t.c, not somewhere else?

这是由调用约定定义的。参见 wikipedia总结一下。

why xorl %edx, %edx is necessary? When removed, I get a runtime error.

参见上面的第 1 点。 rdx 具有被除数的前 64 位,因此应清除无符号除法。

How does it make multiplication on long long numbers using only one instruction, if machine can operate only on 32 bit numbers?

您使用的是 64 位模式,所以事实并非如此。此外,mul 和 div 指令具有双倍宽度,因此您甚至可以在 64 位模式下获得 128 位版本,在 32 位模式下获得 64 位版本。同样,请参阅指令集引用。

关于c - linux 汇编程序如何乘除 64 位数字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33676497/

相关文章:

无法从 TextView 获取 GtkTextBuffer - C、GTK3

assembly - 使用按位运算仅反转二进制数的 MSB

assembly - 引导加载程序不工作,错误 : "int13_harddisk: function 42. LBA out of range"

c - clang 试图优化这个简单的递归算法是什么?

sql - 原始SQL是用汇编语言还是C语言编写的?

c++ - 在 Darwin/OSX 中以编程方式确定进程信息

彩色文本 Windows 命令行

php - 找出数组中的序列相似性

c - 在 NASM 中调用 C 函数时遇到问题

c - 为什么我在 Valgrind 中得到此代码的 "memory error"?