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

fix promotion of MIR terminators #38833

Merged
merged 1 commit into from
Jan 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 66 additions & 78 deletions src/librustc_mir/transform/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl TempState {
/// A "root candidate" for promotion, which will become the
/// returned value in a promoted MIR, unless it's a subset
/// of a larger candidate.
#[derive(Debug)]
pub enum Candidate {
/// Borrow of a constant temporary.
Ref(Location),
Expand Down Expand Up @@ -190,15 +191,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
/// promoted MIR, recursing through temps.
fn promote_temp(&mut self, temp: Local) -> Local {
let old_keep_original = self.keep_original;
let (bb, stmt_idx) = match self.temps[temp] {
TempState::Defined {
location: Location { block, statement_index },
uses
} if uses > 0 => {
let loc = match self.temps[temp] {
TempState::Defined { location, uses } if uses > 0 => {
if uses > 1 {
self.keep_original = true;
}
(block, statement_index)
location
}
state => {
span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
Expand All @@ -209,91 +207,80 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
self.temps[temp] = TempState::PromotedOut;
}

let no_stmts = self.source[bb].statements.len();
let no_stmts = self.source[loc.block].statements.len();
let new_temp = self.promoted.local_decls.push(
LocalDecl::new_temp(self.source.local_decls[temp].ty));

debug!("promote({:?} @ {:?}/{:?}, {:?})",
temp, loc, no_stmts, self.keep_original);

// First, take the Rvalue or Call out of the source MIR,
// or duplicate it, depending on keep_original.
let (mut rvalue, mut call) = (None, None);
let source_info = if stmt_idx < no_stmts {
let statement = &mut self.source[bb].statements[stmt_idx];
let rhs = match statement.kind {
StatementKind::Assign(_, ref mut rhs) => rhs,
_ => {
span_bug!(statement.source_info.span, "{:?} is not an assignment",
statement);
}
if loc.statement_index < no_stmts {
let (mut rvalue, source_info) = {
let statement = &mut self.source[loc.block].statements[loc.statement_index];
let rhs = match statement.kind {
StatementKind::Assign(_, ref mut rhs) => rhs,
_ => {
span_bug!(statement.source_info.span, "{:?} is not an assignment",
statement);
}
};

(if self.keep_original {
rhs.clone()
} else {
let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
mem::replace(rhs, unit)
}, statement.source_info)
};
if self.keep_original {
rvalue = Some(rhs.clone());
} else {
let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
rvalue = Some(mem::replace(rhs, unit));
}
statement.source_info
} else if self.keep_original {
let terminator = self.source[bb].terminator().clone();
call = Some(terminator.kind);
terminator.source_info

self.visit_rvalue(&mut rvalue, loc);
self.assign(new_temp, rvalue, source_info.span);
} else {
let terminator = self.source[bb].terminator_mut();
let target = match terminator.kind {
TerminatorKind::Call {
destination: ref mut dest @ Some(_),
ref mut cleanup, ..
} => {
// No cleanup necessary.
cleanup.take();

// We'll put a new destination in later.
dest.take().unwrap().1
}
ref kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
let mut terminator = if self.keep_original {
self.source[loc.block].terminator().clone()
} else {
let terminator = self.source[loc.block].terminator_mut();
let target = match terminator.kind {
TerminatorKind::Call { destination: Some((_, target)), .. } => target,
ref kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
};
Terminator {
source_info: terminator.source_info,
kind: mem::replace(&mut terminator.kind, TerminatorKind::Goto {
target: target
})
}
};
call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
target: target
}));
terminator.source_info
};

// Then, recurse for components in the Rvalue or Call.
if stmt_idx < no_stmts {
self.visit_rvalue(rvalue.as_mut().unwrap(), Location {
block: bb,
statement_index: stmt_idx
});
} else {
self.visit_terminator_kind(bb, call.as_mut().unwrap(), Location {
block: bb,
statement_index: no_stmts
});
}

let new_temp = self.promoted.local_decls.push(
LocalDecl::new_temp(self.source.local_decls[temp].ty));

// Inject the Rvalue or Call into the promoted MIR.
if stmt_idx < no_stmts {
self.assign(new_temp, rvalue.unwrap(), source_info.span);
} else {
let last = self.promoted.basic_blocks().last().unwrap();
let new_target = self.new_block();
let mut call = call.unwrap();
match call {
TerminatorKind::Call { ref mut destination, ..} => {
*destination = Some((Lvalue::Local(new_temp), new_target));

terminator.kind = match terminator.kind {
TerminatorKind::Call { mut func, mut args, .. } => {
self.visit_operand(&mut func, loc);
for arg in &mut args {
self.visit_operand(arg, loc);
}
TerminatorKind::Call {
func: func,
args: args,
cleanup: None,
destination: Some((Lvalue::Local(new_temp), new_target))
}
}
_ => bug!()
}
let terminator = self.promoted[last].terminator_mut();
terminator.source_info.span = source_info.span;
terminator.kind = call;
}
ref kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
};

// Restore the old duplication state.
self.keep_original = old_keep_original;
*self.promoted[last].terminator_mut() = terminator;
};

self.keep_original = old_keep_original;
new_temp
}

Expand Down Expand Up @@ -355,6 +342,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
mut temps: IndexVec<Local, TempState>,
candidates: Vec<Candidate>) {
// Visit candidates in reverse, in case they're nested.
debug!("promote_candidates({:?})", candidates);
for candidate in candidates.into_iter().rev() {
let (span, ty) = match candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -993,9 +993,9 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants {
Entry::Vacant(entry) => {
// Guard against `const` recursion.
entry.insert(Qualif::RECURSIVE);
Mode::Const
}
}
Mode::Const
}
MirSource::Static(_, hir::MutImmutable) => Mode::Static,
MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
Expand Down
20 changes: 20 additions & 0 deletions src/test/run-pass/issue-37991.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(const_fn)]

const fn foo() -> i64 {
3
}

fn main() {
let val = &(foo() % 2);
assert_eq!(*val, 1);
}