特别是在the SysV x86-64 ABI的背景下
如果我的结构只有两个字段,例如:
typedef struct {
void *foo;
void *bar;
} foobar_t;
然后将其传递给具有如下定义的函数:
foobar_t example_function(foobar_t example_param);
ABI似乎说每个8字节字段都应作为INTEGER
传递给函数,因此应将rdi == foo
和rsi == bar
传递给函数。同样,返回时,我们应该可以使用rax
和rdx
,因为我们不需要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
填充填充该结构,并使该程序集某种程度上无效。我想知道是否将这种只有两个8字节字段 的结构以这种方式处理。
我的问题的更大背景是,我正在为自己的写作编写一个简单的C11任务切换器。我主要基于boost.context和this is exactly how boost passes two-field structs around。我想知道它是否在所有情况下都是洁净的,或者增强是否在作弊。
答案 0 :(得分:3)
在结构布局以及如何通过值作为函数args传递值方面达成共识的编译器是ABI的 key 部分。否则,他们将无法调用彼此的函数。
手写asm与编译器生成的asm没有区别;它不必来自同一编译器的相同版本即可正确互操作。这就是为什么稳定而正确的ABI如此重要的原因。
与手写asm的兼容性与与很久以前编译的机器代码的兼容性非常相似,并且已经存在于二进制共享库中多年了。如果正确,那么现在正确。除非结构在源中更改了 ,否则新调用的代码可以调用并由现有指令调用。
如果编译器与编写的标准不符,则说明它已损坏。
或者更准确地说,如果它与gcc不匹配,那就坏了。并且,如果标准措辞没有描述gcc / clang / ICC的功能,那么标准文档就会被破坏。
如果您有一个x86-64系统V的编译器以2个寄存器以外的任何方式传递2x void*
结构,则该编译器已损坏,请 not 手写的asm。
(假设在到达结构arg之前,没有很多早期的arg占用arg传递寄存器。)
答案 1 :(得分:3)
ABI似乎说每个8字节字段都应按 整数,因此为
rdi == foo
和rsi == bar
。
同意,对于可从多个编译单元访问的“全局”函数, 将参数结构分解为8个字节,第一个完全由foo
填充,第二个完全填充由bar
。它们被归类为INTEGER,因此分别传入%rdi和%rsi。
类似地,返回时,我们应该可以使用
rax
和rdx
,因为我们不需要rdi
中的内存指针。
我不理解您关于%rdi的观点,但是我同意返回值的成员以%rax和%rdx返回。
忽略序言和结尾的有效程序集实现为:[...]
同意。
可以想象,弱智的编译器可能会用NO_CLASS填充填充结构,并使该程序集以某种方式无效。我想知道是否将这种只有两个8字节字段 的结构以这种方式处理。
生成符合SysV x86-64 ABI的代码的编译器将使用已经讨论过的寄存器来传递参数并返回返回值。这样的编译器当然没有义务完全按照您的描述来实现,但是我没有看到您的担忧。是的,这些详细信息已记录下来。尽管在链接的ABI规范中没有明确描述您提出的特定情况,但是上面讨论的所有行为均来自该规范。这就是它的要点。
一个生成行为不同的代码(针对全局函数)的编译器在精神上不是不足的,而是不符合。
我的问题的更大背景是我正在写一个简单的C11 我自己的任务切换器。我主要基于 boost.context,这正是boost通过两场结构的方式 周围。我想知道在所有情况下是否都是犹太洁食 助推力有点作弊。
要确定您所指向的代码中Boost到底在做什么,我需要花费更多的精力进行分析。请注意,您在example_function
中出现的不是 。但是可以合理地假设Boost至少尝试根据ABI实现其功能调用。