Skip to content

Commit 7f66c41

Browse files
committed
Auto merge of #60745 - wesleywiser:const_prop_into_terminators, r=<try>
[WIP] Const prop into terminators Rebased on top of #60597. New commits start at eea75dc. Last commit is just for testing purposes and I'll remove it before we merge this. r? @ghost
2 parents 5f1924c + 269ef5a commit 7f66c41

File tree

7 files changed

+272
-78
lines changed

7 files changed

+272
-78
lines changed

src/librustc_mir/transform/const_prop.rs

Lines changed: 156 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
44
use rustc::hir::def::DefKind;
55
use rustc::mir::{
6-
Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local,
6+
AggregateKind, Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local,
77
NullOp, UnOp, StatementKind, Statement, LocalKind, Static, StaticKind,
88
TerminatorKind, Terminator, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem,
99
SourceScope, SourceScopeLocalData, LocalDecl, Promoted,
1010
};
11-
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
11+
use rustc::mir::visit::{
12+
Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext,
13+
};
1214
use rustc::mir::interpret::{InterpError, Scalar, GlobalId, EvalResult};
1315
use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
1416
use syntax::source_map::DUMMY_SP;
@@ -19,7 +21,7 @@ use rustc::ty::layout::{
1921
HasTyCtxt, TargetDataLayout, HasDataLayout,
2022
};
2123

22-
use crate::interpret::{InterpretCx, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind};
24+
use crate::interpret::{self, InterpretCx, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind};
2325
use crate::const_eval::{
2426
CompileTimeInterpreter, error_to_const_error, eval_promoted, mk_eval_cx,
2527
};
@@ -497,6 +499,50 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
497499
},
498500
}
499501
}
502+
503+
fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>) -> Operand<'tcx> {
504+
Operand::Constant(Box::new(
505+
Constant {
506+
span: DUMMY_SP,
507+
ty,
508+
user_ty: None,
509+
literal: self.tcx.mk_const(ty::Const::from_scalar(
510+
scalar,
511+
ty,
512+
))
513+
}
514+
))
515+
}
516+
517+
fn replace_with_const(&self, rval: &mut Rvalue<'tcx>, value: Const<'tcx>) {
518+
if let interpret::Operand::Immediate(im) = *value {
519+
match im {
520+
interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => {
521+
*rval = Rvalue::Use(self.operand_from_scalar(scalar, value.layout.ty));
522+
},
523+
Immediate::ScalarPair(
524+
ScalarMaybeUndef::Scalar(one),
525+
ScalarMaybeUndef::Scalar(two)
526+
) => {
527+
let ty = &value.layout.ty.sty;
528+
if let ty::Tuple(substs) = ty {
529+
*rval = Rvalue::Aggregate(
530+
Box::new(AggregateKind::Tuple),
531+
vec![
532+
self.operand_from_scalar(one, substs[0].expect_ty()),
533+
self.operand_from_scalar(two, substs[1].expect_ty()),
534+
],
535+
);
536+
}
537+
},
538+
_ => { }
539+
}
540+
}
541+
}
542+
543+
fn should_const_prop(&self) -> bool {
544+
true
545+
}
500546
}
501547

502548
fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -560,10 +606,10 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
560606
}
561607
}
562608

