Jan*_*šil 5 c linux x86-64 abi calling-convention
第 3.2.3 节中的x86_64 System V ABI指定函数调用的哪些参数进入哪些寄存器以及哪些被压入堆栈。我很难理解聚合分类的算法,它说(突出显示的是我的):
\n\n\n聚合(结构体和数组)和联合类型的分类工作原理如下:
\n\n
\n- 如果对象的大小大于八个字节,或者包含未对齐的字段,则它具有 MEMORY 类。
\n- 如果 C++ 对象对于调用而言非常重要(如 C++ ABI13 中所指定),则它通过不可见引用传递(该对象在参数列表中被具有类 INTEGER 的指针替换)。
\n- 如果聚合的大小超过一个八字节,则每个字节将被单独分类。每个八字节都被初始化为 NO_CLASS 类。
\n- 对象的每个字段都被递归分类,以便始终考虑 两个字段。由此产生的类是根据八字节中字段的类来计算的: (a) 如果两个类相等,则这就是结果类。(b) 如果其中一个类是 NO_CLASS,则结果类是另一个类。(c) 如果其中一个类是 MEMORY,则结果是 MEMORY 类。(d) 如果其中一个类是 INTEGER,则结果是 MEMORY 类。是整数。(e) 如果类之一是 X87、X87UP、COMPLEX_X87 类,则使用 MEMORY 作为类。 (f) 否则使用类 SSE。
\n- 然后进行合并后清理: (a) 如果其中一个类是 MEMORY,则整个参数将在内存中传递。(b) 如果 X87UP 前面没有 X87,则整个参数将在内存中传递。(c) 如果聚合的大小超过两个八字节,并且第一个八字节是\xe2\x80\x99t SSE 或任何其他八字节是\xe2\x80\x99t SSEUP,则整个参数将在内存中传递。(d) 如果 SSEUP 前面没有 SSE 或 SSEUP,则转换为 SSE
\n
我不明白第(3)、(4)和(5)点。具体来说,我有以下问题:
\nQ1. 在第(3)点中,“每个单独分类”,作者的意思是“每个八字节”吗?如果是这样,那么我希望接下来是八字节分类的解释。
\nQ2。在第 (4) 点中,“对象的每个字段”是否表示“作为第 (3) 点(分隔)结果的八字节的每个字段?
\nQ3。在第(4)点中,“总是考虑两个字段”中的“两个字段”是否意味着两个连续的字段?
\nQ4。在第(4)点中,“结果类”是指对象的类,还是八字节的类,还是第二个考虑的字段的类,还是其他东西的类?在最后一种情况下,生成的类在哪里使用?这是否意味着算法保持第一个字段的字段不变,然后迭代计算下一个字段的类,直到我们获得八字节中所有字段的类?或者这是否意味着我们的算法同时处理两个字段?
\nQ5. 第(4)点中,如果只有一个字段怎么办?或者偶数个字段?
\nQ6. 在第 (5) 点中,字段或八字节的“类之一”?
\n如果有人可以提供更正式/更精确的东西 - 例如伪代码或流程图 - 那将是理想的。
\n查看实现gcc。
对第 1 点的澄清(回应“八是一个拼写错误,应该是二”的评论):
- 如果对象的大小大于八个字节,或者包含未对齐的字段,则它具有 MEMORY 类。
/* On x86-64 we pass structures larger than 64 bytes on the stack. */
if (bytes > 64)
return 0;
Run Code Online (Sandbox Code Playgroud)
该函数返回用于参数的寄存器数量,零表示应使用内存。
(后来经过分析,如果超过两个八字节,只有第一个是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;
Run Code Online (Sandbox Code Playgroud)
Q2。在第 (4) 点中,“对象的每个字段”是否表示“作为第 (3) 点(分隔)结果的八字节的每个字段?
不,它们指的是结构/类、联合或数组元素的每个字段。这些是在代码中的几个地方处理的,但您会看到for如下循环:
for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
Run Code Online (Sandbox Code Playgroud)
这就是为什么它是递归的。字段本身可以是聚合类型。整个逻辑从每个字段开始应用,递归函数:
num = classify_argument (TYPE_MODE (type), type,
subclasses,
(int_bit_position (field)
+ bit_offset) % 512);
if (!num)
return 0;
Run Code Online (Sandbox Code Playgroud)
Q3。在第(4)点中,“总是考虑两个字段”中的“两个字段”是否意味着两个连续的字段?
我认为“字段”在这里不准确。而且不是连续的。它所做的是将迄今为止为每个 确定的类word与为对应于相同 s 的字段递归确定的类进行合并word。见下文:
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]);
Run Code Online (Sandbox Code Playgroud)
pos从(该字段所在的八字节)开始,每个类都会与由该字段的递归调用确定的子类合并。
Q4。在第(4)点中,“结果类”是指对象的类,还是八字节的类,还是第二个考虑的字段的类,还是其他东西的类?
现在正在描述该merge_classes函数,该函数采用两个类并返回八字节的合并类。我们正在迭代字段,但类是八字节的。
在最后一种情况下,生成的类在哪里使用?
每个类都会确定对应寄存器的类型(GPR/SSE/X87等)。
Q5. 第(4)点中,如果只有一个字段怎么办?或者偶数个字段?
我希望“两个领域”在这一点上得到解答。例如,如果一个结构体有一个字段,则该类将针对该八字节初始化为NO_CLASS,然后对于该字段,它将被确定为INTEGER。然后在合并时,类将变成INTEGER.
Q6. 在第 (5) 点中,字段或八字节的“类之一”?
八字节的。类总是指八字节。