x86 的替代调用约定
Alternative calling convention for x86
是否可以在不修改编译器本身的情况下创建替代调用约定?例如,这个假设的调用约定可以用于优化
gcc / clang 了解将调用约定编译到编译器可执行文件中,而不是由配置文件定义,您可以在不重新编译编译器的情况下覆盖它。
所以是的,您需要修改编译器,但对于 open-source 编译器来说可能并不难。 gcc-internals 文档有 a whole chapter on stack layout and calling conventions,记录了如何向编译器描述调用约定。看起来这是用 C 源代码完成的,而不是用 machine-definition 文件向编译器描述 instruction-set 的方式用特殊语言完成的。
我没有检查 clang / LLVM 是如何定义调用约定的。
但在某些情况下,GNU C (gcc/clang/ICC) 的灵活性有限。不是很好,但也不是完全固定的。
在 x86 上,编译器知道多个现有的调用约定。例如x86-64 System V 和 Windows 具有不同的调用约定,您可以使用 __attribute__((ms_abi))
来声明使用 Windows 约定的函数,即使是为 non-Windows 目标编译时也是如此平台。在编译对函数的调用时,gcc 当然会根据为函数声明的调用约定生成代码来传递 args 和设置堆栈。
对于 32 位 x86,有很多约定。见 gcc docs for function attributes.
例如您可以使用 __attribute__((fastcall)) void foo(int a, int b) { ... }
来声明一个函数,该函数期望它在 ECX 和 EDX 中而不是在堆栈中作为参数。
甚至支持(在 32 位 x86 上)为调用约定设置寄存器参数的最大数量,__attribute((regparm(3)))
声明一个函数在寄存器中最多接受 3 个整数参数而不是堆。但是寄存器的顺序是不可配置的,它只使用EAX、ECX和EDX(大多数约定中call-clobbered的寄存器)。
您也可以使用 -mregparm=2
进行编译以全局设置它。
但是不支持使用 shadow space(如 x64 Windows)或 caller-pops(某些特定的 caller-pops 约定除外)来调用函数),或者使用 FLAGS 而不是整数寄存器返回的布尔结果,或者您可以使用 hand-written 程序集执行的任何其他操作。
MSVC 同样支持 vectorcall 与常规的一些 __declspec
选项。
是否可以在不修改编译器本身的情况下创建替代调用约定?例如,这个假设的调用约定可以用于优化
gcc / clang 了解将调用约定编译到编译器可执行文件中,而不是由配置文件定义,您可以在不重新编译编译器的情况下覆盖它。
所以是的,您需要修改编译器,但对于 open-source 编译器来说可能并不难。 gcc-internals 文档有 a whole chapter on stack layout and calling conventions,记录了如何向编译器描述调用约定。看起来这是用 C 源代码完成的,而不是用 machine-definition 文件向编译器描述 instruction-set 的方式用特殊语言完成的。
我没有检查 clang / LLVM 是如何定义调用约定的。
但在某些情况下,GNU C (gcc/clang/ICC) 的灵活性有限。不是很好,但也不是完全固定的。
在 x86 上,编译器知道多个现有的调用约定。例如x86-64 System V 和 Windows 具有不同的调用约定,您可以使用 __attribute__((ms_abi))
来声明使用 Windows 约定的函数,即使是为 non-Windows 目标编译时也是如此平台。在编译对函数的调用时,gcc 当然会根据为函数声明的调用约定生成代码来传递 args 和设置堆栈。
对于 32 位 x86,有很多约定。见 gcc docs for function attributes.
例如您可以使用 __attribute__((fastcall)) void foo(int a, int b) { ... }
来声明一个函数,该函数期望它在 ECX 和 EDX 中而不是在堆栈中作为参数。
甚至支持(在 32 位 x86 上)为调用约定设置寄存器参数的最大数量,__attribute((regparm(3)))
声明一个函数在寄存器中最多接受 3 个整数参数而不是堆。但是寄存器的顺序是不可配置的,它只使用EAX、ECX和EDX(大多数约定中call-clobbered的寄存器)。
您也可以使用 -mregparm=2
进行编译以全局设置它。
但是不支持使用 shadow space(如 x64 Windows)或 caller-pops(某些特定的 caller-pops 约定除外)来调用函数),或者使用 FLAGS 而不是整数寄存器返回的布尔结果,或者您可以使用 hand-written 程序集执行的任何其他操作。
MSVC 同样支持 vectorcall 与常规的一些 __declspec
选项。