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

标签 c++ linux assembly x86-64 calling-convention

我试图了解在 C++/Linux/x86-64 平台中按值传递对象作为函数参数的开销。
我用于探索的实验代码发布在下面和godbolt.org上:https://godbolt.org/z/r9Yfv4
假设函数是一元的。我观察到的是:

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

  • 我只考虑整型和指针以及这些基本类型的组合类型。我知道传递 float / double 是不同的。std::function尺寸是 32 字节(GCC/Linux 实现,长 + 长 + 指针 + 指针 = 32 字节。)。所以路过std::function按值应该看起来像 pass struct Person4在我的代码中定义。但是输出程序集显示通过 std::function和pass很不一样struct Person3 .看起来 std::function 是通过指针传递的,对吗?为什么会有这样的差异?
    #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;
    }
    

    最佳答案

    您要阅读的文档是 System V ABI for x86-64 ,特别是第 3.2.3 节
    «参数传递»
    大于 32 字节的结构始终在堆栈上。
    对于 <= 32 字节的结构,有一些逻辑在进行:
    Paramater Passing Rules
    合并后清理表明,如果大小大于 2 个 eighbytes(16 字节),并且第一个参数不是 SSE,或者任何其他参数都不是 SSEUP,则整个聚合被归类为 MEMORY(堆栈)。
    关于std::function的使用,最后一条规则可以解释它:

    1. If a C++ object has either a non-trivial copy constructor or a non-trivial destructor, it is passed by invisible reference (the object is replaced in the parameter list by a pointer that has class INTEGER)

    关于在 Linux/x86-84 上传递大对象的 C++ 调用约定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65838379/

    相关文章:

    assembly - 设置IRQ映射

    c++ - 调用模板化基类的模板化成员失败

    c++ - 我如何扩展 std::basic_streambuf 以将任何可迭代序列视为流?

    c++ - 我可以使用带有 Visual Studio 2012 的 Windows 8 操作系统创建带有 GUI 的完整的 Vista win32 应用程序吗?

    mysql - 如何在 XAMPP 中将默认存储引擎设置为 InnoDB

    assembly - 如何连接不在宏定义中的标签? (在 GAS 汇编器中)

    c++ - 禁用 QSql(Relational)TableModel 的预取/缓存行为

    linux - 如何删除名称为 '' $'\r' 的文件?

    linux - Unix 对多个条件进行排序

    windows - PE : Relation between SizeOfRawData and VirtualSize fields of the section header