Auto merge of #107270 - cjgillot:remove-zst, r=oli-obk
Replace ZST operands and debuginfo by constants. This is work that ConstProp will not have to do. Split from https://github.com/rust-lang/rust/pull/107267
This commit is contained in:
commit
e386217dd9
34 changed files with 327 additions and 211 deletions
|
@ -1,7 +1,9 @@
|
|||
//! Removes assignments to ZST places.
|
||||
//! Removes operations on ZST places, and convert ZST operands to constants.
|
||||
|
||||
use crate::MirPass;
|
||||
use rustc_middle::mir::{Body, StatementKind};
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::mir::visit::*;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
||||
pub struct RemoveZsts;
|
||||
|
@ -16,38 +18,24 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts {
|
|||
if tcx.type_of(body.source.def_id()).subst_identity().is_generator() {
|
||||
return;
|
||||
}
|
||||
let param_env = tcx.param_env(body.source.def_id());
|
||||
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
|
||||
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
||||
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 tcx.consider_optimizing(|| {
|
||||
format!(
|
||||
"RemoveZsts - Place: {:?} SourceInfo: {:?}",
|
||||
place, statement.source_info
|
||||
)
|
||||
}) {
|
||||
statement.make_nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut replacer = Replacer { tcx, param_env, local_decls };
|
||||
for var_debug_info in &mut body.var_debug_info {
|
||||
replacer.visit_var_debug_info(var_debug_info);
|
||||
}
|
||||
for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
|
||||
replacer.visit_basic_block_data(bb, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Replacer<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
local_decls: &'a LocalDecls<'tcx>,
|
||||
}
|
||||
|
||||
/// A cheap, approximate check to avoid unnecessary `layout_of` calls.
|
||||
fn maybe_zst(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
|
@ -63,3 +51,93 @@ fn maybe_zst(ty: Ty<'_>) -> bool {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Replacer<'_, 'tcx> {
|
||||
fn known_to_be_zst(&self, ty: Ty<'tcx>) -> bool {
|
||||
if !maybe_zst(ty) {
|
||||
return false;
|
||||
}
|
||||
let Ok(layout) = self.tcx.layout_of(self.param_env.and(ty)) else {
|
||||
return false;
|
||||
};
|
||||
layout.is_zst()
|
||||
}
|
||||
|
||||
fn make_zst(&self, ty: Ty<'tcx>) -> Constant<'tcx> {
|
||||
debug_assert!(self.known_to_be_zst(ty));
|
||||
Constant {
|
||||
span: rustc_span::DUMMY_SP,
|
||||
user_ty: None,
|
||||
literal: ConstantKind::Val(ConstValue::ZeroSized, ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
|
||||
match var_debug_info.value {
|
||||
VarDebugInfoContents::Const(_) => {}
|
||||
VarDebugInfoContents::Place(place) => {
|
||||
let place_ty = place.ty(self.local_decls, self.tcx).ty;
|
||||
if self.known_to_be_zst(place_ty) {
|
||||
var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(place_ty))
|
||||
}
|
||||
}
|
||||
VarDebugInfoContents::Composite { ty, fragments: _ } => {
|
||||
if self.known_to_be_zst(ty) {
|
||||
var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(ty))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) {
|
||||
if let Operand::Constant(_) = operand {
|
||||
return;
|
||||
}
|
||||
let op_ty = operand.ty(self.local_decls, self.tcx);
|
||||
if self.known_to_be_zst(op_ty)
|
||||
&& self.tcx.consider_optimizing(|| {
|
||||
format!("RemoveZsts - Operand: {:?} Location: {:?}", operand, loc)
|
||||
})
|
||||
{
|
||||
*operand = Operand::Constant(Box::new(self.make_zst(op_ty)))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, loc: Location) {
|
||||
let place_for_ty = match statement.kind {
|
||||
StatementKind::Assign(box (place, ref rvalue)) => {
|
||||
rvalue.is_safe_to_remove().then_some(place)
|
||||
}
|
||||
StatementKind::Deinit(box place)
|
||||
| StatementKind::SetDiscriminant { box place, variant_index: _ }
|
||||
| StatementKind::AscribeUserType(box (place, _), _)
|
||||
| StatementKind::Retag(_, box place)
|
||||
| StatementKind::PlaceMention(box place)
|
||||
| StatementKind::FakeRead(box (_, place)) => Some(place),
|
||||
StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
|
||||
Some(local.into())
|
||||
}
|
||||
StatementKind::Coverage(_)
|
||||
| StatementKind::Intrinsic(_)
|
||||
| StatementKind::Nop
|
||||
| StatementKind::ConstEvalCounter => None,
|
||||
};
|
||||
if let Some(place_for_ty) = place_for_ty
|
||||
&& let ty = place_for_ty.ty(self.local_decls, self.tcx).ty
|
||||
&& self.known_to_be_zst(ty)
|
||||
&& self.tcx.consider_optimizing(|| {
|
||||
format!("RemoveZsts - Place: {:?} SourceInfo: {:?}", place_for_ty, statement.source_info)
|
||||
})
|
||||
{
|
||||
statement.make_nop();
|
||||
} else {
|
||||
self.super_statement(statement, loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue