在 Linux/x86-84 上传递大对象的 C++ 调用约定

问题描述

我试图了解在 C++/Linux/x86-64 平台中按值传递对象作为函数参数的开销。

我用于探索的实验代码发布在下面和 Godbolt.org 上:https://godbolt.org/z/r9Yfv4

假设函数是一元的。我观察到的是:

  1. 如果参数对象的大小为 8 字节,则将其放入 RDI。
  2. 如果参数大小为 16 字节(每 8 字节包含两个子对象),则将这两个子对象放入 RDI 和 RSI。
  3. 如果参数大于 16 字节,它将通过堆栈传递。

我只考虑整型和指针以及这些基本类型的组合类型。我知道传递浮动/双打是不同的。

-imatch 的大小为 32 字节(GCC/Linux 实现,long + long + 指针 + 指针 = 32 字节。)。因此,按值传递 -match 应该类似于我的代码中定义的传递 std::function。但是输出程序集显示 pass std::function 与 pass struct Person4 非常不同。看起来 std::function 是通过指针传递的,对吗?为什么会有这样的差异?

std::function

解决方法

你要阅读的文档是System V ABI for x86-64,特别是3.2.3节 «参数传递»

> 32 字节的结构总是在堆栈上。 对于

Paramater Passing Rules

合并后清理说,如果大小大于 2 个 eighbytes(16 个字节),并且第一个参数不是 SSE,或者任何其他参数都不是 SSEUP,则整个聚合被归类为 MEMORY(堆栈) .

关于 std::function 的使用,最后一条规则可以解释它:

  1. 如果一个 C++ 对象有一个非平凡的复制构造函数或一个非平凡的析构函数,它会通过不可见的引用( 对象在参数列表中被具有类的指针替换 整数)