c++ - 使用用于传递参数的寄存器的预定顺序调用约定是否有特殊原因?

标签 c++ c calling-convention abi

例如,调用 int func( int a, int * b...) 会将 ab 放入寄存器 r0r1等,调用该函数并在r0中返回结果(注:这里说得很笼统,与任何特定处理器或调用约定;另外,我们假设在寄存器中快速调用传递参数)。
现在,如果每个函数都使用在寄存器中传递的参数来编译,这些参数已经是其参数类型的首选(指向首选指针/基址寄存器的指针,指向 vector 寄存器的类似数组的数据......),而不是更好,那不是更好吗?遵循使用函数原型(prototype)中给出的或多或少严格的函数参数顺序的少数调用约定规则之一?
这样,代码可以避免在此类函数调用之前和之后用于对寄存器中的参数进行洗牌的指令,并且通常还可以避免再次重复使用相同的基本寄存器等。当然,这将需要每个函数一些额外的数据(大概是保存在数据库或目标文件中),但即使在动态链接的情况下也是有益的。
那么,除了历史原因之外,是否有任何强有力的理由反对在 21 世纪做类似的事情?

最佳答案

对于从单个位置调用的函数,或者至少是静态的函数,这样编译器可以知道每个调用位置(假设编译器可以证明该函数的地址是不作为函数指针传递)这是可以完成的。

问题是,几乎每次都可以这样做,但方式略有不同:编译器可以内联代码,并且一旦内联,就不再需要调用约定,因为不再有调用正在制作中。

但是让我们回到数据库的想法:您可能会说这没有运行时成本。生成代码时,编译器检查数据库并生成适当的代码。这实际上没有任何帮助。您仍然有一个调用约定,但不是所有代码都遵循一个(或几个)调用约定,而是现在每个函数都有不同的调用约定。当然,编译器不再需要将第一个参数放在 r0 中,但对于 foo,它需要将其放在 r1 中,在 >r5 用于 bar 等。使用正确的值设置正确的寄存器仍然存在开销。知道在这样的函数调用之后要恢复哪些寄存器也变得更加困难。调用约定明确指定哪些寄存器是 volatile 的(因此它们的值在从被调用函数返回时会丢失)和非 volatile 的(因此它们的值被保留)。

一个更有用的功能是生成被调用函数的代码,使其使用已经恰好保存这些值的寄存器。内联代码时可能会发生这种情况。

补充一点,我相信这就是 Rust 在 Rust-to-Rust 调用中所做的事情。据我所知,该语言没有固定的调用约定。相反,编译器尝试根据参数值已存在于哪些寄存器中来生成代码。不幸的是,我似乎找不到任何关于此的官方文档,但是这个 rust-lang discussion may be of help .

更进一步:并非所有代码路径在编译时都是已知的。考虑一下函数指针:如果我有以下代码:

typedef void (*my_function_ptr_t)(int arg1);

my_function_ptr_t get_function(int value) {
    switch (value) {
        case 0: return foo;
        case 1: return bar;
        default: return baz;
    }
}

void do_some_stuff(int a, int b) {
    my_function_ptr_t handler = get_function(a);
    handler(b);
}

根据数据库提案,foobarbaz 可以具有完全不同的调用约定。这要么意味着您实际上不再拥有有效的函数指针,要么需要在运行时访问数据库,并且编译器将生成在运行时检查数据库的代码,以便通过函数指针正确调用函数。这可能会产生一些严重的开销,但没有实际 yield 。

这绝不是详尽的原因列表,但主要思想是这样的:通过让每个函数期望参数位于不同的寄存器中,您可以用许多调用约定替换一个调用约定,而不会从中获得任何东西。

关于c++ - 使用用于传递参数的寄存器的预定顺序调用约定是否有特殊原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73682770/

相关文章:

c++ - 节点图编辑器布局算法

c++ - 这是单例模式的正确实现吗?

C 应用程序服务器/客户端在 docker 容器中不起作用

assembly - 为什么在使用堆栈框架操作时 ebx、esi 和 edi 不可用?

c++ - 读取 REG_BINARY 会损坏方法的参数

在 C 中使用 for 循环创建链表并赋值

c++ - "\'“转义序列的目的是什么?

c++ - 为虚拟析构函数指定的冲突类型属性

函数返回结构的调用约定

c++ - 如何防止为 unique_ptr 删除 px.get()