rewrite scope drop to be iterative
while I'm at it, remove the "extra caching" that I was doing for no good reason except laziness. Basically before I was caching at each scope in the chain, but there's not really a reason to do that, since the cached entry point at level N is always equal to the last cached exit point from level N-1.
This commit is contained in:
parent
a276e755e7
commit
0769865f7f
@ -47,6 +47,10 @@ pub struct Builder<'a, 'tcx: 'a> {
|
||||
var_indices: FnvHashMap<ast::NodeId, u32>,
|
||||
temp_decls: Vec<TempDecl<'tcx>>,
|
||||
unit_temp: Option<Lvalue<'tcx>>,
|
||||
|
||||
// cached block with a RESUME terminator; we create this at the
|
||||
// first panic
|
||||
cached_resume_block: Option<BasicBlock>,
|
||||
}
|
||||
|
||||
struct CFG<'tcx> {
|
||||
@ -175,6 +179,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
|
||||
var_decls: vec![],
|
||||
var_indices: FnvHashMap(),
|
||||
unit_temp: None,
|
||||
cached_resume_block: None,
|
||||
};
|
||||
|
||||
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
|
||||
|
@ -456,21 +456,41 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||
/// See module comment for more details. None indicates there’s no
|
||||
/// cleanup to do at this point.
|
||||
pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
|
||||
if self.scopes.is_empty() {
|
||||
if self.scopes.iter().all(|scope| scope.drops.is_empty() && scope.free.is_none()) {
|
||||
return None;
|
||||
}
|
||||
let unit_temp = self.get_unit_temp();
|
||||
let Builder { ref mut hir, ref mut cfg, ref mut scopes, .. } = *self;
|
||||
assert!(!self.scopes.is_empty()); // or `all` above would be true
|
||||
|
||||
// Given an array of scopes, we generate these from the outermost scope to the innermost
|
||||
// one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will
|
||||
// generate B0 <- B1 <- B2 in left-to-right order. Control flow of the generated blocks
|
||||
// always ends up at a block with the Resume terminator.
|
||||
if scopes.iter().any(|scope| !scope.drops.is_empty() || scope.free.is_some()) {
|
||||
Some(build_diverge_scope(hir.tcx(), self.fn_span, cfg, &unit_temp, scopes))
|
||||
let unit_temp = self.get_unit_temp();
|
||||
let Builder { ref mut hir, ref mut cfg, ref mut scopes,
|
||||
ref mut cached_resume_block, .. } = *self;
|
||||
|
||||
// Build up the drops in **reverse** order. The end result will
|
||||
// look like:
|
||||
//
|
||||
// scopes[n] -> scopes[n-1] -> ... -> scopes[0]
|
||||
//
|
||||
// However, we build this in **reverse order**. That is, we
|
||||
// process scopes[0], then scopes[1], etc, pointing each one at
|
||||
// the result generates from the one before. Along the way, we
|
||||
// store caches. If everything is cached, we'll just walk right
|
||||
// to left reading the cached results but never created anything.
|
||||
|
||||
// To start, create the resume terminator.
|
||||
let mut target = if let Some(target) = *cached_resume_block {
|
||||
target
|
||||
} else {
|
||||
None
|
||||
let resumeblk = cfg.start_new_cleanup_block();
|
||||
cfg.terminate(resumeblk, scopes[0].id, self.fn_span, TerminatorKind::Resume);
|
||||
*cached_resume_block = Some(resumeblk);
|
||||
resumeblk
|
||||
};
|
||||
|
||||
for scope in scopes {
|
||||
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target);
|
||||
}
|
||||
|
||||
Some(target)
|
||||
}
|
||||
|
||||
/// Utility function for *non*-scope code to build their own drops
|
||||
@ -640,43 +660,25 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
|
||||
}
|
||||
|
||||
fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>,
|
||||
fn_span: Span,
|
||||
cfg: &mut CFG<'tcx>,
|
||||
unit_temp: &Lvalue<'tcx>,
|
||||
scopes: &mut [Scope<'tcx>])
|
||||
scope: &mut Scope<'tcx>,
|
||||
mut target: BasicBlock)
|
||||
-> BasicBlock
|
||||
{
|
||||
assert!(scopes.len() >= 1);
|
||||
|
||||
// Build up the drops in **reverse** order. The end result will
|
||||
// look like:
|
||||
//
|
||||
// [drops[n]] -...-> [drops[0]] -> [Free] -> [scopes[..n-1]]
|
||||
// [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
|
||||
// | |
|
||||
// +------------------------------------+
|
||||
// code for scopes[n]
|
||||
// code for scope
|
||||
//
|
||||
// The code in this function reads from right to left. At each
|
||||
// point, we check for cached blocks representing the
|
||||
// remainder. If everything is cached, we'll just walk right to
|
||||
// left reading the cached results but never created anything.
|
||||
|
||||
// To start, translate scopes[1..].
|
||||
let (scope, earlier_scopes) = scopes.split_last_mut().unwrap();
|
||||
let mut target = if let Some(cached_block) = scope.cached_block {
|
||||
cached_block
|
||||
} else if earlier_scopes.is_empty() {
|
||||
// Diverging from the root scope creates a RESUME terminator.
|
||||
// FIXME what span to use here?
|
||||
let resumeblk = cfg.start_new_cleanup_block();
|
||||
cfg.terminate(resumeblk, scope.id, fn_span, TerminatorKind::Resume);
|
||||
resumeblk
|
||||
} else {
|
||||
// Diverging from any other scope chains up to the previous scope.
|
||||
build_diverge_scope(tcx, fn_span, cfg, unit_temp, earlier_scopes)
|
||||
};
|
||||
scope.cached_block = Some(target);
|
||||
|
||||
// Next, build up any free.
|
||||
if let Some(ref mut free_data) = scope.free {
|
||||
target = if let Some(cached_block) = free_data.cached_block {
|
||||
|
Loading…
x
Reference in New Issue
Block a user