ASM/NASM - 在类型结构中返回 MUL 的高低

ASM/NASM - Return high low of a MUL in a type struct

提问人:1111 B 提问时间:12/2/2022 最后编辑:Peter Cordes1111 B 更新时间:12/2/2022 访问量:64

问:

global mymul
mymul:
    mov rax, rdi
    mul rsi
    ret

#include <stdio.h>

typedef struct {
        unsigned long long high;
            unsigned long long low;
} resmul;

void mymul(unsigned long long, unsigned long long, resmul *res);

int main() {
    resmul res;

    mymul(3, 6, &res);
    printf("mymul(3, 6); res.high=0x%llx, res.low=0x%llx\n", res.high, res.low);
    //mymul(3, 6); res.high=0x0, res.low=0x12
    return 0;
}

目标是将第一个 arg 与第二个 arg 相乘,然后发送到最后一个 arg 第一个参数 = RDI / 第二个参数 = RSI 将结果高/低发送到 TypeStruct 的目标

我不明白为什么它给两个结果都给 0 RAX 和 RDX 应该返回,但我没有

程序集 x86-64 NASM 调用约定 int128

评论


答:

2赞 Nate Eldredge 12/2/2022 #1

你的函数是从 C 中声明为采用指针参数的,它应该将结果存储在内存中。但实际上,它将它们留在 rdx:rax 寄存器中,并且根本不会在内存中存储任何内容,完全忽略指针。mymul

第三个参数将在 rdx 寄存器中传递,这会使事情变得有点复杂,因为会覆盖它。所以你必须做类似的事情mul

global mymul
mymul:
    mov rcx, rdx  ; save argument
    mov rax, rdi
    mul rsi
    mov [rcx], rdx
    mov [rcx+8], rax
    ret

评论

0赞 1111 B 12/2/2022
哦,谢谢!但是为什么我们加+而不是8,难道不应该加4个字节吗?
0赞 Nate Eldredge 12/2/2022
@1111B:是 64 位,即 8 个字节。您的函数将两个 64 位输入相乘以产生 128 位输出。unsigned long longmymul
0赞 Peter Cordes 12/2/2022
IMO,正确的解决方法是更改原型和调用者,而不是通过引用返回来降低一切效率。(并将结构向后保留,高元素位于较低的地址。
0赞 Peter Cordes 12/2/2022 #2

您告诉编译器您的函数返回,因此调用方不会查看 RDX:RAX。void

使用调试器和/或查看调用方中的 asm;它将从堆栈中加载其未初始化的局部变量,而未写入的堆栈内存恰好为零。res


要正确声明在 RDX:RAX 中为 x86-64 SysV 调用约定返回的函数,请返回结构,或者返回未签名的 __int128(如果您使用的是 GNU 扩展)。

#include <stdio.h>
#include <stdint.h>

typedef struct {
        uint64_t low;         // low first, the low half of RDX:RAX
        uint64_t high;
} resmul;              // x86-64 System V will return this in RDX:RAX, like __int128

resmul mymul(uint64_t, uint64_t);  // your function doesn't look for a pointer in RDX

int main() {
    resmul res = mymul(3, 6);
    printf("mymul(3, 6); res.high=%#lx, res.low=%#lx\n", res.high, res.low);
    //mymul(3, 6); res.high=0x0, res.low=0x12
    return 0;
}

Godbolt,还包括编译为相同 asm 的函数的 GNU C 实现:

// compiles to the same asm
resmul mymul(uint64_t a, uint64_t b){
    unsigned __int128 prod = a * (unsigned __int128)b;
    return (resmul){prod, prod>>64};
}

unsigned __int128 mulv2(uint64_t a, uint64_t b){
    return a * (unsigned __int128)b;
}
mymul(unsigned long, unsigned long):
        mov     rax, rdi
        mul     rsi
        ret
mulv2(unsigned long, unsigned long):
        mov     rax, rdi
        mul     rsi
        ret