提交 f9f6e3ad 编写于 作者: S Simonas Kazlauskas

[MIR] Reintroduce the unit temporary

An attempt to make loop body destination be optional, author implemented a pretty self contained
change and deemed it to be (much) uglier than the alternative of just keeping the unit temporary.
Having the temporary created lazily also has a nice property of not figuring in the MIR of
functions which do not use loops of any sort.
上级 1f4e317e
......@@ -146,11 +146,13 @@ pub fn into_expr(&mut self,
// start the loop
this.cfg.terminate(block, Terminator::Goto { target: loop_block });
this.in_loop_scope(loop_block, exit_block, |this| {
let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
// conduct the test, if necessary
let body_block;
let opt_cond_expr = opt_cond_expr; // FIXME rustc bug
if let Some(cond_expr) = opt_cond_expr {
// This loop has a condition, ergo its exit_block is reachable.
this.find_loop_scope(expr_span, None).might_break = true;
let loop_block_end;
let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
body_block = this.cfg.start_new_block();
......@@ -163,21 +165,22 @@ pub fn into_expr(&mut self,
body_block = loop_block;
}
// execute the body, branching back to the test
// We write body’s “return value” into the destination of loop. This is fine,
// because:
//
// * In Rust both loop expression and its body are required to have `()`
// as the “return value”;
// * The destination will be considered uninitialised (given it was
// uninitialised before the loop) during the first iteration, thus
// disallowing its use inside the body. Alternatively, if it was already
// initialised, the `destination` can only possibly have a value of `()`,
// therefore, “mutating” the destination during iteration is fine.
let body_block_end = unpack!(this.into(destination, body_block, body));
// The “return” value of the loop body must always be an unit, but we cannot
// reuse that as a “return” value of the whole loop expressions, because some
// loops are diverging (e.g. `loop {}`). Thus, we introduce a unit temporary as
// the destination for the loop body and assign the loop’s own “return” value
// immediately after the iteration is finished.
let tmp = this.get_unit_temp();
// Execute the body, branching back to the test.
let body_block_end = unpack!(this.into(&tmp, body_block, body));
this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
exit_block.unit()
})
});
// If the loop may reach its exit_block, we assign an empty tuple to the
// destination to keep the MIR well-formed.
if might_break {
this.cfg.push_assign_unit(exit_block, expr_span, destination);
}
exit_block.unit()
}
ExprKind::Assign { lhs, rhs } => {
// Note: we evaluate assignments right-to-left. This
......@@ -217,7 +220,10 @@ pub fn into_expr(&mut self,
|loop_scope| loop_scope.continue_block)
}
ExprKind::Break { label } => {
this.break_or_continue(expr_span, label, block, |loop_scope| loop_scope.break_block)
this.break_or_continue(expr_span, label, block, |loop_scope| {
loop_scope.might_break = true;
loop_scope.break_block
})
}
ExprKind::Return { value } => {
block = match value {
......@@ -303,11 +309,13 @@ fn break_or_continue<F>(&mut self,
block: BasicBlock,
exit_selector: F)
-> BlockAnd<()>
where F: FnOnce(&LoopScope) -> BasicBlock
where F: FnOnce(&mut LoopScope) -> BasicBlock
{
let loop_scope = self.find_loop_scope(span, label);
let exit_block = exit_selector(&loop_scope);
self.exit_scope(span, loop_scope.extent, block, exit_block);
let (exit_block, extent) = {
let loop_scope = self.find_loop_scope(span, label);
(exit_selector(loop_scope), loop_scope.extent)
};
self.exit_scope(span, extent, block, exit_block);
self.cfg.start_new_block().unit()
}
}
......@@ -26,6 +26,7 @@ pub struct Builder<'a, 'tcx: 'a> {
var_decls: Vec<VarDecl<'tcx>>,
var_indices: FnvHashMap<ast::NodeId, u32>,
temp_decls: Vec<TempDecl<'tcx>>,
unit_temp: Option<Lvalue<'tcx>>,
}
struct CFG<'tcx> {
......@@ -96,6 +97,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
temp_decls: vec![],
var_decls: vec![],
var_indices: FnvHashMap(),
unit_temp: None
};
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
......@@ -156,6 +158,18 @@ fn args_and_body(&mut self,
block.and(arg_decls)
})
}
fn get_unit_temp(&mut self) -> Lvalue<'tcx> {
match self.unit_temp {
Some(ref tmp) => tmp.clone(),
None => {
let ty = self.hir.unit_ty();
let tmp = self.temp(ty);
self.unit_temp = Some(tmp.clone());
tmp
}
}
}
}
///////////////////////////////////////////////////////////////////////////
......
......@@ -103,31 +103,41 @@ pub struct Scope<'tcx> {
#[derive(Clone, Debug)]
pub struct LoopScope {
pub extent: CodeExtent, // extent of the loop
pub continue_block: BasicBlock, // where to go on a `loop`
/// Extent of the loop
pub extent: CodeExtent,
/// Where the body of the loop begins
pub continue_block: BasicBlock,
/// Block to branch into when the loop terminates (either by being `break`-en out from, or by
/// having its condition to become false)
pub break_block: BasicBlock, // where to go on a `break
/// Indicates the reachability of the break_block for this loop
pub might_break: bool
}
impl<'a,'tcx> Builder<'a,'tcx> {
/// Start a loop scope, which tracks where `continue` and `break`
/// should branch to. See module comment for more details.
pub fn in_loop_scope<F, R>(&mut self,
///
/// Returns the might_break attribute of the LoopScope used.
pub fn in_loop_scope<F>(&mut self,
loop_block: BasicBlock,
break_block: BasicBlock,
f: F)
-> BlockAnd<R>
where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>
-> bool
where F: FnOnce(&mut Builder<'a, 'tcx>)
{
let extent = self.extent_of_innermost_scope();
let loop_scope = LoopScope {
extent: extent.clone(),
continue_block: loop_block,
break_block: break_block,
might_break: false
};
self.loop_scopes.push(loop_scope);
let r = f(self);
assert!(self.loop_scopes.pop().unwrap().extent == extent);
r
f(self);
let loop_scope = self.loop_scopes.pop().unwrap();
assert!(loop_scope.extent == extent);
loop_scope.might_break
}
/// Convenience wrapper that pushes a scope and then executes `f`
......@@ -181,28 +191,21 @@ pub fn pop_scope(&mut self, extent: CodeExtent, block: BasicBlock) {
pub fn find_loop_scope(&mut self,
span: Span,
label: Option<CodeExtent>)
-> LoopScope {
let loop_scope =
match label {
None => {
// no label? return the innermost loop scope
self.loop_scopes.iter()
.rev()
.next()
}
Some(label) => {
// otherwise, find the loop-scope with the correct id
self.loop_scopes.iter()
.rev()
.filter(|loop_scope| loop_scope.extent == label)
.next()
}
};
match loop_scope {
Some(loop_scope) => loop_scope.clone(),
None => self.hir.span_bug(span, "no enclosing loop scope found?"),
}
-> &mut LoopScope {
let Builder { ref mut loop_scopes, ref mut hir, .. } = *self;
match label {
None => {
// no label? return the innermost loop scope
loop_scopes.iter_mut().rev().next()
}
Some(label) => {
// otherwise, find the loop-scope with the correct id
loop_scopes.iter_mut()
.rev()
.filter(|loop_scope| loop_scope.extent == label)
.next()
}
}.unwrap_or_else(|| hir.span_bug(span, "no enclosing loop scope found?"))
}
/// Branch out of `block` to `target`, exiting all scopes up to
......@@ -214,20 +217,19 @@ pub fn exit_scope(&mut self,
extent: CodeExtent,
block: BasicBlock,
target: BasicBlock) {
let popped_scopes =
match self.scopes.iter().rev().position(|scope| scope.extent == extent) {
Some(p) => p + 1,
None => self.hir.span_bug(span, &format!("extent {:?} does not enclose",
extent)),
};
for scope in self.scopes.iter_mut().rev().take(popped_scopes) {
let Builder { ref mut scopes, ref mut cfg, ref mut hir, .. } = *self;
let scope_count = 1 + scopes.iter().rev().position(|scope| scope.extent == extent)
.unwrap_or_else(||{
hir.span_bug(span, &format!("extent {:?} does not enclose", extent))
});
for scope in scopes.iter_mut().rev().take(scope_count) {
for &(kind, drop_span, ref lvalue) in &scope.drops {
self.cfg.push_drop(block, drop_span, kind, lvalue);
cfg.push_drop(block, drop_span, kind, lvalue);
}
}
self.cfg.terminate(block, Terminator::Goto { target: target });
cfg.terminate(block, Terminator::Goto { target: target });
}
/// Creates a path that performs all required cleanup for unwinding.
......
......@@ -58,6 +58,10 @@ pub fn bool_ty(&mut self) -> Ty<'tcx> {
self.tcx.types.bool
}
pub fn unit_ty(&mut self) -> Ty<'tcx> {
self.tcx.mk_nil()
}
pub fn str_literal(&mut self, value: token::InternedString) -> Literal<'tcx> {
Literal::Value { value: ConstVal::Str(value) }
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册