Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip a shared borrow of a immutable local variables #53909

Merged
merged 2 commits into from
Sep 8, 2018

Conversation

mikhail-m1
Copy link
Contributor

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 2, 2018
@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-5.0 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
[00:47:21] ....................................................................................................
[00:47:24] ....................................................................................................
[00:47:27] .......................i............................................................................
[00:47:29] ....................................................................................................
[00:47:32] ........................................................................iiiiiiiii...................
[00:47:37] ....................................................................................................
[00:47:41] ....................................................................................................
[00:47:44] .....................................................i..............................................
[00:47:46] ....................................................................................................
---
travis_time:start:test_mir-opt
Check compiletest suite=mir-opt mode=mir-opt (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
[00:54:08] 
[00:54:08] running 46 tests
[00:54:13] ERROR 2018-09-02T20:45:33Z: compiletest::runtest: None
[00:54:23] ERROR 2018-09-02T20:45:43Z: compiletest::runtest: None
[00:54:25] thread 'main' panicked at 'Some tests failed', tools/compiletest/src/main.rs:497:22
[00:54:25] ....................F......................F..
[00:54:25] 
[00:54:25] ---- [mir-opt] mir-opt/end_region_destruction_extents_1.rs stdout ----
[00:54:25] ---- [mir-opt] mir-opt/end_region_destruction_extents_1.rs stdout ----
[00:54:25] thread '[mir-opt] mir-opt/end_region_destruction_extents_1.rs' panicked at 'Did not find expected line, error: Mismatch in lines
[00:54:25] Current block: None
[00:54:25] Actual Line: "    let _5: S1;"
[00:54:25] Expected Line: "    let mut _5: S1;"
[00:54:25] Test Name: rustc.main.QualifyAndPromoteConstants.before.mir
[00:54:25] ... (elided)
[00:54:25] fn main() -> () {
[00:54:25] fn main() -> () {
[00:54:25] let mut _0: ();
[00:54:25]     let mut _1: &'12ds S1;
[00:54:25]     let mut _2: D1<'12ds, '10s>;
[00:54:25]     let mut _3: &'12ds S1;
[00:54:25]     let mut _4: &'12ds S1;
[00:54:25]     let mut _5: S1;
[00:54:25]     let mut _6: &'10s S1;
[00:54:25]     let mut _7: &'10s S1;
[00:54:25]     let mut _8: S1;
[00:54:25]     bb0: {
[00:54:25]         StorageLive(_2);
[00:54:25]         StorageLive(_3);
[00:54:25]         StorageLive(_4    StorageLive(_4);
[00:54:25]         StorageLive(_5);
[00:54:25]         _5 = S1::{{constructor}}(const "ex1",);
[00:54:25]         _4 = &'12ds _5;
[00:54:25]         _3 = &'12ds (*_4);
[00:54:25]         StorageLive(_6);
[00:54:25]         StorageLive(_7);
[00:54:25]         StorageLive(_8);
[00:54:25]         _8 = S1::{{constructor}}(const "dang1",);
[00:54:25]         _7 = &'10s _8;
[00:54:25]         _6 = &'10s (*_7);
[00:54:25]         _2 = D1<'12ds, '10s>::{{constructor}}(move _3, move _6);
[00:54:25]         EndRegion('10s);
[00:54:25]         StorageDead(_6);
[00:54:25]         StorageDead(_3);
[00:54:25]         _1 = (_2.0: &'12ds S1);
[00:54:25]         drop(_2) -> [return: bb2, unwind: bb1];
[00:54:25]     bb1: {
[00:54:25]         resume;
[00:54:25]     }
[00:54:25]     }
[00:54:25]     bb2: {                              
[00:54:25]         StorageDead(_2);
[00:54:25]         StorageDead(_7);
[00:54:25]         StorageDead(_8);
[00:54:25]         StorageDead(_4);
[00:54:25]         StorageDead(_5);
[00:54:25]         EndRegion('12ds);
[00:54:25]         _0 = ();
[00:54:25]         return;
[00:54:25] }', tools/compiletest/src/runtest.rs:2851:13
[00:54:25] note: Run with `RUST_BACKTRACE=1` for a backtrace.
[00:54:25] 
[00:54:25] ---- [mir-opt] mir-opt/storage_live_dead_in_statics.rs stdout ----
[00:54:25] ---- [mir-opt] mir-opt/storage_live_dead_in_statics.rs stdout ----
[00:54:25] thread '[mir-opt] mir-opt/storage_live_dead_in_statics.rs' panicked at 'Did not find expected line, error: Mismatch in lines
[00:54:25] Current block: None
[00:54:25] Actual Line: "    let _2: Foo;"
[00:54:25] Expected Line: "   let mut _2: Foo;"
[00:54:25] Test Name: rustc.XXX.mir_map.0.mir
[00:54:25] ... (elided)
[00:54:25] ... (elided)
[00:54:25]    let mut _0: &'static Foo;
[00:54:25]    let mut _1: &'static Foo;
[00:54:25]    let mut _2: Foo;
[00:54:25]    let mut _3: &'static [(u32, u32)];
[00:54:25]    let mut _4: &'static [(u32, u32); 42];
[00:54:25]    let mut _5: &'static [(u32, u32); 42];
[00:54:25]    let mut _6: [(u32, u32); 42];
[00:54:25]    let mut _7: (u32, u32);
[00:54:25]    let mut _8: (u32, u32);
[00:54:25]    let mut _9: (u32, u32);
[00:54:25]    let mut _10: (u32, u32);
[00:54:25]    let mut _11: (u32, u32);
[00:54:25]    let mut _12: (u32, u32);
[00:54:25]    let mut _13: (u32, u32);
[00:54:25]    let mut _14: (u32, u32);
[00:54:25]    let mut _15: (u32, u32);
[00:54:25]    let mut _16: (u32, u32);
[00:54:25]    let mut _17: (u32, u32);
[00:54:25]    let mut _18: (u32, u32);
[00:54:25]    let mut _19: (u32, u32);
[00:54:25]    let mut _20: (u32, u32);
[00:54:25]    let mut _21: (u32, u32);
[00:54:25]    let mut _22: (u32, u32);
[00:54:25]    let mut _23: (u32, u32);
[00:54:25]    let mut _24: (u32, u32);
[00:54:25]    let mut _25: (u32, u32);
[00:54:25]    let mut _26: (u32, u32);
[00:54:25]    let mut _27: (u32, u32);
[00:54:25]    let mut _28: (u32, u32);
[00:54:25]    let mut _29: (u32, u32);
[00:54:25]    let mut _30: (u32, u32);
[00:54:25]    let mut _31: (u32, u32);
[00:54:25]    let mut _32: (u32, u32);
[00:54:25]    let mut _33: (u32, u32);
[00:54:25]    let mut _34: (u32, u32);
[00:54:25]    let mut _35: (u32, u32);
[00:54:25]    let mut _36: (u32, u32);
[00:54:25]    let mut _37: (u32, u32);
[00:54:25]    ve(_18);
[00:54:25]        _18 = (const 0u32, const 3u32);
[00:54:25]        StorageLive(_19);
[00:54:25]        _19 = (const 0u32, const 1u32);
[00:54:25]        StorageLive(_20);
[00:54:25]        _20 = (const 0u32, const 2u32);
[00:54:25]        StorageLive(_21);
[00:54:25]        _21 = (const 0u32, const 3u32);
[00:54:25]        StorageLive(_22);
[00:54:25]        _22 = (const 0u32, const 1u32);
[00:54:25]        StorageLive(_23);
[00:54:25]        _23 = (const 0u32, const 2u32);
[00:54:25]        StorageLive(_24);
[00:54:25]        _24 = (const 0u32, const 3u32);
[00:54:25]        StorageLive(_25);
[00:54:25]        _25 = (const 0u32, const 1u32);
[00:54:25]        StorageLive(_26);
[00:54:25]        _26 = (const 0u32, const 2u32);
[00:54:25]        StorageLive(_27);
[00:54:25]        _27 = (const 0u32, const 3u32);
[00:54:25]        StorageLive(_28);
[00:54:25]        _28 = (const 0u32, const 1u32);
[00:54:25]        StorageLive(_29);
[00:54:25]        _29 = (const 0u32, const 2u32);
[00:54:25]        StorageLive(_30);
[00:54:25]        _30 = (const 0u32, const 3u32);
[00:54:25]        StorageLive(_31);
[00:54:25]        _31 = (const 0u32, const 1u32);
[00:54:25]        StorageLive(_32);
[00:54:25]        _32 = (const 0u32, const 2u32);
[00:54:25]        StorageLive(_33);
[00:54:25]        _33 = (const 0u32, const 3u32);
[00:54:25]        StorageLive(_34);
[00:54:25]        _34 = (const 0u32, const 1u32);
[00:54:25]        StorageLive(_35);
[00:54:25]        _35 = (const 0u32, const 2u32);
[00:54:25]        StorageLive(_36);
[00:54:25]        _36 = (const 0u32, const 3u32);
[00:54:2         _33 = (const 0u32, const 3u32);
[00:54:25]         StorageLive(_34);
[00:54:25]         _34 = (const 0u32, const 1u32);
[00:54:25]         StorageLive(_35);
[00:54:25]         _35 = (const 0u32, const 2u32);
[00:54:25]         StorageLive(_36);
[00:54:25]         _36 = (const 0u32, const 3u32);
[00:54:25]         StorageLive(_37);
[00:54:25]         _37 = (const 0u32, const 1u32);
[00:54:25]         StorageLive(_38);
[00:54:25]         _38 = (const 0u32, const 2u32);
[00:54:25]         StorageLive(_39);
[00:54:25]         _39 = (const 0u32, const 3u32);
[00:54:25]         StorageLive(_40);
[00:54:25]         _40 = (const 0u32, const 1u32);
[00:54:25]         StorageLive(_41);
[00:54:25]         _41 = (const 0u32, const 2u32);
[00:54:25]         StorageLive(_42);
[00:54:25]         _42 = (const 0u32, const 3u32);
[00:54:25]         StorageLive(_43);
[00:54:25]         _43 = (const 0u32, const 1u32);
[00:54:25]         StorageLive(_44);
[00:54:25]         _44 = (const 0u32, const 2u32);
[00:54:25]         StorageLive(_45);
[00:54:25]         _45 = (const 0u32, const 3u32);
[00:54:25]         StorageLive(_46);
[00:54:25]         _46 = (const 0u32, const 1u32);
[00:54:25]         StorageLive(_47);
[00:54:25]         _47 = (const 0u32, const 2u32);
[00:54:25]         StorageLive(_48);
[00:54:25]         _48 = (const 0u32, const 3u32);
[00:54:25]         _6 = [move _7, move _8, move _9, move _10, move _11, move _12, move _13, move _14, move _15, move _16, move _17, move _18, move _19, move _20, move _21, move _22, move _23, move _24, move _25, move _26, move _27, move _28, move _29, move _30,Sun, 02 Sep 2018 20:45:46 GMT

The command "date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)
" exited with 0.
travis_fold:start:after_failure.1

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@@ -223,10 +223,10 @@ impl<'tcx> Mir<'tcx> {
} else if self.local_decls[local].name.is_some() {
LocalKind::Var
} else {
debug_assert!(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say just delete the debug_assert!, there's no value in leaving it in commented out

}

let mut has_storage_dead = HasStorageDead(BitArray::new(mir.local_decls.len()));
has_storage_dead.visit_mir(mir);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a similar prototype that I was working on, I combined this bit-array computation into a new enum for locals_are_invalidated_at_exit. (That is, I tried to ensure that we only did this work when locals_are_invalidated_at_exit is false here.) Just a stray thought on something you might want to consider incorporating.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, I will do this join as a next step, but may be the bit-array can be used somewhere else? or just make it an Option but I cannot find a nice name for it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if I understand correctly, you can only have the bit-array when locals_are_invalidated_at_exit is false in the first place. So even if the bit-array can be used somewhere else, you should be able to extract it, assuming you've changed the type like so: locals_are_invalidated_at_exit: LocalInvalidationState, where

 enum InvalidationState {
    LocalsAreInvalidated(BitArray<Local>),
    LocalsNotInvalidated,
}

@@ -63,7 +63,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
block.and(Rvalue::Repeat(value_operand, count))
}
ExprKind::Borrow { region, borrow_kind, arg } => {
let arg_place = unpack!(block = this.as_place(block, arg));
debug!("#### as rvalue {:?}", (borrow_kind));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why are there parens around (borrow_kind) here?

@pnkfelix
Copy link
Member

pnkfelix commented Sep 3, 2018

The travis failures seem to indicate that you need to update some of the mir-opt tests to account for the new output that now differentiates between let and let mut on temps

@nikomatsakis
Copy link
Contributor

@bors try

@bors
Copy link
Contributor

bors commented Sep 4, 2018

⌛ Trying commit 55e69cfc1952ea0290b4a9b02123958a69631e8d with merge 5dacc1649d847cca076ace72bc21130bc114a383...

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I think I may have found an oversight in my original specification though (the move case). Probably we can easily check if a variable (or some sub-path of it) may be moved, though. In any case, I'm doing a local build to run some profiles.

mir: &Mir<'tcx>,
has_storage_dead: &BitArray<Local>,
locals_are_invalidated_at_exit: bool,
) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this closing paren should be "unindented"; or just run rustfmt

Place::Local(_) => false,
Place::Promoted(_) => false,
Place::Local(index) => {
let ignore = !locals_are_invalidated_at_exit &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like this is worth a comment. Something like:


If a local variable is immutable, then we only need to track borrows to guard against two kinds of errors:

  • The variable being dropped while still borrowed (e.g., because the fn returns a reference to a local variable)
  • The variable being moved while still borrowed

In particular, the variable cannot be mutated -- the "access checks" will fail -- so we don't have to worry about mutation while borrowed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But writing that made me realize we may be missing a case! That is, we're not checking for moves. This is ultimately only having an effect in constants, so it may be hard to write code that exploits this at present, but it seems bad.

}

fn expr_as_place(&mut self,
mut block: BasicBlock,
expr: Expr<'tcx>)
expr: Expr<'tcx>,
immutable: bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can pass a hir::Mutability instead -- something more declarative than a naked boolean

}

fn expr_as_temp(&mut self,
mut block: BasicBlock,
temp_lifetime: Option<region::Scope>,
expr: Expr<'tcx>)
expr: Expr<'tcx>,
immutable: bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

let this = self;

let expr_span = expr.span;
let source_info = this.source_info(expr_span);
if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
return this.in_scope((region_scope, source_info), lint_level, block, |this| {
this.as_temp(block, temp_lifetime, value)
if immutable {
this.as_immutable_temp(block, temp_lifetime, value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then we would pass the argument here (this.as_temp(block, temp_lifetime, value, mutability)), perhaps?

self.tcx,
self.mir,
&self.borrow_set.has_storage_dead,
self.locals_are_invalidated_at_exit) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: formatting seems off. I think the ) should go on the next line:

if place.ignore_borrow(
    foo,
    bar,
) {
    return;
}

@bors
Copy link
Contributor

bors commented Sep 4, 2018

☀️ Test successful - status-travis
State: approved= try=True

@Mark-Simulacrum
Copy link
Member

@rust-timer build 5dacc1649d847cca076ace72bc21130bc114a383

@rust-timer
Copy link
Collaborator

Success: Queued 5dacc1649d847cca076ace72bc21130bc114a383 with parent 1c2e17f, comparison URL.

@memoryruins
Copy link
Contributor

😮

@lqd
Copy link
Member

lqd commented Sep 5, 2018

This brings ucd down to 116% !

@mikhail-m1 mikhail-m1 changed the title [WIP] Skip a shared borrow of a immutable local variables Skip a shared borrow of a immutable local variables Sep 6, 2018
Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Left some nits.

has_storage_dead.visit_mir(mir);
let mut has_storage_dead_or_moved = has_storage_dead.0;
for move_out in &move_data.moves {
let mut path = &move_data.move_paths[move_out.path];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make this a helper function. Something like:

impl MoveData {
    /// For the move path `mpi`, returns the root local variable (if any) that starts the path.
    /// (e.g., for a path like `a.b.c` returns `Some(a)`)
    pub fn base_local(&self, mut mpi: MovePathIndex) -> Option<Local> {
        loop {
            let path = &self.move_paths[mpi];
            if let Place::Local(l) = path.place { return Some(l); }
            if let Some(parent) = path.parent { mpi = parent; continue } else { return None }
        }
    }
}

// In particular, the variable cannot be mutated -- the "access checks" will fail --
// so we don't have to worry about mutation while borrowed.
Place::Local(index) => {
if let LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Personally I'd use a match here, which I think would indent more cleanly:

match locals_state_at_exit {
    LocalsStateAtExit::AllAreInvalidated => false,
    LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
        ...
    }
}

as a bonus, this is an exhaustive match, so if we ever add a third variant, we'll get a compilation error.

pub fn as_immutable_place<M>(&mut self,
block: BasicBlock,
expr: M)
-> BlockAnd<Place<'tcx>>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: indentation is wacky. Run rustfmt?

self.expr_as_place(block, expr, Mutability::Mut)
}

/// Compile `expr`, yielding a place that we can move from etc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd add a comment here like:

Mutability note: The caller of this method promises only to read from the resulting
place. The place itself may or may not be mutable:

  • If this expr is a place expr like a.b, then we will return that place.
  • Otherwise, a temporary is created: in that event, it will be an immutable temporary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe argues for calling it as_read_only_place

@nikomatsakis nikomatsakis added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 6, 2018
@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Sep 7, 2018

📌 Commit d0c1e5a has been approved by nikomatsakis

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 7, 2018
@bors
Copy link
Contributor

bors commented Sep 8, 2018

⌛ Testing commit d0c1e5a with merge 0198a1e...

bors added a commit that referenced this pull request Sep 8, 2018
Skip a shared borrow of a immutable local variables

issue #53643

r? @nikomatsakis
@bors
Copy link
Contributor

bors commented Sep 8, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing 0198a1e to master...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-NLL Area: Non Lexical Lifetimes (NLL) S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants