"SRET"实际上是什么意思?

8

我比较了两个返回结构体的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;
}

对于这些函数,我检查了x86_64 Linux和Windows的汇编代码。 fsret被声明为void @fsret(%struct.S* sret %s)
比较这两个版本,在被调用方面没有区别。但是,在函数内部,fsret另外将其第一个参数(指向结构体的指针)复制到RAX寄存器中。为什么呢?

如果你这样做 if (fsret().words[0]==10) { do_something(); } ? 编译器在这种情况下需要一个返回值(不确定,只是一个想法) - Jean-François Fabre
1个回答

5

原因在于这个审查差异:

if (Subtarget->is64Bit() || Subtarget->isTargetKnownWindowsMSVC()) {
  for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
    // The x86-64 ABIs require that for returning structs by value we copy
    // the sret argument into %rax/%eax (depending on ABI) for the return.
    // Win32 requires us to put the sret argument to %eax as well.
    // Save the argument into a virtual register so that we can access it
    // from the return points.

所以被调用者必须填写由调用者提供的内存返回它所传递的指针。
这在x86_64 r252 System V ABI文档中得到确认。

返回值的返回是根据以下算法完成的:

  1. 使用分类算法对返回类型进行分类。
  2. 如果类型具有MEMORY类 (ndMarco:即大数据),则调用者为返回值提供空间,并将该存储的地址作为第一个参数传递给函数中。实际上,该地址成为“隐藏”的第一个参数。此存储必须不重叠任何通过其他名称可见于被调用方的数据。 返回时 %rax 将包含调用者在%rdi中传递的地址。

1
很棒的回答!您知道其他目标是否有类似的要求吗? - Paweł Bylica
@PawełBylica o32和eabi,许多ABI文档都相当糟糕。 - Marco A.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接