GCC文档中的x86 Function Attributes表示:

在32位和64位x86目标上,可以使用ABI属性来指示函数应使用哪个调用约定.ms_abi属性告诉编译器使用Microsoft ABI,而sysv_abi属性告诉编译器使用System V ELF ABI,它在GNU/Linux和其他系统上使用.默认情况下,以Windows为目标时使用Microsoft ABI.在所有其他系统上,默认值为System V ELF ABI.

但请考虑以下C代码:

#include <assert.h>

#ifdef _MSC_VER
#define MS_ABI
#else
#define MS_ABI __attribute__((__ms_abi__))
#endif

typedef struct {
    void *x, *y;
} foo;

static_assert(sizeof(foo) == 8, "foo must be an 8-byte structure");

foo MS_ABI f(void *x, void *y) {
    foo rv;
    rv.x = x;
    rv.y = y;
    return rv;
}

gcc -O2 -m32将其编译为:

f:
        mov     eax, DWORD PTR [esp+4]
        mov     edx, DWORD PTR [esp+8]
        mov     DWORD PTR [eax], edx
        mov     edx, DWORD PTR [esp+12]
        mov     DWORD PTR [eax+4], edx
        ret

cl /O2将其编译为:

_x$ = 8                                       ; size = 4
_y$ = 12                                                ; size = 4
_f      PROC                                                ; COMDAT
        mov     eax, DWORD PTR _x$[esp-4]
        mov     edx, DWORD PTR _y$[esp-4]
        ret     0
_f      ENDP

Godbolt link

这些显然使用了不兼容的调用约定.MSDN上的Argument Passing and Naming Conventions表示:

返回值也被加宽到32位,并在EAX寄存器中返回,但8字节 struct 除外,它在EDX:EAX寄存器对中返回.较大的 struct 在EAX寄存器中作为指向隐藏返回 struct 的指针返回.

这意味着MSVC是正确的.那么,为什么GCC使用指向隐藏返回 struct 的指针方法,即使返回值是8字节 struct ?这是GCC中的一个bug,还是我不能像我想的那样使用ms_abi

推荐答案

对我来说,那辆车似乎有问题.i386 SysV只在寄存器中返回int64_t,而不是相同大小的 struct ,但可能GCC忘记了在ms_abi中考虑这一点.gcc -mabi=ms(docs)存在相同问题.

通常情况下,即使-mabi=ms也不会在 struct 布局不同的情况下更改 struct 布局,或者对于x86-64,也不会将long设置为32位类型.但是您的 struct 在两个ABI中都有相同的布局,因此您希望它以MSVC调用方希望的方式返回.但这并没有发生.

这不是我第一次听说__attribute__((ms_abi))-mabi=ms中有bug.但你使用它是正确的;在64位代码中,这将对它查找的参数传递寄存器产生影响.

Clang生成的asm与GCC相同,但这并不重要,因为它警告the 'ms_abi' calling convention is not supported for this target.这是我们期望从i386 SysV获得的asm.(而且螺栓没有叮当声)


因此,感谢您向GCC报告它为bug #105932.

(有趣的是,当它 Select 使用SSE或AVX时,它会分别加载两个dword堆栈参数并将它们混洗在一起,而不是movq次加载.不过,我想如果调用者不使用单个64位存储来写入参数,这可能会避免存储转发暂停.)

C++相关问答推荐

通过管道将一个子系统的标准输出发送到另一个子系统的标准输出

如何使用Python C API实现多线程程序?

我编译了一个新的c程序,并收到以下错误

识别和处理c中整数溢出的最佳方法?

如何捕捉只有换行符或空格字符缓冲区的边缘大小写

如何使用[BTStack]BLE发送大型(>;2kb)信息包

这个C程序在工作中途停止获取输入.我收到分段故障(核心转储).我还是不知道问题出在哪里

为什么指针运算会产生错误的结果?

cairo 剪辑区域是否存在多个矩形?

接受任何参数的函数指针是否与接受不同参数的函数兼容

如何在C-函数中混合使用C代码和ASM?

将多项式从文件.txt加载到终端时出现问题

Wcstok导致分段故障

运行时错误:在索引数组时加载类型为';char';`的空指针

为什么GCC-O1优化破解了这个代码,为了一个GameBoy高级只读存储器而修改了VRAM的循环?

如何将大写/小写土耳其字母相互转换?

不兼容的整数到指针转换传递';char';到类型';常量字符*

如何在C中计算包含递增和递减运算符的逻辑表达式?

";错误:寄存器的使用无效;当使用-masm=intel;在gcc中,但在AT&;T模式

如何用用户输入的多个字符串填充数组?