Skip to content

Commit

Permalink
Nice graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
nagisa committed Aug 17, 2016
1 parent 2d36642 commit 2c3250a
Showing 1 changed file with 48 additions and 12 deletions.
60 changes: 48 additions & 12 deletions src/librustc_mir/build/scope.rs
Expand Up @@ -459,16 +459,52 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {

for scope in self.scopes.iter_mut().rev() {
let this_scope = scope.extent == extent;
// We must invalidate all the caches leading up to the scope we’re looking for, because
// the cached blocks will branch into build of scope not containing the new drop. If we
// add stuff to the currently inspected scope, then in some cases the non-unwind caches
// may become invalid, therefore we should invalidate these as well. The unwind caches
// will stay correct, because the already generated unwind blocks cannot be influenced
// by just added drop.
// When building drops, we try to cache chains of drops in such a way so these drops
// could be reused by the drops which would branch into the cached (already built)
// blocks. This, however, means that whenever we add a drop into a scope which already
// had some blocks built (and thus, cached) for it, we must invalidate all caches which
// might branch into the scope which had a drop just added to it. This is necessary,
// because otherwise some other code might use the cache to branch into already built
// chain of drops, essentially ignoring the newly added drop.
//
// If we’re scheduling cleanup for non-droppable type (i.e. DropKind::Storage), then we
// do not need to invalidate unwind branch, because DropKind::Storage does not end up
// built in the unwind branch currently.
// For example consider there’s two scopes with a drop in each. These are built and
// thus the caches are filled:
//
// +--------------------------------------------------------+
// | +---------------------------------+ |
// | | +--------+ +-------------+ | +---------------+ |
// | | | return | <-+ | drop(outer) | <-+ | drop(middle) | |
// | | +--------+ +-------------+ | +---------------+ |
// | +------------|outer_scope cache|--+ |
// +------------------------------|middle_scope cache|------+
//
// Now, a new, inner-most scope is added along with a new drop into both inner-most and
// outer-most scopes:
//
// +------------------------------------------------------------+
// | +----------------------------------+ |
// | | +--------+ +-------------+ | +---------------+ | +-------------+
// | | | return | <+ | drop(new) | <-+ | drop(middle) | <--+| drop(inner) |
// | | +--------+ | | drop(outer) | | +---------------+ | +-------------+
// | | +-+ +-------------+ | |
// | +---|invalid outer_scope cache|----+ |
// +----=----------------|invalid middle_scope cache|-----------+
//
// If, when adding `drop(new)` we do not invalidate the cached blocks for both
// outer_scope and middle_scope, then, when building drops for the inner (right-most)
// scope, the old, cached blocks, without `drop(new)` will get used, producing the
// wrong results.
//
// The cache and its invalidation for unwind branch is somewhat special. The cache is
// per-drop, rather than per scope, which has a several different implications. Adding
// a new drop into a scope will not invalidate cached blocks of the prior drops in the
// scope. That is true, because none of the already existing drops will have an edge
// into a block with the newly added drop.
//
// Note that this code iterates scopes from the inner-most to the outer-most,
// invalidating caches of each scope visited. This way bare minimum of the
// caches gets invalidated. i.e. if a new drop is added into the middle scope, the
// cache of outer scpoe stays intact.
let invalidate_unwind = needs_drop && !this_scope;
scope.invalidate_cache(invalidate_unwind);
if this_scope {
Expand Down Expand Up @@ -497,9 +533,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
value: &Lvalue<'tcx>,
item_ty: Ty<'tcx>) {
for scope in self.scopes.iter_mut().rev() {
// We must invalidate all the caches leading up to and including the scope we’re
// looking for, because otherwise some of the blocks in the chain will become
// incorrect and must be rebuilt.
// See the comment in schedule_drop above. The primary difference is that we invalidate
// the unwind blocks unconditionally. That’s because the box free may be considered
// outer-most cleanup within the scope.
scope.invalidate_cache(true);
if scope.extent == extent {
assert!(scope.free.is_none(), "scope already has a scheduled free!");
Expand Down

0 comments on commit 2c3250a

Please sign in to comment.