c-x86-64系统V abi-参数传递的参数分类



第3.2.3节中的x86_64 System V ABI指定函数调用的哪些参数去往哪些寄存器,哪些参数被推送到堆栈上。我很难理解骨料分类的算法,它说(突出显示是我的):

聚合(结构和数组)和并集类型的分类如下:

  1. 如果对象的大小大于八个八字节,或者它包含未对齐的字段,则它具有类MEMORY
  2. 如果C++对象对于调用来说是非平凡的,如C++ABI13中所指定的,则通过不可见引用传递(该对象在参数列表中由类为INTEGER的指针替换)
  3. 如果聚合的大小超过一个八字节,则每个都将被单独分类。每个八字节被初始化为类NO_class
  4. 对象的每个字段都是递归分类的,因此总是考虑两个字段结果类是根据八字节中字段的类计算的:(a)如果两个类相等,这就是结果类。(b) 如果其中一个类是NO_CLASS,则生成的类是另一个类。(c) 如果其中一个类是MEMORY,那么结果就是MEMORY类。(d) 如果其中一个类是INTEGER,那么结果就是INTEGER。(e) 如果其中一个类是X87、X87UP、COMPLEX_X87类,则使用MEMORY作为类。(f) 否则使用SSE类
  5. 然后进行合并后的清理:(a)如果其中一个类是MEMORY,则整个参数在内存中传递。(b) 如果X87UP前面没有X87,那么整个参数将在内存中传递。(c) 如果聚合的大小超过两个八字节,并且第一个八字节不是SSE,或者任何其他八字节都不是SSEUP,那么整个参数将在内存中传递。(d) 如果SSEUP前面没有SSE或SSEUP,则转换为SSE

我不理解第(3)、(4)和(5)点。具体来说,我有以下问题:

Q1.在点(3)中;每个被单独分类";,作者的意思是";每个八字节";?如果是这样的话,那么我希望下面是对八字节分类的解释。

Q2。在点(4)中;对象的每个字段";,他们的意思是";八字节的每个字段是点(3)的结果?

Q3.在点(4)中;两个字段";在";总是考虑两个字段";,它们是指两个连续的字段吗?

Q4。在点(4)中;得到的类";,它们是指对象的类、八字节的类、第二个考虑的字段的类还是其他的类?在最后一种情况下,结果类在哪里使用?这是否意味着算法保持第一个字段的字段不变,然后迭代计算下一个字段的类,直到我们得到八字节中所有字段的类?或者这意味着我们的算法同时处理两个字段?

问题5.在第(4)点中,如果只有一个字段呢?还是偶数个字段?

Q6.在点(5)中;类之一";字段,还是八字节?

如果有人能提供更正式/更精确的东西,例如伪代码或流程图,那将是理想的选择。

请参阅gcc实现。

对第1点的澄清(回应评论称"八是拼写错误,应该是两"):

  1. 如果对象的大小大于八个八字节,或者它包含未对齐的字段,则它具有类MEMORY
/* On x86-64 we pass structures larger than 64 bytes on the stack.  */
if (bytes > 64)
return 0;

该函数返回用于参数的寄存器数量,零表示应使用内存。

(稍后,在分析之后,如果有两个以上的八字节,则只有当第一个是SSE,其余是SSEUP时,才使用寄存器,如第5节所述。(c):

(c)如果聚合的大小超过两个八字节,并且第一个八字节不是SSE,或者任何其他八字节都不是SSEUP,则整个参数将在内存中传递。)


Q1。在点(3)中;每个被单独分类";,作者的意思是";每个八字节";?

是。在代码中,每个八字节都被称为word

每个八字节初始化为类NO_class。

int words = CEIL (bytes + (bit_offset % 64) / 8, UNITS_PER_WORD);
// ...
for (i = 0; i < words; i++)
classes[i] = X86_64_NO_CLASS;

Q2。在点(4)中;对象的每个字段";,他们的意思是";八字节的每个字段是点(3)的结果?

否,它们表示结构/类、联合或数组元素的每个字段。这些在代码中的几个地方进行了处理,但您将看到for循环,如:

for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))

这就是为什么它是递归的。字段本身可以是聚合类型。整个逻辑从每个字段开始应用,递归函数:

  • 要么返回0,意味着整个过程都在内存中传递
  • 或者它返回将要使用的寄存器数量(八字节)和每个寄存器的类(通过嵌套字段的递归将终止于具有非聚合类型的字段)
num = classify_argument (TYPE_MODE (type), type,
subclasses,
(int_bit_position (field)
+ bit_offset) % 512);
if (!num)
return 0;

Q3。在点(4)中;两个字段";在";总是考虑两个字段";,它们是指两个连续的字段吗?

我不认为"字段";在这里是准确的。而且不是连续的。它所做的是将迄今为止为每个word确定的类与为对应于相同words的字段递归确定的类进行合并。见下文:

pos = (int_bit_position (field)
+ (bit_offset % 64)) / 8 / 8;
for (i = 0; i < num && (i + pos) < words; i++)
classes[i + pos]
= merge_classes (subclasses[i], classes[i + pos]);

pos(该字段所在的八字节)开始,每个类都与该字段的递归调用所确定的子类合并。


Q4。在点(4)中;得到的类";,它们是指对象的类、八字节的类、第二个考虑的字段的类还是其他的类?

现在描述merge_classes函数,它接受两个类并返回八字节的合并类。我们在字段上迭代,但类是针对八字节的。

在最后一种情况下,结果类在哪里使用?

每个类将确定相应寄存器的类型(GPR/SSE/X87等)


Q5.在第(4)点中,如果只有一个字段怎么办?还是偶数个字段?

我希望"双字段";在这一点上得到了回答。例如,如果一个结构有一个字段,那么该类将为该八字节初始化为NO_CLASS,那么该字段将被确定为INTEGER。然后在合并时,类将变为INTEGER


Q6.在点(5)中;类之一";字段,还是八字节?

八字节的。类总是引用一个八字节。

最新更新