Engee 文档

机器代码生成过程概述

指针的表示

将代码传递到目标文件时,指针将以移动方式传递。 反序列化代码确保指向这些常量之一的任何对象都将被重新创建,并将包含正确的运行时指针。

否则,它们将作为文字常量输出。

要输出其中一个对象,请调用函数’literal_pointer_val'。 它将跟踪Julia和全局LLVM对象的值,确保它们对当前运行时环境和反序列化后都有效。

当您将对象传输到文件时,这些全局对象将作为引用存储在一个大的"gvals"表中。 在这种情况下,解串器可以通过索引引用它们,并为它们的恢复实现自定义手动机制,类似于全局偏移表(GOT)。

函数指针的处理方式类似。 它们作为值存储在一个大的"fvals"表中。 与全局对象一样,反序列化器可以通过索引引用它们。

请注意,'extern’函数是使用链接器中通常的符号解析机制单独处理的,带有名称。

请注意,'ccall’功能也使用手动使用的GOT表和过程布局表(PLT)单独处理。

中间值的表示

这些值在`jl_cgval_t`结构中传递。 它表示一个R值,并包含足够的信息来确定如何分配它或将其转移到某个地方。

值是使用其中一个辅助构造函数创建的,通常如下:mark_julia_type'(用于直接值)和`mark_julia_slot(用于指向值的指针)。

'Convert_julia_type’函数可以在任何两种类型之间进行转换。 它返回一个带有’cgval的R值。typ’具有值’typ'。 它将通过创建堆指针,分配堆栈副本以及根据需要计算标记连接来更改表示形式,从而将对象带到所需的表示形式。

相反’update_julia_type’函数将更改’cgval。只有当它可以以零成本完成(即不创建代码)时,才键入`to`type`。

协会的代表

联合的输出类型可以通过标记类型表示在堆栈上分配。

以下简单过程用于处理标记连接。

  • 标记-类型

  • 本地负荷

  • 商店-本地

  • 伊萨

  • emit_typeof

  • emit_sizeof

  • 盒装

  • 拆箱

  • 专业cc-ret

对于其他一切,应该可以使用这些基元在输出中处理以实现联合的分离。

将标记联合表示为一对'<void*union,byte selector>`。 选择器具有固定大小的’byte&0x7f',并标记了前126种类型的isbits的并集。 它将基于单元的数量深度写入isbits对象类型的联合中。 索引为零表示’union*'实际上是在堆`jl_value_t*`中分配的标记,应该被视为常规打包对象,而不是标记联合。

可以检查选择器的高位(byte&0x80)以确定`void*是否实际上是在堆上分配的指针(`jl_value_t*),这避免了重新分配块的成本,同时保持了基于低阶位高效处理拆分联接

保证’byte&0x7f’是一个准确的类型测试;如果一个值可以用标签表示,它永远不会被标记为’byte=0x80'。 在测试’isa`时,也不需要检查类型标签。

分配的内存区域’union*'可以有任何大小。 唯一的限制是它必须足够大,以包含选择器当前指定的数据。 它可能不够大,无法容纳可以根据union类型的关联字段存储在其中的所有类型的union。 复制应该小心。

提供专门的呼叫协议签名

`Jl_returninfo_t’对象描述了任何被调用对象的调用约定的详细信息。

如果方法的任何参数或返回类型可以以扩展形式表示,并且该方法不使用可变数量的参数,则将根据其"specTypes"和"rettype"字段为其提供优化的调用约定签名。

一般原则如下。

  • 原始类型在整数或浮点数的寄存器中传递。

  • VecElement类型在向量寄存器中传递。

  • 结构在堆栈上传递。

  • 返回值的处理类似于参数,其大小限制将使用隐藏的sret参数返回。

一般逻辑使用’get_specsig_function`和`deserves_sret’实现。

此外,如果返回的类型是一个联合,它可以作为一对值(指针和标签)返回。 如果联合值可以放在堆栈上,则足够的存储空间也将作为隐藏的第一个参数传递。 返回的指针将指向什么-这个空间,一个打包的对象,甚至其他永久内存取决于被调用的对象。