我比较了返回结构的2个C函数.我们知道在ABI级别上,大型结构将被指针作为第一个函数参数传递.
struct S {
int words[8];
};
struct S fsret() {
struct S s;
s.words[0] = 1;
return s;
}
void fout(struct S* s) {
s->words[0] = 1;
}
Run Code Online (Sandbox Code Playgroud)
对于这些函数,我检查了x86_64 Linux和Windows的程序集.该fsret声明为void @fsret(%struct.S* sret %s).
比较这两种变体,被叫方没有区别.但是,在函数内部,fsret另外将其第一个参数(指向结构的指针)复制到RAX寄存器.为什么?
原因在于这个评论差异:
\n\nif (Subtarget->is64Bit() || Subtarget->isTargetKnownWindowsMSVC()) {\n for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {\n // The x86-64 ABIs require that for returning structs by value we copy\n // the sret argument into %rax/%eax (depending on ABI) for the return.\n // Win32 requires us to put the sret argument to %eax as well.\n // Save the argument into a virtual register so that we can access it\n // from the return points.\nRun Code Online (Sandbox Code Playgroud)\n\n因此被调用者必须填充调用者提供的内存并返回它传递的指针。
\n\nx86_64证实了这一点 r252 System V ABI 文档
\n\n\n\n返回值 值的返回是根据以下算法完成的:
\n\n\n
\n- 使用分类算法对返回类型进行分类。
\n- 如果类型具有 MEMORY 类(ndMarco:即大东西),则调用者为返回值提供空间,并在 %rdi 中传递此存储的地址,就好像它是函数的第一个参数一样。实际上,此地址成为 \xe2\x80\x9chidden\xe2\x80\x9d 第一个参数。\n 此存储不得与被调用方通过除此参数之外的其他名称可见的任何数据重叠。\n 返回 %rax 时将包含调用者在 %rdi 中传入的地址。
\n