C 编译器是否保证两个八字节字段结构将在 SysV x64 上作为 INTEGER 传递?

Do C compilers guarantee two eightbyte field structs will be passed as INTEGER on SysV x64?

特别是在 the SysV x86-64 ABI

的上下文中

如果我有一个只有两个字段的结构,比如:

typedef struct {
    void *foo;
    void *bar;
} foobar_t;

然后我将它传递给定义如下的函数:

foobar_t example_function(foobar_t example_param);

ABI 似乎说每个八字节字段都应作为 INTEGER 传递给函数,因此 rdi == foorsi == bar。类似地,返回时我们应该能够使用 raxrdx,因为我们不需要 rdi 中的内存指针。如果 example_function 被简单地定义为:

foobar_t example_function(foobar_t example_param) {
    return example_param;
}

一个有效的汇编实现,忽略序言和结尾,应该是:

example_function:
   mov rax, rdi
   mov rdx, rsi
   ret

可以想象,一个智力缺陷的编译器可以用 NO_CLASS 填充填充结构,并以某种方式使该程序集无效。我想知道是否在任何地方都写下了只有两个八字节字段的结构 必须 以这种方式处理。

我的问题的更大背景是我正在编写一个简单的 C11 任务切换器来启发我自己。我主要基于 boost.context 和 this is exactly how boost passes two-field structs around。我想知道它是否在所有情况下都符合犹太洁食标准,或者 boost 是否有点作弊。

编译器同意结构布局以及它们如何作为函数参数按值传递是 ABI 的 key 部分。否则他们无法调用彼此的功能。

Hand-written asm 与 compiler-generated asm 没有区别;它不必来自同一编译器的同一版本即可正确互操作。这就是为什么稳定和正确的 ABI 如此重要的原因。

与 hand-written asm 的兼容性与与很久以前编译的机器代码的兼容性非常相似,并且多年来一直位于二进制共享库中。如果当时是正确的,那么现在也是正确的。除非源代码中的结构已更改 新编译的代码可以调用现有指令并被现有指令调用。


如果编译器不符合标准as-written,它就坏了。

或者更准确地说,如果它与 gcc 不匹配,它就坏了。如果标准措辞没有描述 gcc/clang/ICC 做什么,那么标准文档就被破坏了。

如果您的 x86-64 System V 编译器以 2 个寄存器之外的任何方式传递 2x void* 结构,则该编译器已损坏,不会 你的 hand-written asm.

(假设在我们到达 struct arg 之前没有很多早期的 args 用完 arg-passing 寄存器。)

The ABI seems to say that each eightbyte field should be passed as INTEGER to the function, therefore rdi == foo and rsi == bar.

同意,对于可从多个编译单元访问的 "global" 函数,参数结构被分成八字节的片段,第一个完全由 foo,第二个完全由 bar 填充。它们被归类为 INTEGER,因此分别传入 %rdi 和 %rsi。

Similarly, when returning we should be able to use rax and rdx, since we don't need a memory pointer in rdi.

我不同意你关于 %rdi 的观点,但我同意 return 值的成员在 %rax 和 %rdx 中 returned。

A valid assembly implementation, ignoring prologue and epilogue, would be: [...]

同意。

Conceivably, a mentally-deficient compiler could fill the struct with NO_CLASS padding and make that assembly invalid somehow. I'm wondering if it's written down anywhere that a struct with only two eightbyte fields must be handled this way.

生成符合 SysV x86-64 ABI 代码的编译器将使用已经讨论过的寄存器来传递参数和 returning return 值。这样的编译器当然没有义务完全按照您的描述实现函数体,但我没有看到您的担忧。是的,这些细节都写下来了。尽管您提供的具体案例未在您链接的 ABI 规范中明确描述,但上面讨论的所有行为都遵循该规范。这就是它的

生成行为不同的代码(针对全局函数)的编译器不是 mentally-deficient,它是 non-conforming.

The larger context to my question is that I'm writing a simple C11 task switcher for my own edification. I'm basing it largely on boost.context and this is exactly how boost passes two-field structs around. I want to know if it's kosher under all circumstances or if boost is cheating a little.

要确定 Boost 在您指向的代码中究竟做了什么,我需要进行比我准备花费更多的分析。请注意,不是您在 example_function 中显示的内容。但有理由假设 Boost 至少在尝试根据 ABI 实现其函数调用。