From 19898d636bb5e00c76dabfeb9ae3f980d0f95e83 Mon Sep 17 00:00:00 2001 From: Ellen Date: Sun, 24 Oct 2021 21:23:15 +0100 Subject: [PATCH 1/3] a wild commit has appeared! --- compiler/rustc_middle/src/mir/terminator.rs | 7 + .../rustc_mir_dataflow/src/framework/mod.rs | 1 + compiler/rustc_mir_dataflow/src/lib.rs | 2 +- compiler/rustc_mir_transform/src/lib.rs | 3 + .../rustc_mir_transform/src/refinements.rs | 396 ++++++++++++++++++ .../double_match.foo.Refinements.diff | 52 +++ src/test/mir-opt/refinements/double_match.rs | 16 + .../or_pattern.foo.Refinements.diff | 67 +++ src/test/mir-opt/refinements/or_pattern.rs | 17 + ...otherwise_refinements.foo.Refinements.diff | 47 +++ .../refinements/otherwise_refinements.rs | 15 + .../refine_from_removal.foo.Refinements.diff | 97 +++++ .../refinements/refine_from_removal.rs | 25 ++ .../remove_otherwise.foo.Refinements.diff | 47 +++ .../mir-opt/refinements/remove_otherwise.rs | 15 + 15 files changed, 806 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_mir_transform/src/refinements.rs create mode 100644 src/test/mir-opt/refinements/double_match.foo.Refinements.diff create mode 100644 src/test/mir-opt/refinements/double_match.rs create mode 100644 src/test/mir-opt/refinements/or_pattern.foo.Refinements.diff create mode 100644 src/test/mir-opt/refinements/or_pattern.rs create mode 100644 src/test/mir-opt/refinements/otherwise_refinements.foo.Refinements.diff create mode 100644 src/test/mir-opt/refinements/otherwise_refinements.rs create mode 100644 src/test/mir-opt/refinements/refine_from_removal.foo.Refinements.diff create mode 100644 src/test/mir-opt/refinements/refine_from_removal.rs create mode 100644 src/test/mir-opt/refinements/remove_otherwise.foo.Refinements.diff create mode 100644 src/test/mir-opt/refinements/remove_otherwise.rs diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index e78b6fd092de2..f3a08d0b0edb2 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -417,6 +417,13 @@ impl<'tcx> TerminatorKind<'tcx> { } } + pub fn switch_targets_mut(&mut self) -> Option<&mut SwitchTargets> { + match self { + TerminatorKind::SwitchInt { targets, .. } => Some(targets), + _ => None, + } + } + pub fn as_goto(&self) -> Option { match self { TerminatorKind::Goto { target } => Some(*target), diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index f0c9ac4c504a3..77c555b7c969f 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -530,6 +530,7 @@ impl EffectIndex { } } +#[derive(Debug)] pub struct SwitchIntTarget { pub value: Option, pub target: BasicBlock, diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 2f3de52965db1..8804a55a09326 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -31,7 +31,7 @@ pub use self::drop_flag_effects::{ pub use self::framework::{ fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor, ResultsRefCursor, - ResultsVisitable, ResultsVisitor, + ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, }; use self::move_paths::MoveData; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index cd42edce23d7e..78d2ce3cb5714 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -12,6 +12,7 @@ #![feature(never_type)] #![feature(trusted_step)] #![feature(try_blocks)] +#![feature(if_let_guard)] #![recursion_limit = "256"] #![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] @@ -62,6 +63,7 @@ mod match_branches; mod multiple_return_terminators; mod normalize_array_len; mod nrvo; +mod refinements; mod remove_noop_landing_pads; mod remove_storage_markers; mod remove_unneeded_drops; @@ -507,6 +509,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // The main optimizations that we do on MIR. let optimizations: &[&dyn MirPass<'tcx>] = &[ + &refinements::Refinements, &remove_storage_markers::RemoveStorageMarkers, &remove_zsts::RemoveZsts, &const_goto::ConstGoto, diff --git a/compiler/rustc_mir_transform/src/refinements.rs b/compiler/rustc_mir_transform/src/refinements.rs new file mode 100644 index 0000000000000..2f9c60d9750e3 --- /dev/null +++ b/compiler/rustc_mir_transform/src/refinements.rs @@ -0,0 +1,396 @@ +use std::ops::RangeInclusive; + +use crate::MirPass; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_mir_dataflow::{ + fmt::DebugWithContext, Analysis, AnalysisDomain, Engine, JoinSemiLattice, SwitchIntEdgeEffects, +}; + +pub struct Refinements; + +impl<'tcx> MirPass<'tcx> for Refinements { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.mir_opt_level() < 4 { + return; + } + + let body_ref = &*body; + let has_refine = HasRefinement { body: &body_ref }; + let mut results = Engine::new_generic(tcx, body_ref, has_refine) + .iterate_to_fixpoint() + .into_results_cursor(body); + + #[derive(Debug)] + enum NewTerm { + Unreachable, + SwitchInt(Vec<(u128, BasicBlock)>, BasicBlock), + } + let mut mutate_buffer = Vec::new(); + for (bb, block) in body.basic_blocks().iter_enumerated() { + let terminator_loc = body.terminator_loc(bb); + if let TerminatorKind::SwitchInt { discr, targets, .. } = &block.terminator().kind { + results.seek_before_primary_effect(terminator_loc); + let state = results.get(); + + let discr_refines = match discr.place() { + Some(discr_p) if discr_p.projection.is_empty() => { + state.0[discr_p.local.as_usize()] + } + _ => continue, + }; + + let mut new_targets = targets + .iter() + .filter(|&(value, _)| { + RefineRange::from(value..=value).is_subtype_of(discr_refines) + }) + .collect::>(); + + let new_terminator = match RefineRange::for_otherwise_arm(discr_refines, &targets) { + None => match new_targets.pop() { + None => NewTerm::Unreachable, + Some((_, target_bb)) => NewTerm::SwitchInt(new_targets, target_bb), + }, + Some(_) => NewTerm::SwitchInt(new_targets, targets.otherwise()), + }; + + match &new_terminator { + NewTerm::Unreachable => { + debug!("Refinements::run_pass: new_terminator=Unreachable for bb={:?}", bb) + } + NewTerm::SwitchInt(new_targets, _) + if new_targets.len() + 1 < targets.all_targets().len() => + { + debug!( + "Refinements::run_pass: new_terminator={:?} for bb={:?}", + &new_terminator, bb, + ); + } + _ => (), + }; + + mutate_buffer.push((bb, new_terminator)); + } + } + + for (bb, new_terminator) in mutate_buffer.into_iter() { + let block = &mut body.basic_blocks_mut()[bb]; + let term_kind = &mut block.terminator_mut().kind; + let targets = term_kind.switch_targets_mut().unwrap(); + + match new_terminator { + NewTerm::Unreachable => *term_kind = TerminatorKind::Unreachable, + NewTerm::SwitchInt(new_targets, otherwise) => { + *targets = SwitchTargets::new(new_targets.into_iter(), otherwise) + } + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +/// FIXME: handle signedness of refinements, currently we create *extremely* +/// large refinements for things like `-1..=0` which would give us `0..=uN::MAX` +struct RefineRange { + start: u128, + end: u128, +} + +impl RefineRange { + /// Returns a refinement that both `a` and `b` can be turned into + /// i.e. for `mutual_supertype_of(0..=10, 5..=15)` we return `0..=15` + /// (also known as union of the union of the two patterns) + /// FIXME: for the case of `mutual_supertype_of(0..=10, 20..=30)` + /// we return `0..=30`, eventually we should support "gapped" + /// refinements and instead return `0..=10 | 20..=30` + fn mutual_supertype_of( + a: impl Into>, + b: impl Into>, + ) -> Option { + let a = a.into(); + let b = b.into(); + match (a, b) { + (None, other) | (other, None) => other, + (Some(a), Some(b)) => { + let range = (u128::min(a.start, b.start))..=(u128::max(a.end, b.end)); + Some(RefineRange::from(range)) + } + } + } + + fn is_subtype_of(self, sup: impl Into>) -> bool { + match sup.into() { + None => false, + Some(sup) => sup.start <= self.start && sup.end >= self.end, + } + } + + /// Returns a refinement which can be turned into both `a` and `b` + /// i.e. `mutual_subtype_of(0..=10, 5..=15)` would return `5..=10` + /// (also known as the overlap/intersection of the two patterns) + /// Returns `None` in the case where there is no overlap/subtype + fn mutual_subtype_of( + a: impl Into>, + b: impl Into>, + ) -> Option { + let a = a.into()?; + let b = b.into()?; + Some(if a.is_subtype_of(b) { + a + } else if b.is_subtype_of(a) { + b + } else if a.start >= b.start && a.start <= b.end && a.end > b.end { + // lhs point of `a` is inside of `b` + RefineRange::from(a.start..=b.end) + } else if b.start >= a.start && b.start <= a.end && b.end > a.end { + // lhs point of `b` is inside of `a` + RefineRange::from(b.start..=a.end) + } else { + // refine ranges are disjoint there is no mutual subtype + // except for `None` + return None; + }) + } + + /// Returns a `RefineRange` representing the refinements that can be applied to the + /// `switchInt`'d on place when the `otherwise` target is taken. + fn for_otherwise_arm( + discr_refines: Option, + targets: &SwitchTargets, + ) -> Option { + discr_refines.and_then(|mut discr_refines| { + for (value, _) in targets.iter() { + if value == discr_refines.start { + match discr_refines.start { + u128::MAX => return None, + _ => discr_refines.start += 1, + } + } + + if value == discr_refines.end { + match discr_refines.end { + 0 => return None, + _ => discr_refines.end -= 1, + } + } + + if discr_refines.start > discr_refines.end { + return None; + } + } + Some(discr_refines) + }) + } +} + +impl From> for RefineRange { + fn from(range: RangeInclusive) -> Self { + Self { start: *range.start(), end: *range.end() } + } +} + +struct HasRefinement<'a, 'tcx> { + body: &'a Body<'tcx>, +} + +impl<'a, 'tcx> HasRefinement<'a, 'tcx> { + fn get_switch_int_targets(&self, bb: BasicBlock) -> &SwitchTargets { + match &self.body.basic_blocks()[bb].terminator().kind { + TerminatorKind::SwitchInt { targets, .. } => targets, + _ => panic!(""), + } + } +} + +#[derive(PartialEq, Eq, Clone, Debug)] +struct LocalRefines(Box<[Option]>); + +impl LocalRefines { + /// Sets every refinement to `..` / `_` + fn clear_all(&mut self) { + for refine in self.0.iter_mut() { + *refine = Some(RefineRange::from(0..=(u128::MAX))); + } + } + + /// Sets refinement of `local` to `..` / `_` + fn clear(&mut self, local: Local) { + self.0[local.as_usize()] = Some(RefineRange::from(0..=(u128::MAX))); + } + + fn set(&mut self, local: Local, refine: impl Into>) { + self.0[local.as_usize()] = refine.into(); + } +} + +impl JoinSemiLattice for LocalRefines { + fn join(&mut self, other: &Self) -> bool { + let mut mutated = false; + for (lhs, rhs) in self.0.iter_mut().zip(other.0.iter()) { + let sup = RefineRange::mutual_supertype_of(*lhs, *rhs); + mutated |= *lhs != sup; + *lhs = sup; + } + mutated + } +} + +impl DebugWithContext for LocalRefines { + fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } +} + +impl<'a, 'tcx> AnalysisDomain<'tcx> for HasRefinement<'a, 'tcx> { + type Domain = LocalRefines; + + const NAME: &'static str = "has_refinement"; + + fn bottom_value(&self, body: &rustc_middle::mir::Body<'tcx>) -> Self::Domain { + // a refine of `None` conceptually represents a refinement like `is !` it gets + // created whenever propagating refinements along edges that come from dead code: + // the `2 => { ... }` arm below would get a refinement of `is !` on the local + // `match 1 { 1 => { ...}, 2 => { ... }}` + LocalRefines(vec![None; body.local_decls.len()].into_boxed_slice()) + } + + fn initialize_start_block(&self, _: &rustc_middle::mir::Body<'tcx>, state: &mut Self::Domain) { + state.clear_all(); + } +} + +impl<'a, 'tcx> Analysis<'tcx> for HasRefinement<'a, 'tcx> { + fn apply_statement_effect( + &self, + trans: &mut Self::Domain, + statement: &rustc_middle::mir::Statement<'tcx>, + _: Location, + ) { + use StatementKind::*; + match &statement.kind { + SetDiscriminant { place, .. } => trans.clear(place.local), + Assign(box (lhs_p, rhs)) => { + trans.clear(lhs_p.local); + + use Rvalue::*; + match rhs { + Use(Operand::Move(rhs_p) | Operand::Copy(rhs_p)) => { + if lhs_p.projection.is_empty() && rhs_p.projection.is_empty() { + let rhs_refines = trans.0[rhs_p.local.as_usize()]; + trans.set(lhs_p.local, rhs_refines); + } + } + // FIXME this should probably do something + Use(Operand::Constant(_)) => (), + // clear the refinement regardless of mutability because `UnsafeCell` scares me + AddressOf(_, ref_p) => trans.clear(ref_p.local), + Ref(_, borrow_kind, ref_p) => { + match borrow_kind { + BorrowKind::Shared + // FIXME probably fine without projection refinements + | BorrowKind::Shallow + | BorrowKind::Unique => (), + BorrowKind::Mut { .. } => trans.clear(ref_p.local), + } + }, + // FIXME this should probably do something + Aggregate(_, _) => (), + Repeat(_, _) + | ThreadLocalRef(_) + | Len(_) + | Discriminant(_) + | Cast(_, _, _) + | BinaryOp(_, _) + | CheckedBinaryOp(_, _) + | NullaryOp(_, _) + | UnaryOp(_, _) + | ShallowInitBox(_, _) => (), + } + } + // FIXME can probably clear less than this + LlvmInlineAsm(_) => trans.clear_all(), + // I dont think this matters but can't hurt + StorageDead(local) => trans.clear(*local), + // doesn't matter + FakeRead(_) + | StorageLive(_) + | Retag(_, _) + | AscribeUserType(_, _) + | Coverage(_) + // CopyNonOverlapping isnt important because we don't have + // refinements on borrows yet + | CopyNonOverlapping(_) + | Nop => (), + } + } + + fn apply_terminator_effect( + &self, + trans: &mut Self::Domain, + terminator: &rustc_middle::mir::Terminator<'tcx>, + _: Location, + ) { + use TerminatorKind::*; + match &terminator.kind { + Drop { place, .. } => trans.clear(place.local), + // we can probably do better here and give `place` `value`'s refinements + DropAndReplace { place, .. } => trans.clear(place.local), + // this is probably overly conservative but sound + InlineAsm { .. } => trans.clear_all(), + // handled in `apply_call_return_effect` + Call { .. } => (), + // doesn't matter + Goto { .. } + | SwitchInt { .. } + | Resume + | Abort + | Return + | Unreachable + | Assert { .. } + | Yield { .. } + | GeneratorDrop + | FalseEdge { .. } + | FalseUnwind { .. } => (), + } + } + + fn apply_call_return_effect( + &self, + trans: &mut Self::Domain, + _: BasicBlock, + _: &rustc_middle::mir::Operand<'tcx>, + _: &[rustc_middle::mir::Operand<'tcx>], + return_place: rustc_middle::mir::Place<'tcx>, + ) { + trans.clear(return_place.local); + } + + #[instrument(level = "debug", skip(self, edge_effects))] + fn apply_switch_int_edge_effects( + &self, + block: BasicBlock, + discr: &rustc_middle::mir::Operand<'tcx>, + edge_effects: &mut impl SwitchIntEdgeEffects, + ) { + edge_effects.apply(|trans, target| { + if let Some(discr_p) = discr.place() { + if discr_p.projection.is_empty() { + let discr_refine = trans.0[discr_p.local.as_usize()]; + let refine = target + .value + .map(|v| Some(RefineRange::from(v..=v))) + .unwrap_or_else(|| { + RefineRange::for_otherwise_arm( + discr_refine, + self.get_switch_int_targets(block), + ) + }) + .and_then(|refine| RefineRange::mutual_subtype_of(refine, discr_refine)); + debug!("applying refine={:?} on edge={:?}", refine, target,); + trans.set(discr_p.local, refine); + } + } + }); + } +} diff --git a/src/test/mir-opt/refinements/double_match.foo.Refinements.diff b/src/test/mir-opt/refinements/double_match.foo.Refinements.diff new file mode 100644 index 0000000000000..e1e35f443ea89 --- /dev/null +++ b/src/test/mir-opt/refinements/double_match.foo.Refinements.diff @@ -0,0 +1,52 @@ +- // MIR for `foo` before Refinements ++ // MIR for `foo` after Refinements + + fn foo(_1: u32) -> i32 { + debug a => _1; // in scope 0 at $DIR/double_match.rs:3:8: 3:9 + let mut _0: i32; // return place in scope 0 at $DIR/double_match.rs:3:19: 3:22 + let _2: u32; // in scope 0 at $DIR/double_match.rs:5:9: 5:14 + scope 1 { + debug b => _2; // in scope 1 at $DIR/double_match.rs:5:9: 5:14 + } + + bb0: { + switchInt(_1) -> [1_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/double_match.rs:4:5: 4:12 + } + + bb1: { + _0 = const 3_i32; // scope 0 at $DIR/double_match.rs:10:14: 10:15 + goto -> bb7; // scope 0 at $DIR/double_match.rs:10:14: 10:15 + } + + bb2: { + StorageLive(_2); // scope 0 at $DIR/double_match.rs:5:9: 5:14 + _2 = _1; // scope 0 at $DIR/double_match.rs:5:9: 5:14 +- switchInt(_2) -> [1_u32: bb4, 2_u32: bb5, otherwise: bb3]; // scope 1 at $DIR/double_match.rs:5:18: 5:25 ++ switchInt(_2) -> bb4; // scope 1 at $DIR/double_match.rs:5:18: 5:25 + } + + bb3: { + _0 = const 3_i32; // scope 1 at $DIR/double_match.rs:8:18: 8:19 + goto -> bb6; // scope 1 at $DIR/double_match.rs:8:18: 8:19 + } + + bb4: { + _0 = const 1_i32; // scope 1 at $DIR/double_match.rs:6:18: 6:19 + goto -> bb6; // scope 1 at $DIR/double_match.rs:6:18: 6:19 + } + + bb5: { + _0 = const 2_i32; // scope 1 at $DIR/double_match.rs:7:18: 7:19 + goto -> bb6; // scope 1 at $DIR/double_match.rs:7:18: 7:19 + } + + bb6: { + StorageDead(_2); // scope 0 at $DIR/double_match.rs:9:9: 9:10 + goto -> bb7; // scope 0 at $DIR/double_match.rs:9:9: 9:10 + } + + bb7: { + return; // scope 0 at $DIR/double_match.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/refinements/double_match.rs b/src/test/mir-opt/refinements/double_match.rs new file mode 100644 index 0000000000000..94cab3728ea26 --- /dev/null +++ b/src/test/mir-opt/refinements/double_match.rs @@ -0,0 +1,16 @@ +// compile-flags: -Z mir-opt-level=4 +// EMIT_MIR double_match.foo.Refinements.diff +fn foo(a: u32) -> i32 { + match a { + b @ 1 => match b { + 1 => 1, + 2 => 2, + _ => 3, + }, + _ => 3, + } +} + +fn main() { + foo(10); +} diff --git a/src/test/mir-opt/refinements/or_pattern.foo.Refinements.diff b/src/test/mir-opt/refinements/or_pattern.foo.Refinements.diff new file mode 100644 index 0000000000000..db3faefd6e07f --- /dev/null +++ b/src/test/mir-opt/refinements/or_pattern.foo.Refinements.diff @@ -0,0 +1,67 @@ +- // MIR for `foo` before Refinements ++ // MIR for `foo` after Refinements + + fn foo(_1: u32) -> u32 { + debug b => _1; // in scope 0 at $DIR/or_pattern.rs:3:8: 3:9 + let mut _0: u32; // return place in scope 0 at $DIR/or_pattern.rs:3:19: 3:22 + let _2: u32; // in scope 0 at $DIR/or_pattern.rs:5:9: 5:20 + scope 1 { + debug c => _2; // in scope 1 at $DIR/or_pattern.rs:5:9: 5:20 + } + + bb0: { + switchInt(_1) -> [1_u32: bb3, 2_u32: bb4, otherwise: bb1]; // scope 0 at $DIR/or_pattern.rs:4:5: 4:12 + } + + bb1: { + _0 = const 1_u32; // scope 0 at $DIR/or_pattern.rs:11:14: 11:15 + goto -> bb10; // scope 0 at $DIR/or_pattern.rs:11:14: 11:15 + } + + bb2: { +- switchInt(_2) -> [1_u32: bb6, 2_u32: bb7, 3_u32: bb8, otherwise: bb5]; // scope 1 at $DIR/or_pattern.rs:5:24: 5:31 ++ switchInt(_2) -> [1_u32: bb6, otherwise: bb7]; // scope 1 at $DIR/or_pattern.rs:5:24: 5:31 + } + + bb3: { + StorageLive(_2); // scope 0 at $DIR/or_pattern.rs:5:9: 5:20 + _2 = _1; // scope 0 at $DIR/or_pattern.rs:5:9: 5:20 + goto -> bb2; // scope 0 at $DIR/or_pattern.rs:4:5: 12:6 + } + + bb4: { + StorageLive(_2); // scope 0 at $DIR/or_pattern.rs:5:9: 5:20 + _2 = _1; // scope 0 at $DIR/or_pattern.rs:5:9: 5:20 + goto -> bb2; // scope 0 at $DIR/or_pattern.rs:4:5: 12:6 + } + + bb5: { + _0 = const 8594_u32; // scope 1 at $DIR/or_pattern.rs:9:18: 9:22 + goto -> bb9; // scope 1 at $DIR/or_pattern.rs:9:18: 9:22 + } + + bb6: { + _0 = const 18_u32; // scope 1 at $DIR/or_pattern.rs:6:18: 6:20 + goto -> bb9; // scope 1 at $DIR/or_pattern.rs:6:18: 6:20 + } + + bb7: { + _0 = const 384_u32; // scope 1 at $DIR/or_pattern.rs:7:18: 7:21 + goto -> bb9; // scope 1 at $DIR/or_pattern.rs:7:18: 7:21 + } + + bb8: { + _0 = const 28732897_u32; // scope 1 at $DIR/or_pattern.rs:8:18: 8:26 + goto -> bb9; // scope 1 at $DIR/or_pattern.rs:8:18: 8:26 + } + + bb9: { + StorageDead(_2); // scope 0 at $DIR/or_pattern.rs:10:9: 10:10 + goto -> bb10; // scope 0 at $DIR/or_pattern.rs:10:9: 10:10 + } + + bb10: { + return; // scope 0 at $DIR/or_pattern.rs:13:2: 13:2 + } + } + diff --git a/src/test/mir-opt/refinements/or_pattern.rs b/src/test/mir-opt/refinements/or_pattern.rs new file mode 100644 index 0000000000000..20a9ffed7dc46 --- /dev/null +++ b/src/test/mir-opt/refinements/or_pattern.rs @@ -0,0 +1,17 @@ +// compile-flags: -Z mir-opt-level=4 +// EMIT_MIR or_pattern.foo.Refinements.diff +fn foo(b: u32) -> u32 { + match b { + c @ (1 | 2) => match c { + 1 => 18, + 2 => 384, + 3 => 28732897, + _ => 8594, + }, + _ => 1, + } +} + +fn main() { + foo(2); +} diff --git a/src/test/mir-opt/refinements/otherwise_refinements.foo.Refinements.diff b/src/test/mir-opt/refinements/otherwise_refinements.foo.Refinements.diff new file mode 100644 index 0000000000000..f7a2c6dad8e16 --- /dev/null +++ b/src/test/mir-opt/refinements/otherwise_refinements.foo.Refinements.diff @@ -0,0 +1,47 @@ +- // MIR for `foo` before Refinements ++ // MIR for `foo` after Refinements + + fn foo(_1: bool) -> u32 { + debug a => _1; // in scope 0 at $DIR/otherwise_refinements.rs:3:8: 3:9 + let mut _0: u32; // return place in scope 0 at $DIR/otherwise_refinements.rs:3:20: 3:23 + let _2: bool; // in scope 0 at $DIR/otherwise_refinements.rs:5:9: 5:17 + scope 1 { + debug b => _2; // in scope 1 at $DIR/otherwise_refinements.rs:5:9: 5:17 + } + + bb0: { + switchInt(_1) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/otherwise_refinements.rs:4:5: 4:12 + } + + bb1: { + _0 = const 28_u32; // scope 0 at $DIR/otherwise_refinements.rs:9:18: 9:20 + goto -> bb6; // scope 0 at $DIR/otherwise_refinements.rs:9:18: 9:20 + } + + bb2: { + StorageLive(_2); // scope 0 at $DIR/otherwise_refinements.rs:5:9: 5:17 + _2 = _1; // scope 0 at $DIR/otherwise_refinements.rs:5:9: 5:17 +- switchInt(_2) -> [false: bb3, otherwise: bb4]; // scope 1 at $DIR/otherwise_refinements.rs:5:21: 5:28 ++ switchInt(_2) -> bb4; // scope 1 at $DIR/otherwise_refinements.rs:5:21: 5:28 + } + + bb3: { + _0 = const 7_u32; // scope 1 at $DIR/otherwise_refinements.rs:7:22: 7:23 + goto -> bb5; // scope 1 at $DIR/otherwise_refinements.rs:7:22: 7:23 + } + + bb4: { + _0 = const 1_u32; // scope 1 at $DIR/otherwise_refinements.rs:6:21: 6:22 + goto -> bb5; // scope 1 at $DIR/otherwise_refinements.rs:6:21: 6:22 + } + + bb5: { + StorageDead(_2); // scope 0 at $DIR/otherwise_refinements.rs:8:9: 8:10 + goto -> bb6; // scope 0 at $DIR/otherwise_refinements.rs:8:9: 8:10 + } + + bb6: { + return; // scope 0 at $DIR/otherwise_refinements.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/refinements/otherwise_refinements.rs b/src/test/mir-opt/refinements/otherwise_refinements.rs new file mode 100644 index 0000000000000..cd9826a0b8406 --- /dev/null +++ b/src/test/mir-opt/refinements/otherwise_refinements.rs @@ -0,0 +1,15 @@ +// compile-flags: -Z mir-opt-level=4 +// EMIT_MIR otherwise_refinements.foo.Refinements.diff +fn foo(a: bool) -> u32 { + match a { + b @ true => match b { + true => 1, + false => 7, + }, + false => 28, + } +} + +fn main() { + foo(true); +} diff --git a/src/test/mir-opt/refinements/refine_from_removal.foo.Refinements.diff b/src/test/mir-opt/refinements/refine_from_removal.foo.Refinements.diff new file mode 100644 index 0000000000000..d64d01bf5709c --- /dev/null +++ b/src/test/mir-opt/refinements/refine_from_removal.foo.Refinements.diff @@ -0,0 +1,97 @@ +- // MIR for `foo` before Refinements ++ // MIR for `foo` after Refinements + + fn foo(_1: u32) -> u32 { + debug a => _1; // in scope 0 at $DIR/refine_from_removal.rs:3:8: 3:9 + let mut _0: u32; // return place in scope 0 at $DIR/refine_from_removal.rs:3:19: 3:22 + let _2: i32; // in scope 0 at $DIR/refine_from_removal.rs:4:9: 4:10 + let _3: u32; // in scope 0 at $DIR/refine_from_removal.rs:5:9: 5:20 + scope 1 { + debug c => _2; // in scope 1 at $DIR/refine_from_removal.rs:4:9: 4:10 + } + scope 2 { + debug b => _3; // in scope 2 at $DIR/refine_from_removal.rs:5:9: 5:20 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/refine_from_removal.rs:4:9: 4:10 + switchInt(_1) -> [1_u32: bb3, 2_u32: bb4, otherwise: bb1]; // scope 0 at $DIR/refine_from_removal.rs:4:13: 4:20 + } + + bb1: { + _2 = const 1_i32; // scope 0 at $DIR/refine_from_removal.rs:11:14: 11:15 + goto -> bb10; // scope 0 at $DIR/refine_from_removal.rs:11:14: 11:15 + } + + bb2: { +- switchInt(_3) -> [1_u32: bb6, 2_u32: bb7, 3_u32: bb8, otherwise: bb5]; // scope 2 at $DIR/refine_from_removal.rs:5:24: 5:31 ++ switchInt(_3) -> [1_u32: bb6, otherwise: bb7]; // scope 2 at $DIR/refine_from_removal.rs:5:24: 5:31 + } + + bb3: { + StorageLive(_3); // scope 0 at $DIR/refine_from_removal.rs:5:9: 5:20 + _3 = _1; // scope 0 at $DIR/refine_from_removal.rs:5:9: 5:20 + goto -> bb2; // scope 0 at $DIR/refine_from_removal.rs:4:13: 12:6 + } + + bb4: { + StorageLive(_3); // scope 0 at $DIR/refine_from_removal.rs:5:9: 5:20 + _3 = _1; // scope 0 at $DIR/refine_from_removal.rs:5:9: 5:20 + goto -> bb2; // scope 0 at $DIR/refine_from_removal.rs:4:13: 12:6 + } + + bb5: { + _2 = const 8594_i32; // scope 2 at $DIR/refine_from_removal.rs:9:18: 9:22 + goto -> bb9; // scope 2 at $DIR/refine_from_removal.rs:9:18: 9:22 + } + + bb6: { + _2 = const 18_i32; // scope 2 at $DIR/refine_from_removal.rs:6:18: 6:20 + goto -> bb9; // scope 2 at $DIR/refine_from_removal.rs:6:18: 6:20 + } + + bb7: { + _2 = const 384_i32; // scope 2 at $DIR/refine_from_removal.rs:7:18: 7:21 + goto -> bb9; // scope 2 at $DIR/refine_from_removal.rs:7:18: 7:21 + } + + bb8: { + _2 = const 28732897_i32; // scope 2 at $DIR/refine_from_removal.rs:8:18: 8:26 + goto -> bb9; // scope 2 at $DIR/refine_from_removal.rs:8:18: 8:26 + } + + bb9: { + StorageDead(_3); // scope 0 at $DIR/refine_from_removal.rs:10:9: 10:10 + goto -> bb10; // scope 0 at $DIR/refine_from_removal.rs:10:9: 10:10 + } + + bb10: { + switchInt(_2) -> [18_i32: bb12, 384_i32: bb13, 1_i32: bb14, otherwise: bb11]; // scope 1 at $DIR/refine_from_removal.rs:14:5: 14:12 + } + + bb11: { + _0 = const 6_u32; // scope 1 at $DIR/refine_from_removal.rs:18:14: 18:15 + goto -> bb15; // scope 1 at $DIR/refine_from_removal.rs:18:14: 18:15 + } + + bb12: { + _0 = const 3_u32; // scope 1 at $DIR/refine_from_removal.rs:15:15: 15:16 + goto -> bb15; // scope 1 at $DIR/refine_from_removal.rs:15:15: 15:16 + } + + bb13: { + _0 = const 4_u32; // scope 1 at $DIR/refine_from_removal.rs:16:16: 16:17 + goto -> bb15; // scope 1 at $DIR/refine_from_removal.rs:16:16: 16:17 + } + + bb14: { + _0 = const 5_u32; // scope 1 at $DIR/refine_from_removal.rs:17:14: 17:15 + goto -> bb15; // scope 1 at $DIR/refine_from_removal.rs:17:14: 17:15 + } + + bb15: { + StorageDead(_2); // scope 0 at $DIR/refine_from_removal.rs:21:1: 21:2 + return; // scope 0 at $DIR/refine_from_removal.rs:21:2: 21:2 + } + } + diff --git a/src/test/mir-opt/refinements/refine_from_removal.rs b/src/test/mir-opt/refinements/refine_from_removal.rs new file mode 100644 index 0000000000000..30ed2387f119d --- /dev/null +++ b/src/test/mir-opt/refinements/refine_from_removal.rs @@ -0,0 +1,25 @@ +// compile-flags: -Z mir-opt-level=4 +// EMIT_MIR refine_from_removal.foo.Refinements.diff +fn foo(a: u32) -> u32 { + let c = match a { + b @ (1 | 2) => match b { + 1 => 18, + 2 => 384, + 3 => 28732897, + _ => 8594, + }, + _ => 1, + }; + + match c { + 18 => 3, + 384 => 4, + 1 => 5, + _ => 6, + // FIXME: this currently doesnt optimise out the `1 =>` and `_ =>` arms + } +} + +fn main() { + foo(10); +} diff --git a/src/test/mir-opt/refinements/remove_otherwise.foo.Refinements.diff b/src/test/mir-opt/refinements/remove_otherwise.foo.Refinements.diff new file mode 100644 index 0000000000000..d44bf51085055 --- /dev/null +++ b/src/test/mir-opt/refinements/remove_otherwise.foo.Refinements.diff @@ -0,0 +1,47 @@ +- // MIR for `foo` before Refinements ++ // MIR for `foo` after Refinements + + fn foo(_1: bool) -> u32 { + debug a => _1; // in scope 0 at $DIR/remove_otherwise.rs:3:8: 3:9 + let mut _0: u32; // return place in scope 0 at $DIR/remove_otherwise.rs:3:20: 3:23 + let _2: bool; // in scope 0 at $DIR/remove_otherwise.rs:5:9: 5:18 + scope 1 { + debug b => _2; // in scope 1 at $DIR/remove_otherwise.rs:5:9: 5:18 + } + + bb0: { + switchInt(_1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_otherwise.rs:4:5: 4:12 + } + + bb1: { + _0 = const 28_u32; // scope 0 at $DIR/remove_otherwise.rs:9:17: 9:19 + goto -> bb6; // scope 0 at $DIR/remove_otherwise.rs:9:17: 9:19 + } + + bb2: { + StorageLive(_2); // scope 0 at $DIR/remove_otherwise.rs:5:9: 5:18 + _2 = _1; // scope 0 at $DIR/remove_otherwise.rs:5:9: 5:18 +- switchInt(_2) -> [false: bb3, otherwise: bb4]; // scope 1 at $DIR/remove_otherwise.rs:5:22: 5:29 ++ switchInt(_2) -> bb3; // scope 1 at $DIR/remove_otherwise.rs:5:22: 5:29 + } + + bb3: { + _0 = const 7_u32; // scope 1 at $DIR/remove_otherwise.rs:7:22: 7:23 + goto -> bb5; // scope 1 at $DIR/remove_otherwise.rs:7:22: 7:23 + } + + bb4: { + _0 = const 1_u32; // scope 1 at $DIR/remove_otherwise.rs:6:21: 6:22 + goto -> bb5; // scope 1 at $DIR/remove_otherwise.rs:6:21: 6:22 + } + + bb5: { + StorageDead(_2); // scope 0 at $DIR/remove_otherwise.rs:8:9: 8:10 + goto -> bb6; // scope 0 at $DIR/remove_otherwise.rs:8:9: 8:10 + } + + bb6: { + return; // scope 0 at $DIR/remove_otherwise.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/refinements/remove_otherwise.rs b/src/test/mir-opt/refinements/remove_otherwise.rs new file mode 100644 index 0000000000000..89f615e0d5d14 --- /dev/null +++ b/src/test/mir-opt/refinements/remove_otherwise.rs @@ -0,0 +1,15 @@ +// compile-flags: -Z mir-opt-level=4 +// EMIT_MIR remove_otherwise.foo.Refinements.diff +fn foo(a: bool) -> u32 { + match a { + b @ false => match b { + true => 1, + false => 7, + }, + true => 28, + } +} + +fn main() { + foo(true); +} From 63e3d4c89322a74d43717b7ac82a89c26aa0196b Mon Sep 17 00:00:00 2001 From: Ellen Date: Sun, 24 Oct 2021 21:32:38 +0100 Subject: [PATCH 2/3] comment --- compiler/rustc_mir_transform/src/refinements.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_mir_transform/src/refinements.rs b/compiler/rustc_mir_transform/src/refinements.rs index 2f9c60d9750e3..7e66548e37643 100644 --- a/compiler/rustc_mir_transform/src/refinements.rs +++ b/compiler/rustc_mir_transform/src/refinements.rs @@ -159,6 +159,10 @@ impl RefineRange { discr_refines: Option, targets: &SwitchTargets, ) -> Option { + // FIXME this logic is kinda janky, i.e. if we have a range `0..=3` + // with arms 2 and 3 in that order than we'll end up with a range `0..=2` + // rather than `0..=1`... This is probably fine for now, this will all + // be scrapped when we eventually support OR patterns properly. discr_refines.and_then(|mut discr_refines| { for (value, _) in targets.iter() { if value == discr_refines.start { From 8c16a25c83af14e00ef13bf65a93484526cec4a2 Mon Sep 17 00:00:00 2001 From: Ellen Date: Sun, 24 Oct 2021 21:39:49 +0100 Subject: [PATCH 3/3] comment --- compiler/rustc_mir_transform/src/refinements.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/refinements.rs b/compiler/rustc_mir_transform/src/refinements.rs index 7e66548e37643..4109ac3098391 100644 --- a/compiler/rustc_mir_transform/src/refinements.rs +++ b/compiler/rustc_mir_transform/src/refinements.rs @@ -273,6 +273,7 @@ impl<'a, 'tcx> Analysis<'tcx> for HasRefinement<'a, 'tcx> { ) { use StatementKind::*; match &statement.kind { + // FIXME this should set some refinements SetDiscriminant { place, .. } => trans.clear(place.local), Assign(box (lhs_p, rhs)) => { trans.clear(lhs_p.local); @@ -300,10 +301,11 @@ impl<'a, 'tcx> Analysis<'tcx> for HasRefinement<'a, 'tcx> { }, // FIXME this should probably do something Aggregate(_, _) => (), + // FIXME this should set some refinements + Discriminant(_) => (), Repeat(_, _) | ThreadLocalRef(_) | Len(_) - | Discriminant(_) | Cast(_, _, _) | BinaryOp(_, _) | CheckedBinaryOp(_, _)