HAIR 和 MIR 的构建
从 HIR lower到 MIR 的过程会在下面(可能不完整)这些item上进行:
- 函数体和闭包体
static
和const
的初始化- 枚举判别的初始化
- 任何类型的胶水和补充代码
- Tuple结构体的初始化函数
- Drop 代码 (
Drop::drop
函数不会直接被调用) - 没有显式
Drop
实现的对象的drop
Lowering是通过调用mir_built
查询触发的。
HIR和MIR之间有一个中间表示,称为HAIR,这仅在lowering过程中使用。
HAIR的最重要特征是各种调整(在没有显式语法的情况下发生),例如隐式类型转换,自动解引用,自动引用和重载方法调用,已成为显式强制转换,解引用操作,引用操作和具体的函数调用。
HAIR的数据类型与HIR数据类型类似,
但是例如-x
是hair::ExprKind::Neg(hir::Expr)
,
而不是hair::ExprKind::Neg(hair::Expr)
。
这种shallow的特性使HAIR
能够表示HIR具有的所有数据类型,而不必创建整个HIR的副本。
MIR lowering 将首先将最上面的表达式从HIR转换为HAIR(在rustc_mir_build::hair::cx::expr
中),然后递归处理HAIR表达式。
Lowering会为函数签名中指定的每个参数创建局部变量。
接下来,它为指定的每个绑定创建局部变量(例如, (a, b): (i32, String)
)产生3个绑定,一个用于参数,两个用于绑定。
接下来,它生成字段访问,该访问从参数读取字段并将其值写入绑定变量。
在解决了初始化的情况下,lowering为函数体递归生成MIR( Block
表达式)并将结果写入RETURN_PLACE
。
unpack!
所有东西
生成MIR的函数有两种模式。 第一种情况,如果该函数仅生成语句,则它将以基本块作为参数,这些语句应放入该基本块。 然后可以正常返回结果:
fn generate_some_mir(&mut self, block: BasicBlock) -> ResultType {
...
}
但是还有其他一些函数会生成新的基本块。
例如,lowering像if foo { 22 } else { 44 }
这样的表达式需要生成一个小的“菱形图”。
在这种情况下,函数将在其代码开始处使用一个基本块,并在代码生成结束时返回一个(可能)新的基本块。
BlockAnd
类型用于表示此类情况:
fn generate_more_mir(&mut self, block: BasicBlock) -> BlockAnd<ResultType> {
...
}
当您调用这些函数时,通常有一个局部变量block
,它实际上是一个“光标”。 它代表了我们要添加新的MIR的位置。
当调用generate_more_mir
时,您会想更新该光标。
您可以手动执行此操作,但这很繁琐:
let mut block;
let v = match self.generate_more_mir(..) {
BlockAnd { block: new_block, value: v } => {
block = new_block;
v
}
};
For this reason, we offer a macro that lets you write
let v = unpack!(block = self.generate_more_mir(...))
.
It simply extracts the new block and overwrites the
variable block
that you named in the unpack!
.
因此,我们提供了一个宏,可让您编写
let v = unpack!(block = self.generate_more_mir(...))
。
它简单地提取新的块并覆盖在unpack!
中指明的变量block
。
将表达式 Lowering 到 MIR
本质上一个表达式可以有四种表示形式:
Place
指一个(或一部分)已经存在的内存地址(本地,静态,或者提升过的)Rvalue
是可以给一个Place
赋值的东西Operand
是一个给像+
这样的运算符或者一个函数调用的参数- 一个存放了一个值的拷贝的临时变量
下图描绘了表示之间的交互的一般概述:
我们首先将函数体lowering到一个 Rvalue
,这样我们就可以为 RETURN_PLACE
创建一个赋值,
这个Rvalue
的lowering反过来会触发其参数的Operand
lowering(如果有的话)
lowering Operaper
会产生一个const
操作数,或者移动/复制出Place
,从而触发Place
lowering。
如果降低的表达式包含操作,则lowering到Place
的表达式可以触发创建一个临时变量。
这是蛇咬自己的尾巴的地方,我们需要触发Rvalue
lowering,以将表达式的值写入本地变量。
Operator lowering
内置类型的运算符不会lower为函数调用(这将导致无限递归调用,因为trait包含了操作本身)。相反,存在用于二元和一元运算符和索引运算的Rvalue
。
这些Rvalue
稍后将生成为llvm基本操作或llvm内部函数。
所有其他类型的运算符都被lower为对运算符对应特征的impl
的函数调用。
无论采用哪种lower方式,运算符的参数都会lower为Operand
。
这意味着所有参数都是常量,或者引用局部或静态位置中已经存在的值。
方法调用的 lowering
方法调用被降低到与一般函数调用相同的TerminatorKind
。
在MIR中,方法调用和一般函数调用之间不再存在差异。
条件
不带字段变量的enum
的if
条件判断和match
语句都会被lower为TerminatorKind::SwitchInt
。
每个可能的值(如果为if
条件判断,则对应的值为0
和1
)都有一个对应的BasicBlock
。
分支的参数是表示if条件值的Operand
。
模式匹配
具有字段enum
的match
语句也被lower为TerminatorKind::SwitchInt
,但是操作数是一个Place
,可以在其中找到该值的判别式。
这通常涉及将判别式读取为新的临时变量。
聚合构造
任何类型的聚合值(例如结构或元组)都是通过Rvalue::Aggregate
建立的。
所有字段都lower为Operator
。
从本质上讲,这等效于每个聚合字段都会有一个赋值语句,再加上一个对enum
的判别式的赋值。