最近,我试图编写一个程序来计算(a*b)%m,其中(0<=a,b,m<=2^63-1)。幸运的是,我知道GCC支持__int128_t。所以我最终得到了下面的程序。

#include <stdint.h>

int64_t multimod(int64_t a, int64_t b, int64_t m)
{
  __int128_t ab = (__int128_t)a * b;
  ab %= m;
  return ab;
}

但我想在不__int128_t的情况下完成这项工作,这样既能挑战自己,又能使这项工作更有效率。我决定先模拟这个函数的汇编程序的过程。所以我使用了objdump并得到了multimod的以下部分。
int64_t multimod(int64_t a, int64_t b, int64_t m)
{
 720:   55                      push   %rbp
 721:   49 89 d1                mov    %rdx,%r9
 724:   49 89 f8                mov    %rdi,%r8
 727:   49 c1 f8 3f             sar    $0x3f,%r8
 72b:   48 89 f0                mov    %rsi,%rax
 72e:   48 c1 f8 3f             sar    $0x3f,%rax
 732:   4c 89 c2                mov    %r8,%rdx
 735:   48 0f af d6             imul   %rsi,%rdx
 739:   48 0f af c7             imul   %rdi,%rax
 73d:   49 89 c0                mov    %rax,%r8
 740:   49 01 d0                add    %rdx,%r8
 743:   48 89 f8                mov    %rdi,%rax
 746:   48 f7 e6                mul    %rsi
 749:   48 89 c7                mov    %rax,%rdi
 74c:   49 8d 34 10             lea    (%r8,%rdx,1),%rsi
 750:   4c 89 c9                mov    %r9,%rcx
 753:   48 c1 f9 3f             sar    $0x3f,%rcx
 757:   4c 89 ca                mov    %r9,%rdx
 75a:   e8 61 00 00 00          callq  7c0 <__modti3>
 75f:   5d                      pop    %rbp
 760:   c3                      retq

我分析了整个部分,认为它可以分为两个部分——1。得到64位变量ab2的正确128位乘积。__modti3
我开始知道__modti3的原型是long long __modti3(long long a, long long b)。但是程序集代码不是这样得到的。当它调用__modti3时,第一个参数%rdi包含ab的低64位乘积,第二个参数%rsi包含ab的高64位乘积,第三个参数%rdx包含m。那么__modti3如何才能得到正确的答案呢?

最佳答案

不,long long是64位的。您可以看到gcc正在rdi、rsi、rdx和rcx中传递\uuu modti3参数。(即x86-64 SysV ABI中的前4个arg传递插槽。)
所以这是两个128位操作数,通过值成对的regs传递:rsi:rdircx:rdx
它实际上是__int128 __modti3(__int128 quotient, __int128 divisor);这是整个存在点和原因:x86—64在硬件中有long long % long long余数。
idiv r64,gcc将用于运行时变量除数/模。
注意,您的功能是从m扩展到rdx的符号

mov    %r9, %rcx        # originally from RDX on entry; you didn't enable full optimization
sar    $63, %rcx        # copy sign bit to all bit positions.

这与rcx:rdx(AT&Tcqo)将extend RAX标记为RDX:RAX完全一样。
顺便说一句,如果使用cqto启用完全优化,则代码更易于阅读。然后只得到1条乘法指令,使用64位输入并产生128位输出。https://gcc.godbolt.org/z/0gKc5d
使用-O3-O1编译有时更有用,如果您想要看起来更像源代码的asm,但是由于C没有加宽的乘法运算符,您实际上并不需要它。您希望编译器在乘法成加宽乘法之前优化加宽输入,而不是将输入扩展成成对寄存器并执行128x128=>128位乘法。(这就是您显示的代码中发生的情况。)

关于c - __modti3做什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52384456/

10-11 18:14