本机代码生成过程的高级概述
指针的表示
向目标文件发射代码时,指针将作为重定位发射。 反序列化代码将确保指向这些常量之一的任何对象被重新创建并包含正确的运行时指针。
否则,它们将作为字面常量发出。
若要发出其中一个对象,请调用 文献_pointer_val. 它将处理跟踪Julia值和LLVM全局,确保它们在当前运行时和反序列化后都有效。
当发射到目标文件时,这些全局变量作为引用存储在一个大文件中 [医]短节 表。 这允许解串器按索引引用它们,并实现类似于全局偏移表(GOT)的自定义手动机制来恢复它们。
函数指针的处理方式类似。 它们作为值存储在一个大的 [医]节 表。 与全局变量一样,这允许解串器按索引引用它们。
请注意 外,外 函数通过链接器中通常的符号解析机制单独处理,并带有名称。
也要注意 ccall 功能也是单独处理,通过手动得到和过程链接表(PLT).
中间值的表示
值在一个 jl_cgval_t 结构。 这表示一个R值,并包括足够的信息来确定如何将其分配或传递到某个地方。
它们是通过一个帮助器构造函数创建的,通常: mark_julia_type (对于即时值)和 马克*朱利亚*斯洛特 (对于值的指针)。
的功能 convert_julia_type 可以在任何两种类型之间转换。 它返回一个R值 cgval。typ 设置为 typ. 它会将对象强制转换为请求的表示,制作堆框,分配堆栈副本,并根据需要计算标记联合以更改表示。
相比之下 update_julia_type 会改变 cgval。typ 到 typ,只有当它可以以零成本完成(即不发出任何代码)。
工会代表
推断的联合类型可以通过标记类型表示进行堆栈分配。
需要能够处理标记联合的基本例程是:
*标记类型 *本地负载 *商店-本地 *isa *是 *emit_typeof *emit_sizeof *盒装 *拆箱 *专业cc-ret
通过使用这些原语来实现联合拆分,其他一切都应该可以在推理中处理。
标记联合的表示是作为一对 <void*union,byte selector>. 选择器是固定大小为 字节&0x7f,并将联合标记前126个isbit。 它将基于一的深度优先计数记录到内部isbits对象的类型联合中。 指数为零表示 工会* 实际上是一个标记的堆分配 jl_value_t*,并且需要被视为正常的盒装对象,而不是作为标记的联合。
选择器的高位(字节&0x80)可以进行测试,以确定是否 作废* 实际上是一个堆分配(jl_value_t*)盒,因此避免了重新分配一个盒的成本,同时保持了基于低位高效地处理联合拆分的能力。
它保证 字节&0x7f 是类型的确切测试,如果值可以用标记表示-它永远不会被标记 字节=0x80. 测试时不需要也测试type-tag 伊萨.
该 工会* 内存区域可以以_any_大小分配。 唯一的约束是它足够大,可以包含当前由 选择器. 它可能不够大,无法包含可以根据关联的Union type字段存储在那里的所有类型的union。 复制时使用适当的护理。
专门的呼叫约定签名表示
A jl_returninfo_t object描述任何可调用对象的专用调用约定详细信息。 它可以从任何(specTypes,rettype)对生成,例如CodeInstance或它们被声明的其他地方。 这是specptr的预期调用约定,但其他数据可能存储在那里。 只有当存储在那里的函数指针具有预期的专用调用约定时,才会在specsigflags中设置相应的标志以指示它是可用的。
如果方法的任何参数或返回类型可以被表示为无箱,而没有一个不能被表示为无箱(例如无界vararg),则会根据 [医]眼镜 和 [医]重型 价值观。
一般原则是:
*原始类型在int/float寄存器中传递。 *Vecelement类型的元组在向量寄存器中传递。 *结构在堆栈上传递。 *返回值的处理方式类似于参数,其大小截止值将通过隐藏的sret参数返回。
这方面的总逻辑是由 get_specsig_function 和 当之无愧.
此外,如果返回类型是联合,它可以作为一对值(指针和标记)返回。 如果联合值可以是堆栈分配的,那么存储它们的足够空间也将作为隐藏的第一个参数传递。 如果要返回的结构需要gc根,那么这些根的空间将作为隐藏的第二个参数传递。 返回的指针是否指向此空间、盒装对象或甚至其他常量内存取决于被调用者。