提问人:Xiaoyong Guo 提问时间:1/22/2021 最后编辑:Xiaoyong Guo 更新时间:1/22/2021 访问量:338
在 Linux/x86-84 上传递大对象的 C++ 调用约定
C++ calling convention for passing big objects on Linux/x86-84
问:
我正在尝试理解在 C++/Linux/x86-64 平台中按值传递对象作为函数参数的开销。
我用于探索的实验代码发布在下面和 godbolt.org 上:https://godbolt.org/z/r9Yfv4
假设函数是一元的。我观察到的是:
- 如果参数对象的大小为 8 个字节,则将其放入 RDI 中。
- 如果参数大小为 16 字节(包含两个子对象,每个子对象 8 个字节),则这两个子对象将被放入 RDI 和 RSI。
- 如果参数大于 16 个字节,它将通过堆栈传递。
我只考虑整数类型和指针以及这些基本类型的组合类型。我知道传球浮子/双打是不同的。
的大小为 32 字节(GCC/Linux 实现,长 + 长 + 指针 + 指针 = 32 字节。因此,按值传递应该看起来像我的代码中定义的传递。但是输出程序集显示 pass 与 pass 有很大不同。看起来 std::function 是通过指针传递的,对吗?为什么会有这样的差异?std::function
std::function
struct Person4
std::function
struct 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;
}
答:
3赞
Marco
1/22/2021
#1
您要阅读的文档是 x86-64 的 System V ABI,特别是第 3.2.3 节 «参数传递»
> 32 字节的结构始终位于堆栈上。 对于 <= 32 字节的结构,有一些逻辑:
合并后清理表示,如果大小大于 2 eighbytes(16 字节),并且第一个参数不是 SSE,或者任何其他参数不是 SSEUP,则整个聚合被归类为 MEMORY(堆栈)。
关于 的使用,还有最后一条规则可以解释它:std::function
- 如果 C++ 对象具有非平凡的复制构造函数或非平凡的析构函数,则它通过不可见的引用( 对象在参数列表中被具有类的指针替换 整数)
评论
1赞
Peter Cordes
1/22/2021
TL:DR:关于SSE / SSEUP的东西是,如果其大小正确,则由所有/成员(例如)组成的结构或其他对象可以进入XMM或YMM注册;否则,它就像任何物体一样。float
double
__m128
1赞
Nate Eldredge
1/22/2021
是的,最后一个做到了。事实上,如果向其添加复制构造函数或析构函数,则其行为与 相同。Person3
std::function
0赞
Xiaoyong Guo
1/22/2021
非常感谢您的解释。
评论
struct Person4
std::function