C++/ASM:按值传递使用 xmm0,但通过引用使用 rdi

Posted

技术标签:

【中文标题】C++/ASM:按值传递使用 xmm0,但通过引用使用 rdi【英文标题】:C++/ASM: Passing by value uses xmm0, but by reference it uses rdi 【发布时间】:2019-09-01 18:01:56 【问题描述】:

我开始熟悉如何让 C/C++ 与 ASM 函数对话。为此,我首先了解了寄存器如何用于参数。

我有 2 个例程,可以将 2 个 64 位浮点数相乘。其中一个返回结果,另一个将结果保存在第一个参数中,即通过引用传递。 第一种情况很简单,而且没有问题,但第二种情况我无法让它工作,直到我让 g++ 生成汇编代码来看看它应该如何完成。程序集和 c++ 宿主代码放在各自的文件中:

section .text
global Mul_Double_ASM
global Mul_Double_ASM_2

Mul_Double_ASM:
    mulsd   xmm0,   xmm1
    ret

Mul_Double_ASM_2:
    mulsd   xmm0,   [rdi]
    movsd   [rdi],  xmm0

C++ 代码很简单:

#include <iostream>

using namespace std;

extern "C" double Mul_Double_ASM(double val1, double val2);
extern "C" void Mul_Double_ASM_2(double &val1, double val2);

int main(void)
    
    double  val_1,
            val_2;

    cin >> val_1;
    cin >> val_2;

    cout << std::fixed << Mul_Double_ASM(val_1, val_2) << endl;

    Mul_Double_ASM_2(val_1, val_2);
    cout << std::fixed << val_1 << endl;

    return 0;
    

它读取 2 个用户提供的数字并将它们与汇编函数相乘。这两个代码现在都可以工作,但我尝试 Mul_Double_ASM_2 的方式是:

mulsd   xmm0,   xmm1

最后没有 ret,当我打印 val_1 的值时,它具有原始值。然后我了解到第一个参数存储在寄存器 rdi 中,因此对我的代码进行了调整。

所以我的问题是:我可以在第一个函数中直接将 xmm0 和 xmm1 相乘而从不使用 rdi,但是当函数是通过引用传递时,我需要使用 rdi。你能解释一下为什么在第一种情况下使用 xmm0 和 xmm1 有效,而在第二种情况下无效?

【问题讨论】:

查找平台/编译器的调用约定。缺点是浮点数在xmm 寄存器中传递,但非浮点数在常规寄存器中传递;引用被视为指针(非浮点数)。 @Justin ,我碰巧有一个页面,其中包含打开 nasm (Linux) 的 x64 调用约定,但没有真正发现任何说明您所解释的内容:引用被视为指针。它只提到“xmm0 是第一,xmm1 是第二......”。如果您想将此作为答案,我很乐意接受。 【参考方案1】:

GCC 使用的调用约定——System V AMD64 ABI*——将浮点值放在 XMM 寄存器中,但在通用寄存器中放置非浮点值。引用在程序集中作为指针(非浮点数)传递(请参阅How are references implemented internally?)。

这意味着doublexmm0xmm1中的两个by-value doubles被传递给函数,而by-reference double的地址在rdi中被传递by-值double 传入xmm0

不同的调用约定可能会有所不同,但基于寄存器的调用约定通常在 XMM 寄存器而不是通用寄存器中传递浮点值。

* 我找不到 GCC 使用此 ABI 的保证,但它看起来是它的默认 ABI。

【讨论】:

GCC 默认使用目标系统的标准调用约定。例如MinGW GCC 面向 Windows x64(即使从 Linux 交叉编译),而面向 Linux 的 GCC 使用 x86-64 System V。所以是的,在 RDI 中传递第一个指针/整数 arg 清楚地表明它正在使用 x86-64 SysV。

以上是关于C++/ASM:按值传递使用 xmm0,但通过引用使用 rdi的主要内容,如果未能解决你的问题,请参考以下文章

Common Lisp:按值传递与按引用传递[重复]

C# 按值传递与按引用传递

复制赋值运算符应该通过 const 引用还是按值传递?

为啥我要在 C 中按值传递函数参数?

JavaScript 是按引用传递还是按值传递语言?

通过引用而不是按值传递 OpenCV C++