|
2 | 2 |
|
3 | 3 | use rustc_ast::attr;
|
4 | 4 | use rustc_hir::LangItem;
|
| 5 | +use rustc_index::IndexVec; |
5 | 6 | use rustc_middle::bug;
|
6 | 7 | use rustc_middle::mir::*;
|
7 | 8 | use rustc_middle::ty::layout::ValidityRequirement;
|
@@ -60,8 +61,8 @@ impl<'tcx> MirPass<'tcx> for InstSimplify {
|
60 | 61 | _ => {}
|
61 | 62 | }
|
62 | 63 | }
|
63 |
| - |
64 | 64 | ctx.simplify_primitive_clone(block.terminator.as_mut().unwrap(), &mut block.statements);
|
| 65 | + ctx.simplify_copy_like(&mut block.statements); |
65 | 66 | ctx.simplify_intrinsic_assert(block.terminator.as_mut().unwrap());
|
66 | 67 | ctx.simplify_nounwind_call(block.terminator.as_mut().unwrap());
|
67 | 68 | simplify_duplicate_switch_targets(block.terminator.as_mut().unwrap());
|
@@ -207,6 +208,95 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
|
207 | 208 | }
|
208 | 209 | }
|
209 | 210 |
|
| 211 | + /// Transform `Aggregate(Adt, [(*_1).0, (*_1).1])` ==> `Copy(*_1)`. |
| 212 | + /// This comes from the simplification of the clone method by `simplify_primitive_clone`. |
| 213 | + fn simplify_copy_like(&self, statements: &mut Vec<Statement<'tcx>>) { |
| 214 | + let mut assignments = IndexVec::from_elem(None::<Place<'tcx>>, self.local_decls); |
| 215 | + for statement in statements { |
| 216 | + match statement.kind { |
| 217 | + StatementKind::Assign(box (dest, ref mut rvalue)) => { |
| 218 | + if let Rvalue::Aggregate(_, fields) = rvalue { |
| 219 | + let mut from_local = None; |
| 220 | + if fields.iter_enumerated().all(|(index, field)| { |
| 221 | + let Some(from_place) = field |
| 222 | + .place() |
| 223 | + .and_then(|p| p.as_local()) |
| 224 | + .and_then(|l| assignments[l]) |
| 225 | + else { |
| 226 | + return false; |
| 227 | + }; |
| 228 | + // All fields must come from the same local. |
| 229 | + if let Some(from_local) = from_local { |
| 230 | + if from_place.local != from_local { |
| 231 | + return false; |
| 232 | + } |
| 233 | + } else { |
| 234 | + // We can only copy the same type. |
| 235 | + let Some(from_ty) = |
| 236 | + self.local_decls[from_place.local].ty.builtin_deref(false) |
| 237 | + else { |
| 238 | + return false; |
| 239 | + }; |
| 240 | + let dest_ty = dest.ty(self.local_decls, self.tcx).ty; |
| 241 | + if dest_ty != from_ty { |
| 242 | + return false; |
| 243 | + }; |
| 244 | + from_local = Some(from_place.local); |
| 245 | + } |
| 246 | + // For more complex scenarios, we expect to get this simplified projection within a complete pipeline. |
| 247 | + let [ProjectionElem::Deref, ProjectionElem::Field(from_index, _)] = |
| 248 | + *from_place.projection.as_slice() |
| 249 | + else { |
| 250 | + return false; |
| 251 | + }; |
| 252 | + from_index == index |
| 253 | + }) { |
| 254 | + if let Some(local) = from_local { |
| 255 | + if self.should_simplify(&statement.source_info, rvalue) { |
| 256 | + *rvalue = Rvalue::Use(Operand::Copy(Place { |
| 257 | + local, |
| 258 | + projection: self |
| 259 | + .tcx |
| 260 | + .mk_place_elems(&[ProjectionElem::Deref]), |
| 261 | + })); |
| 262 | + } |
| 263 | + } |
| 264 | + } |
| 265 | + } |
| 266 | + // Collect available assignments, including those transformed from `Aggregate`. |
| 267 | + if let Some(local) = dest.as_local() { |
| 268 | + assignments[local] = if let Rvalue::Use(operand) = rvalue |
| 269 | + && let Some(place) = operand.place() |
| 270 | + { |
| 271 | + if let Some(rvalue_local) = place.as_local() { |
| 272 | + let place = assignments[rvalue_local]; |
| 273 | + if operand.is_move() { |
| 274 | + assignments[rvalue_local] = None; |
| 275 | + } |
| 276 | + place |
| 277 | + } else { |
| 278 | + Some(place) |
| 279 | + } |
| 280 | + } else { |
| 281 | + // This assignment generally comes from debuginfo (e.g., Ref), |
| 282 | + // but we still need to check if a local is being overridden. |
| 283 | + None |
| 284 | + }; |
| 285 | + } else { |
| 286 | + // We don't handle projection, so we drop all previous assignments. |
| 287 | + assignments.reset_all(None); |
| 288 | + } |
| 289 | + } |
| 290 | + StatementKind::StorageLive(_) |
| 291 | + | StatementKind::StorageDead(_) |
| 292 | + | StatementKind::Nop => {} |
| 293 | + _ => { |
| 294 | + assignments.reset_all(None); |
| 295 | + } |
| 296 | + } |
| 297 | + } |
| 298 | + } |
| 299 | + |
210 | 300 | fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) {
|
211 | 301 | if let Rvalue::Cast(kind, operand, cast_ty) = rvalue {
|
212 | 302 | let operand_ty = operand.ty(self.local_decls, self.tcx);
|
|
0 commit comments