|
| 1 | +//! The general point of the optimizations provided here is to simplify something like: |
| 2 | +//! |
| 3 | +//! ```rust |
| 4 | +//! match x { |
| 5 | +//! Ok(x) => Ok(x), |
| 6 | +//! Err(x) => Err(x) |
| 7 | +//! } |
| 8 | +//! ``` |
| 9 | +//! |
| 10 | +//! into just `x`. |
| 11 | +
|
| 12 | +use crate::transform::{MirPass, MirSource, simplify}; |
| 13 | +use rustc::ty::{TyCtxt, Ty}; |
| 14 | +use rustc::mir::*; |
| 15 | +use rustc_target::abi::VariantIdx; |
| 16 | +use itertools::Itertools as _; |
| 17 | + |
| 18 | +/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move. |
| 19 | +/// |
| 20 | +/// This is done by transforming basic blocks where the statements match: |
| 21 | +/// |
| 22 | +/// ```rust |
| 23 | +/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY ); |
| 24 | +/// ((_LOCAL_0 as Variant).FIELD: TY) = move _LOCAL_TMP; |
| 25 | +/// discriminant(_LOCAL_0) = VAR_IDX; |
| 26 | +/// ``` |
| 27 | +/// |
| 28 | +/// into: |
| 29 | +/// |
| 30 | +/// ```rust |
| 31 | +/// _LOCAL_0 = move _LOCAL_1 |
| 32 | +/// ``` |
| 33 | +pub struct SimplifyArmIdentity; |
| 34 | + |
| 35 | +impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { |
| 36 | + fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { |
| 37 | + for bb in body.basic_blocks_mut() { |
| 38 | + // Need 3 statements: |
| 39 | + let (s0, s1, s2) = match &mut *bb.statements { |
| 40 | + [s0, s1, s2] => (s0, s1, s2), |
| 41 | + _ => continue, |
| 42 | + }; |
| 43 | + |
| 44 | + // Pattern match on the form we want: |
| 45 | + let (local_tmp_s0, local_1, vf_s0) = match match_get_variant_field(s0) { |
| 46 | + None => continue, |
| 47 | + Some(x) => x, |
| 48 | + }; |
| 49 | + let (local_tmp_s1, local_0, vf_s1) = match match_set_variant_field(s1) { |
| 50 | + None => continue, |
| 51 | + Some(x) => x, |
| 52 | + }; |
| 53 | + if local_tmp_s0 != local_tmp_s1 |
| 54 | + || vf_s0 != vf_s1 |
| 55 | + || Some((local_0, vf_s0.var_idx)) != match_set_discr(s2) |
| 56 | + { |
| 57 | + continue; |
| 58 | + } |
| 59 | + |
| 60 | + // Right shape; transform! |
| 61 | + match &mut s0.kind { |
| 62 | + StatementKind::Assign(box (place, rvalue)) => { |
| 63 | + *place = local_0.into(); |
| 64 | + *rvalue = Rvalue::Use(Operand::Move(local_1.into())); |
| 65 | + } |
| 66 | + _ => unreachable!(), |
| 67 | + } |
| 68 | + s1.make_nop(); |
| 69 | + s2.make_nop(); |
| 70 | + } |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +/// Match on: |
| 75 | +/// ```rust |
| 76 | +/// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); |
| 77 | +/// ``` |
| 78 | +fn match_get_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { |
| 79 | + match &stmt.kind { |
| 80 | + StatementKind::Assign(box (place_into, rvalue_from)) => match rvalue_from { |
| 81 | + Rvalue::Use(Operand::Copy(pf)) | Rvalue::Use(Operand::Move(pf)) => { |
| 82 | + let local_into = place_into.as_local()?; |
| 83 | + let (local_from, vf) = match_variant_field_place(&pf)?; |
| 84 | + Some((local_into, local_from, vf)) |
| 85 | + } |
| 86 | + _ => None, |
| 87 | + }, |
| 88 | + _ => None, |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +/// Match on: |
| 93 | +/// ```rust |
| 94 | +/// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO; |
| 95 | +/// ``` |
| 96 | +fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { |
| 97 | + match &stmt.kind { |
| 98 | + StatementKind::Assign(box (place_from, rvalue_into)) => match rvalue_into { |
| 99 | + Rvalue::Use(Operand::Move(place_into)) => { |
| 100 | + let local_into = place_into.as_local()?; |
| 101 | + let (local_from, vf) = match_variant_field_place(&place_from)?; |
| 102 | + Some((local_into, local_from, vf)) |
| 103 | + } |
| 104 | + _ => None, |
| 105 | + }, |
| 106 | + _ => None, |
| 107 | + } |
| 108 | +} |
| 109 | + |
| 110 | +/// Match on: |
| 111 | +/// ```rust |
| 112 | +/// discriminant(_LOCAL_TO_SET) = VAR_IDX; |
| 113 | +/// ``` |
| 114 | +fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> { |
| 115 | + match &stmt.kind { |
| 116 | + StatementKind::SetDiscriminant { place, variant_index } => Some(( |
| 117 | + place.as_local()?, |
| 118 | + *variant_index |
| 119 | + )), |
| 120 | + _ => None, |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +#[derive(PartialEq)] |
| 125 | +struct VarField<'tcx> { |
| 126 | + field: Field, |
| 127 | + field_ty: Ty<'tcx>, |
| 128 | + var_idx: VariantIdx, |
| 129 | +} |
| 130 | + |
| 131 | +/// Match on `((_LOCAL as Variant).FIELD: TY)`. |
| 132 | +fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarField<'tcx>)> { |
| 133 | + match place.as_ref() { |
| 134 | + PlaceRef { |
| 135 | + base: &PlaceBase::Local(local), |
| 136 | + projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)], |
| 137 | + } => Some((local, VarField { field, field_ty: ty, var_idx })), |
| 138 | + _ => None, |
| 139 | + } |
| 140 | +} |
| 141 | + |
| 142 | +/// Simplifies `SwitchInt(_) -> [targets]`, |
| 143 | +/// where all the `targets` have the same form, |
| 144 | +/// into `goto -> target_first`. |
| 145 | +pub struct SimplifyBranchSame; |
| 146 | + |
| 147 | +impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { |
| 148 | + fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { |
| 149 | + let bbs = body.basic_blocks_mut(); |
| 150 | + for bb_idx in bbs.indices() { |
| 151 | + let targets = match &bbs[bb_idx].terminator().kind { |
| 152 | + TerminatorKind::SwitchInt { targets, .. } => targets, |
| 153 | + _ => continue, |
| 154 | + }; |
| 155 | + |
| 156 | + // Reaching `unreachable` is UB so assume it doesn't happen. |
| 157 | + let mut iter_bbs_reachable = targets |
| 158 | + .iter() |
| 159 | + .map(|idx| (*idx, &bbs[*idx])) |
| 160 | + .filter(|(_, bb)| bb.terminator().kind != TerminatorKind::Unreachable) |
| 161 | + .peekable(); |
| 162 | + |
| 163 | + // We want to `goto -> bb_first`. |
| 164 | + let bb_first = iter_bbs_reachable |
| 165 | + .peek() |
| 166 | + .map(|(idx, _)| *idx) |
| 167 | + .unwrap_or(targets[0]); |
| 168 | + |
| 169 | + // All successor basic blocks should have the exact same form. |
| 170 | + let all_successors_equivalent = iter_bbs_reachable |
| 171 | + .map(|(_, bb)| bb) |
| 172 | + .tuple_windows() |
| 173 | + .all(|(bb_l, bb_r)| { |
| 174 | + bb_l.is_cleanup == bb_r.is_cleanup |
| 175 | + && bb_l.terminator().kind == bb_r.terminator().kind |
| 176 | + && bb_l.statements.iter().eq_by(&bb_r.statements, |x, y| x.kind == y.kind) |
| 177 | + }); |
| 178 | + |
| 179 | + if all_successors_equivalent { |
| 180 | + // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`. |
| 181 | + bbs[bb_idx].terminator_mut().kind = TerminatorKind::Goto { target: bb_first }; |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + // We may have dead blocks now, so remvoe those. |
| 186 | + simplify::remove_dead_blocks(body); |
| 187 | + } |
| 188 | +} |
| 189 | + |
| 190 | +/* |
| 191 | +
|
| 192 | +KEEPSAKE: REMOVE IF NOT NECESSARY! |
| 193 | +
|
| 194 | +fn statement_semantic_eq(sa: &StatementKind<'_>, sb: &StatementKind<'_>) -> bool { |
| 195 | + use StatementKind::*; |
| 196 | + match (sb, sa) { |
| 197 | + (AscribeUserType(pa, va), AscribeUserType(pb, vb)) => pa == pb && va == vb, |
| 198 | + (Assign(a), Assign(b)) => a == b, |
| 199 | + (FakeRead(fa, pa), FakeRead(fb, pb)) => fa == fb && pa == pb, |
| 200 | + (InlineAsm(a), InlineAsm(b)) => a == b, |
| 201 | + (Nop, StatementKind::Nop) => true, |
| 202 | + (Retag(ra, pa), Retag(rb, pb)) => ra == rb && pa == pb, |
| 203 | + ( |
| 204 | + SetDiscriminant { place: pa, variant_index: va }, |
| 205 | + SetDiscriminant { place: pb, variant_index: vb }, |
| 206 | + ) => pa == pb && va == vb, |
| 207 | + (StorageDead(a), StorageDead(b)) => a == b, |
| 208 | + (StorageLive(a), StorageLive(b)) => a == b, |
| 209 | + (AscribeUserType(..), _) | (_, AscribeUserType(..)) |
| 210 | + | (StorageDead(..), _) | (_, StorageDead(..)) |
| 211 | + | (Assign(..), _) | (_, Assign(..)) |
| 212 | + | (FakeRead(..), _) | (_, FakeRead(..)) |
| 213 | + | (InlineAsm(..), _) | (_, InlineAsm(..)) |
| 214 | + | (Nop, _) | (_, Nop) |
| 215 | + | (Retag(..), _) | (_, Retag(..)) |
| 216 | + | (SetDiscriminant { .. }, _) | (_, SetDiscriminant { .. }) => true, |
| 217 | + } |
| 218 | +} |
| 219 | +
|
| 220 | +fn terminator_semantic_eq(ta: &TerminatorKind<'_>, tb: &TerminatorKind<'_>) -> bool { |
| 221 | + use TerminatorKind::*; |
| 222 | + match (ta, tb) { |
| 223 | + (Goto { target: a }, Goto { target: b }) => a == b, |
| 224 | + (Resume, Resume) |
| 225 | + | (Abort, Abort) |
| 226 | + | (Return, Return) |
| 227 | + | (Unreachable, Unreachable) |
| 228 | + | (GeneratorDrop, GeneratorDrop) => true, |
| 229 | + ( |
| 230 | + SwitchInt { discr: da, switch_ty: sa, targets: ta, values: _ }, |
| 231 | + SwitchInt { discr: db, switch_ty: sb, targets: tb, values: _ }, |
| 232 | + ) => da == db && sa == sb && ta == tb, |
| 233 | + ( |
| 234 | + Drop { location: la, target: ta, unwind: ua }, |
| 235 | + Drop { location: lb, target: tb, unwind: ub }, |
| 236 | + ) => la == lb && ta == tb && ua == ub, |
| 237 | + ( |
| 238 | + DropAndReplace { location: la, target: ta, unwind: ua, value: va }, |
| 239 | + DropAndReplace { location: lb, target: tb, unwind: ub, value: vb }, |
| 240 | + ) => la == lb && ta == tb && ua == ub && va == vb, |
| 241 | + ( |
| 242 | + Call { func: fa, args: aa, destination: da, cleanup: ca, from_hir_call: _ }, |
| 243 | + Call { func: fb, args: ab, destination: db, cleanup: cb, from_hir_call: _ }, |
| 244 | + ) => fa == fb && aa == ab && da == db && ca == cb, |
| 245 | + ( |
| 246 | + Assert { cond: coa, expected: ea, msg: ma, target: ta, cleanup: cla }, |
| 247 | + Assert { cond: cob, expected: eb, msg: mb, target: tb, cleanup: clb }, |
| 248 | + ) => coa == cob && ea == eb && ma == mb && ta == tb && cla == clb, |
| 249 | + ( |
| 250 | + Yield { value: va, resume: ra, drop: da }, |
| 251 | + Yield { value: vb, resume: rb, drop: db }, |
| 252 | + ) => va == vb && ra == rb && da == db, |
| 253 | + ( |
| 254 | + FalseEdges { real_target: ra, imaginary_target: ia }, |
| 255 | + FalseEdges { real_target: rb, imaginary_target: ib }, |
| 256 | + ) => ra == rb && ia == ib, |
| 257 | + ( |
| 258 | + FalseUnwind { real_target: ra, unwind: ua }, |
| 259 | + FalseUnwind { real_target: rb, unwind: ub }, |
| 260 | + ) => ra == rb && ua == ub, |
| 261 | + (Goto { .. }, _) | (_, Goto { .. }) |
| 262 | + | (Resume, _) | (_, Resume) |
| 263 | + | (Abort, _) | (_, Abort) |
| 264 | + | (Return, _) | (_, Return) |
| 265 | + | (Unreachable, _) | (_, Unreachable) |
| 266 | + | (GeneratorDrop, _) | (_, GeneratorDrop) |
| 267 | + | (SwitchInt { .. }, _) | (_, SwitchInt { .. }) |
| 268 | + | (Drop { .. }, _) | (_, Drop { .. }) |
| 269 | + | (DropAndReplace { .. }, _) | (_, DropAndReplace { .. }) |
| 270 | + | (Call { .. }, _) | (_, Call { .. }) |
| 271 | + | (Assert { .. }, _) | (_, Assert { .. }) |
| 272 | + | (Yield { .. }, _) | (_, Yield { .. }) |
| 273 | + | (FalseEdges { .. }, _) | (_, FalseEdges { .. }) => false, |
| 274 | + } |
| 275 | +} |
| 276 | +*/ |
0 commit comments