当前位置: 首页 > 知识库问答 >
问题:

x86-64 System V abi-参数传递的参数分类

袁安志
2023-03-14

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

聚合(结构和数组)和联合类型的分类工作如下:

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

我不明白第(3),(4),(5)点,具体来说,我有以下问题:

问题1。在第(3)点中,作者所说的“每个被单独分类”是指“每个8字节”吗?如果是这样,那么我希望下面是对8字节分类的解释。

问题2.在第(4)点中,“物体的每个场”是指“由第(3)点(分离)产生的八字节的每个场”吗?

Q3。在第(4)点中,“总是考虑两个字段”中的“两个字段”是指两个连续的字段吗?

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

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

在第(5)点中,字段或八字节的“类之一”?

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

共有1个答案

范建华
2023-03-14

请参阅gcc的实现。

对第1点的澄清(针对“八个是打字错误,应该是两个”的评论):

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

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

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

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

问题1.在第(3)点中,作者所说的“每个都是单独分类的”是指“每个八字节”吗?

对在代码中,每个八字节称为一个字。

每个八字节初始化为class NO\u class。

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

问题2.在第(4)点中,“物体的每个场”是指“由第(3)点(分离)产生的八字节的每个场”吗?

不,它们是指结构/类、联合或数组元素的每个字段。这些在代码中的几个地方进行了处理,但您将看到类似以下循环的循环:

          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)点中,“总是考虑两个字段”中的“两个字段”是指两个连续的字段吗?

我认为“字段”在这里并不准确。而且不是连续的。它所做的是将迄今为止为每个单词确定的类与递归确定的对应于相同单词的字段的类合并在一起。见下文:

                      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开始(该字段所在的八字节),每个类都与该字段的递归调用确定的子类合并。

在第(4)点中,“结果类”是指对象的类,还是八字节的类,或者第二个考虑的字段的类,还是其他的类?

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

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

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

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

我希望“两个字段”在这一点上得到回答。例如,如果一个结构有一个字段,该类将为该8个字节初始化为NO_CLASS,然后对于该字段,它将被确定为,例如,INTEGER。然后在合并时,该类将成为INTEGER

在第(5)点中,字段或八字节的“类之一”?

八字节。类总是指八字节。

 类似资料:
  • 您可以将其他数据作为常规方法参数传递到后台任务。我再次使用下面的例子(希望没有让你厌恶): BackgroundJob.Enqueue(() => Console.WriteLine("Hello, {0}!", "world")); 在常规方法调用中,在执行后台作业期间, Console.WriteLine 方法将使用这些参数。为了参数传递进去,需要先序列化。 使用 了不起的 Newtonsof

  • 尽管应用中的大部分组件都依赖其他组件,但并不总是如此。同时 Windsor 用来查找满足依赖的正确组件的默认规则有时不得不进行调整。 如何做到这一点基于值的来源和获取值的地方。 Composition root - container.Resolve container.Resolve 方法有几个重载允许传递 IDictionary 作为参数(在这种情况下,建议使用 [Arguments] 类),

  • 我在MYSQL服务器上的y db中有一个存储过程。当我试图调用它时,它给出了错误: 通用域名格式。mysql。jdbc。例外。jdbc4。MySQLSyntaxErrorException:过程thu的参数数量不正确。生产者;预期为2,在组织中为0。springframework。jdbc。支持SQLExceptionSubclassTranslator。doTranslate(SQLExcept

  • 有时对一个类的某些方面进行 参数化(parameterize)是很有用的。例如, 你可能需要管理不同版本的 gem 软件包,既可以为每一种版本创建分离的单独的类, 也可以使用继承和覆盖,为一个类传递一个版本号作为参数。 操作步骤 声明参数作为如下类定义的一部分: class eventmachine( $version ) { package { "eventmachine": pro

  • 问题内容: 对不起这个基本问题。我想将一个切片作为参数传递给。像这样: 结果将是,但这显然不起作用。 (我要格式化的字符串比这要复杂的多,因此,简单的串联是不会做到的:) 所以问题是:如果我有数组,如何将其作为单独的参数传递给?或者:我可以调用在Go中传递参数列表的函数吗? 问题答案: 正如您在IRC上发现的那样,它将起作用: 您的原始代码无法正常工作,因为接受a 并且无法将其隐式或显式转换为该类

  • 就前面所讲,函数的基本内容已经完毕。但是,函数还有很多值得不断玩味的细节。这里进行阐述。 参数的传递 python中函数的参数通过赋值的方式来传递引用对象。下面总结通过总结常见的函数参数定义方式,来理解参数传递的流程。 def foo(p1,p2,p3,...) 这种方式最常见了,列出有限个数的参数,并且彼此之间用逗号隔开。在调用函数的时候,按照顺序以此对参数进行赋值,特备注意的是,参数的名字不重