563-
impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
609+
impl<'b, 'a, 'tcx> MutVisitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
564610
fn visit_constant(
565611
&mut self,
566-
constant: &Constant<'tcx>,
612+
constant: &mut Constant<'tcx>,
567613
location: Location,
568614
) {
569615
trace!("visit_constant: {:?}", constant);
@@ -573,11 +619,11 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
573619

574620
fn visit_statement(
575621
&mut self,
576-
statement: &Statement<'tcx>,
622+
statement: &mut Statement<'tcx>,
577623
location: Location,
578624
) {
579625
trace!("visit_statement: {:?}", statement);
580-
if let StatementKind::Assign(ref place, ref rval) = statement.kind {
626+
if let StatementKind::Assign(ref place, ref mut rval) = statement.kind {
581627
let place_ty: Ty<'tcx> = place
582628
.ty(&self.local_decls, self.tcx)
583629
.ty;
@@ -589,6 +635,10 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
589635
trace!("storing {:?} to {:?}", value, local);
590636
assert!(self.places[local].is_none());
591637
self.places[local] = Some(value);
638+
639+
if self.should_const_prop() {
640+
self.replace_with_const(rval, value);
641+
}
592642
}
593643
}
594644
}
@@ -599,79 +649,111 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
599649

600650
fn visit_terminator(
601651
&mut self,
602-
terminator: &Terminator<'tcx>,
652+
terminator: &mut Terminator<'tcx>,
603653
location: Location,
604654
) {
605655
self.super_terminator(terminator, location);
606-
let source_info = terminator.source_info;;
607-
if let TerminatorKind::Assert { expected, msg, cond, .. } = &terminator.kind {
608-
if let Some(value) = self.eval_operand(&cond, source_info) {
609-
trace!("assertion on {:?} should be {:?}", value, expected);
610-
let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
611-
if expected != self.ecx.read_scalar(value).unwrap() {
612-
// poison all places this operand references so that further code
613-
// doesn't use the invalid value
614-
match cond {
615-
Operand::Move(ref place) | Operand::Copy(ref place) => {
616-
let mut place = place;
617-
while let Place::Projection(ref proj) = *place {
618-
place = &proj.base;
619-
}
620-
if let Place::Base(PlaceBase::Local(local)) = *place {
621-
self.places[local] = None;
656+
let source_info = terminator.source_info;
657+
match &mut terminator.kind {
658+
TerminatorKind::Assert { expected, msg, ref mut cond, .. } => {
659+
if let Some(value) = self.eval_operand(&cond, source_info) {
660+
trace!("assertion on {:?} should be {:?}", value, expected);
661+
let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
662+
let value_const = self.ecx.read_scalar(value).unwrap();
663+
if expected != value_const {
664+
// poison all places this operand references so that further code
665+
// doesn't use the invalid value
666+
match cond {
667+
Operand::Move(ref place) | Operand::Copy(ref place) => {
668+
let mut place = place;
669+
while let Place::Projection(ref proj) = *place {
670+
place = &proj.base;
671+
}
672+
if let Place::Base(PlaceBase::Local(local)) = *place {
673+
self.places[local] = None;
674+
}
675+
},
676+
Operand::Constant(_) => {}
677+
}
678+
let span = terminator.source_info.span;
679+
let hir_id = self
680+
.tcx
681+
.hir()
682+
.as_local_hir_id(self.source.def_id())
683+
.expect("some part of a failing const eval must be local");
684+
use rustc::mir::interpret::InterpError::*;
685+
let msg = match msg {
686+
Overflow(_) |
687+
OverflowNeg |
688+
DivisionByZero |
689+
RemainderByZero => msg.description().to_owned(),
690+
BoundsCheck { ref len, ref index } => {
691+
let len = self
692+
.eval_operand(len, source_info)
693+
.expect("len must be const");
694+
let len = match self.ecx.read_scalar(len) {
695+
Ok(ScalarMaybeUndef::Scalar(Scalar::Bits {
696+
bits, ..
697+
})) => bits,
698+
other => bug!("const len not primitive: {:?}", other),
699+
};
700+
let index = self
701+
.eval_operand(index, source_info)
702+
.expect("index must be const");
703+
let index = match self.ecx.read_scalar(index) {
704+
Ok(ScalarMaybeUndef::Scalar(Scalar::Bits {
705+
bits, ..
706+
})) => bits,
707+
other => bug!("const index not primitive: {:?}", other),
708+
};
709+
format!(
710+
"index out of bounds: \
711+
the len is {} but the index is {}",
712+
len,
713+
index,
714+
)
715+
},
716+
// Need proper const propagator for these
717+
_ => return,
718+
};
719+
self.tcx.lint_hir(
720+
::rustc::lint::builtin::CONST_ERR,
721+
hir_id,
722+
span,
723+
&msg,
724+
);
725+
} else {
726+
if self.should_const_prop() {
727+
if let ScalarMaybeUndef::Scalar(scalar) = value_const {
728+
*cond = self.operand_from_scalar(scalar, self.tcx.types.bool);
622729
}
623-
},
624-
Operand::Constant(_) => {}
730+
}
625731
}
626-
let span = terminator.source_info.span;
627-
let hir_id = self
628-
.tcx
629-
.hir()
630-
.as_local_hir_id(self.source.def_id())
631-
.expect("some part of a failing const eval must be local");
632-
use rustc::mir::interpret::InterpError::*;
633-
let msg = match msg {
634-
Overflow(_) |
635-
OverflowNeg |
636-
DivisionByZero |
637-
RemainderByZero => msg.description().to_owned(),
638-
BoundsCheck { ref len, ref index } => {
639-
let len = self
640-
.eval_operand(len, source_info)
641-
.expect("len must be const");
642-
let len = match self.ecx.read_scalar(len) {
643-
Ok(ScalarMaybeUndef::Scalar(Scalar::Bits {
644-
bits, ..
645-
})) => bits,
646-
other => bug!("const len not primitive: {:?}", other),
647-
};
648-
let index = self
649-
.eval_operand(index, source_info)
650-
.expect("index must be const");
651-
let index = match self.ecx.read_scalar(index) {
652-
Ok(ScalarMaybeUndef::Scalar(Scalar::Bits {
653-
bits, ..
654-
})) => bits,
655-
other => bug!("const index not primitive: {:?}", other),
656-
};
657-
format!(
658-
"index out of bounds: \
659-
the len is {} but the index is {}",
660-
len,
661-
index,
662-
)
663-
},
664-
// Need proper const propagator for these
665-
_ => return,
666-
};
667-
self.tcx.lint_hir(
668-
::rustc::lint::builtin::CONST_ERR,
669-
hir_id,
670-
span,
671-
&msg,
672-
);
673732
}
674-
}
733+
},
734+
TerminatorKind::SwitchInt { ref mut discr, switch_ty, .. } => {
735+
if self.should_const_prop() {
736+
if let Some(value) = self.eval_operand(&discr, source_info) {
737+
if let Ok(ScalarMaybeUndef::Scalar(scalar)) = self.ecx.read_scalar(value) {
738+
*discr = self.operand_from_scalar(scalar, switch_ty);
739+
}
740+
}
741+
}
742+
},
743+
//none of these have Operands to const-propagate
744+
TerminatorKind::Goto { .. } |
745+
TerminatorKind::Resume |
746+
TerminatorKind::Abort |
747+
TerminatorKind::Return |
748+
TerminatorKind::Unreachable |
749+
TerminatorKind::Drop { .. } |
750+
TerminatorKind::DropAndReplace { .. } |
751+
TerminatorKind::Yield { .. } |
752+
TerminatorKind::GeneratorDrop |
753+
TerminatorKind::FalseEdges { .. } |
754+
TerminatorKind::FalseUnwind { .. } => { }
755+
//FIXME(wesleywiser) Call does have Operands that could be const-propagated
756+
TerminatorKind::Call { .. } => { }
675757
}
676758
}
677759
}

src/test/codegen/inline-always-works-always.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub extern "C" fn callee() -> u32 {
1414
// CHECK-LABEL: caller
1515
// SIZE-OPT: ret i32 8
1616
// SPEED-OPT: ret i32 8
17-
// NO-OPT: ret i32 8
17+
// NO-OPT: ret i32 %3
1818
#[no_mangle]
1919
pub extern "C" fn caller() -> u32 {
2020
callee()

src/test/codegen/optimize-attr-1.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
// CHECK-LABEL: define i32 @nothing
1010
// CHECK-SAME: [[NOTHING_ATTRS:#[0-9]+]]
11-
// NO-OPT: ret i32 %1
11+
// NO-OPT: ret i32 %3
1212
// SIZE-OPT: ret i32 4
1313
// SPEEC-OPT: ret i32 4
1414
#[no_mangle]
@@ -18,7 +18,7 @@ pub fn nothing() -> i32 {
1818

1919
// CHECK-LABEL: define i32 @size
2020
// CHECK-SAME: [[SIZE_ATTRS:#[0-9]+]]
21-
// NO-OPT: ret i32 %1
21+
// NO-OPT: ret i32 %3
2222
// SIZE-OPT: ret i32 6
2323
// SPEED-OPT: ret i32 6
2424
#[optimize(size)]
@@ -31,7 +31,7 @@ pub fn size() -> i32 {
3131
// NO-OPT-SAME: [[NOTHING_ATTRS]]
3232
// SPEED-OPT-SAME: [[NOTHING_ATTRS]]
3333
// SIZE-OPT-SAME: [[SPEED_ATTRS:#[0-9]+]]
34-
// NO-OPT: ret i32 %1
34+
// NO-OPT: ret i32 %3
3535
// SIZE-OPT: ret i32 8
3636
// SPEED-OPT: ret i32 8
3737
#[optimize(speed)]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
fn main() {
2+
let x: u32 = [0, 1, 2, 3][2];
3+
}
4+
5+
// END RUST SOURCE
6+
// START rustc.main.ConstProp.before.mir
7+
// bb0: {
8+
// ...
9+
// _2 = [const 0u32, const 1u32, const 2u32, const 3u32];
10+
// ...
11+
// _3 = const 2usize;
12+
// _4 = const 4usize;
13+
// _5 = Lt(_3, _4);
14+
// assert(move _5, "index out of bounds: the len is move _4 but the index is _3") -> bb1;
15+
// }
16+
// bb1: {
17+
// _1 = _2[_3];
18+
// ...
19+
// return;
20+
// }
21+
// END rustc.main.ConstProp.before.mir
22+
// START rustc.main.ConstProp.after.mir
23+
// bb0: {
24+
// ...
25+
// _5 = const true;
26+
// assert(const true, "index out of bounds: the len is move _4 but the index is _3") -> bb1;
27+
// }
28+
// bb1: {
29+
// _1 = _2[_3];
30+
// ...
31+
// return;
32+
// }
33+
// END rustc.main.ConstProp.after.mir
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// compile-flags: -C overflow-checks=on
2+
3+
fn main() {
4+
let x: u32 = 1 + 1;
5+
}
6+
7+
// END RUST SOURCE
8+
// START rustc.main.ConstProp.before.mir
9+
// bb0: {
10+
// ...
11+
// _2 = CheckedAdd(const 1u32, const 1u32);
12+
// assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1;
13+
// }
14+
// END rustc.main.ConstProp.before.mir
15+
// START rustc.main.ConstProp.after.mir
16+
// bb0: {
17+
// ...
18+
// _2 = (const 2u32, const false);
19+
// assert(!const false, "attempt to add with overflow") -> bb1;
20+
// }
21+
// END rustc.main.ConstProp.after.mir

0 commit comments

Comments
 (0)