是否可以在不修改编译器本身的情况下创建替代调用约定?例如,这个假设的调用约定可以用于优化
答案 0 :(得分:1)
gcc / clang具有编译到编译器可执行文件中的调用约定的知识,不是由您可以覆盖的配置文件定义的,无需重新编译编译器。
所以是的,您需要修改编译器,但对于开源编译器来说可能并不难。 gcc-internals文档有a whole chapter on stack layout and calling conventions,记录了如何向编译器描述调用约定。看起来这是用C源代码完成的,而不是像机器定义文件描述编译器指令集那样的特殊语言。
我没有检查clang / LLVM如何定义调用约定。
但在GNU C(gcc / clang / ICC)的某些情况下,灵活性有限。它不是很好,但并不完全固定。
在x86上,编译器知道多个现有的调用约定。例如x86-64 System V和Windows具有不同的调用约定,您可以使用__attribute__((ms_abi))
声明使用Windows约定的函数,即使是针对非Windows目标平台进行编译也是如此。在编译对函数的调用时,gcc当然会生成代码来传递args并根据为函数声明的调用约定来设置堆栈。
对于32位x86,有许多约定。请参阅gcc docs for function attributes。
e.g。您可以使用__attribute__((fastcall)) void foo(int a, int b) { ... }
声明一个函数,该函数在ECX和EDX中声明它是args,而不是在堆栈上。
甚至支持(在32位x86上)为调用约定设置寄存器参数的最大数量,__attribute((regparm(3)))
声明一个函数在寄存器而不是堆栈中最多占用3个整数args。但是寄存器的顺序是不可配置的,它只使用EAX,ECX和EDX(在大多数约定中被调用的寄存器)。
您也可以使用-mregparm=2
进行编译以全局设置。
但是不支持声明使用阴影空间(如x64 Windows)调用函数,或者使用调用者弹出调用(除了一些特定的调用者 - 弹出式约定),或者使用FLAGS中返回的布尔结果而不是整数寄存器,或者你可以用手写装配做的任何其他事情。
MSVC同样支持vectorcall与常规的一些__declspec
选项。