diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_mir_transform/src/remove_zsts.rs | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs new file mode 100644 index 000000000..40be4f146 --- /dev/null +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -0,0 +1,86 @@ +//! Removes assignments to ZST places. + +use crate::MirPass; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::{Body, LocalDecls, Place, StatementKind}; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +pub struct RemoveZsts; + +impl<'tcx> MirPass<'tcx> for RemoveZsts { + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + sess.mir_opt_level() > 0 + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + // Avoid query cycles (generators require optimized MIR for layout). + if tcx.type_of(body.source.def_id()).is_generator() { + return; + } + let param_env = tcx.param_env(body.source.def_id()); + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); + let local_decls = &body.local_decls; + for block in basic_blocks { + for statement in block.statements.iter_mut() { + if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) = + statement.kind + { + let place_ty = place.ty(local_decls, tcx).ty; + if !maybe_zst(place_ty) { + continue; + } + let Ok(layout) = tcx.layout_of(param_env.and(place_ty)) else { + continue; + }; + if !layout.is_zst() { + continue; + } + if involves_a_union(place, local_decls, tcx) { + continue; + } + if tcx.consider_optimizing(|| { + format!( + "RemoveZsts - Place: {:?} SourceInfo: {:?}", + place, statement.source_info + ) + }) { + statement.make_nop(); + } + } + } + } + } +} + +/// A cheap, approximate check to avoid unnecessary `layout_of` calls. +fn maybe_zst(ty: Ty<'_>) -> bool { + match ty.kind() { + // maybe ZST (could be more precise) + ty::Adt(..) | ty::Array(..) | ty::Closure(..) | ty::Tuple(..) | ty::Opaque(..) => true, + // definitely ZST + ty::FnDef(..) | ty::Never => true, + // unreachable or can't be ZST + _ => false, + } +} + +/// Miri lazily allocates memory for locals on assignment, +/// so we must preserve writes to unions and union fields, +/// or it will ICE on reads of those fields. +fn involves_a_union<'tcx>( + place: Place<'tcx>, + local_decls: &LocalDecls<'tcx>, + tcx: TyCtxt<'tcx>, +) -> bool { + let mut place_ty = PlaceTy::from_ty(local_decls[place.local].ty); + if place_ty.ty.is_union() { + return true; + } + for elem in place.projection { + place_ty = place_ty.projection_ty(tcx, elem); + if place_ty.ty.is_union() { + return true; + } + } + return false; +} |