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

C++ calling convention for passing big objects on Linux/x86-84

提问人:Xiaoyong Guo 提问时间:1/22/2021 最后编辑:Xiaoyong Guo 更新时间:1/22/2021 访问量:338

问:

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

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

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

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

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

的大小为 32 字节(GCC/Linux 实现,长 + 长 + 指针 + 指针 = 32 字节。因此,按值传递应该看起来像我的代码中定义的传递。但是输出程序集显示 pass 与 pass 有很大不同。看起来 std::function 是通过指针传递的,对吗?为什么会有这样的差异?std::functionstd::functionstruct Person4std::functionstruct Person3

#include <functional>

struct Person0 {
  long name;
};

long GetName(Person0 p) {
  return p.name;
}

struct Person1 {
  long name;
  long age;
};

long GetName(Person1 p) {
  return p.name;
}

struct Person2 {
  long name;
  long age;
  long height;
};

long GetName(Person2 p) {
  return p.name;
}

struct Person3 {
  long name;
  long age;
  long height;
  long weight;
};

long GetName(Person3 p) {
  return p.name + sizeof(p);
}

long Invoke(std::function<long(long)> f) {
  return f(20) + sizeof(f);
}


int main() {
  Person3 p;
  p.name = 13;
  p.age = 23;
  p.height = 33;
  p.weight = 43;
  long n = GetName(p);

  std::function<long(long)> ff;
  Invoke(ff);
  return 0;
}
C++ Linux 程序集 x86-64 调用约定

评论

1赞 Peter 1/22/2021
您的描述引用了 ,但代码中没有。struct Person4
0赞 Ted Lyngmo 1/22/2021
难道是 的类型擦除使编译器这样做吗?(也许只是增加了混乱 - 插话问题)std::function
0赞 Xiaoyong Guo 1/22/2021
@Peter结构 Person4 应该是结构 Person3,感谢您指出这一点。

答:

3赞 Marco 1/22/2021 #1

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

> 32 字节的结构始终位于堆栈上。 对于 <= 32 字节的结构,有一些逻辑:

Paramater Passing Rules

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

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

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

评论

1赞 Peter Cordes 1/22/2021
TL:DR:关于SSE / SSEUP的东西是,如果其大小正确,则由所有/成员(例如)组成的结构或其他对象可以进入XMM或YMM注册;否则,它就像任何物体一样。floatdouble__m128
1赞 Nate Eldredge 1/22/2021
是的,最后一个做到了。事实上,如果向其添加复制构造函数或析构函数,则其行为与 相同。Person3std::function
0赞 Xiaoyong Guo 1/22/2021
非常感谢您的解释。