1
Fork 0

remove box derefs from codgen

This commit is contained in:
DrMeepster 2022-05-13 21:53:03 -07:00
parent 3e9d3d917a
commit cb417881a9
16 changed files with 385 additions and 146 deletions

View file

@ -0,0 +1,130 @@
//! This pass transforms derefs of Box into a deref of the pointer inside Box
//! Codegen does not allow box to be directly dereferenced
use crate::MirPass;
use rustc_hir::def_id::DefId;
use rustc_index::vec::Idx;
use rustc_middle::mir::patch::MirPatch;
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::*;
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::TyCtxt;
struct ElaborateBoxDerefVistor<'tcx, 'a> {
tcx: TyCtxt<'tcx>,
unique_did: DefId,
nonnull_did: DefId,
local_decls: &'a mut LocalDecls<'tcx>,
patch: MirPatch<'tcx>,
}
impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVistor<'tcx, 'a> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn visit_place(
&mut self,
place: &mut Place<'tcx>,
context: visit::PlaceContext,
location: Location,
) {
let tcx = self.tcx;
let base_ty = self.local_decls[place.local].ty;
// Derefer ensures that derefs are always the first projection
if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() {
let source_info = self.local_decls[place.local].source_info;
let substs = tcx.intern_substs(&[base_ty.boxed_ty().into()]);
let unique_ty = tcx.bound_type_of(self.unique_did).subst(tcx, substs);
let nonnull_ty = tcx.bound_type_of(self.nonnull_did).subst(tcx, substs);
let ptr_ty = tcx.mk_imm_ptr(base_ty.boxed_ty());
let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
self.local_decls.push(LocalDecl::new(ptr_ty, source_info.span));
self.patch.add_statement(location, StatementKind::StorageLive(ptr_local));
self.patch.add_assign(
location,
Place::from(ptr_local),
Rvalue::Use(Operand::Copy(Place::from(place.local).project_deeper(
&[
PlaceElem::Field(Field::new(0), unique_ty),
PlaceElem::Field(Field::new(0), nonnull_ty),
PlaceElem::Field(Field::new(0), ptr_ty),
],
tcx,
))),
);
place.local = ptr_local;
self.patch.add_statement(
Location { block: location.block, statement_index: location.statement_index + 1 },
StatementKind::StorageDead(ptr_local),
);
}
self.super_place(place, context, location);
}
}
pub struct ElaborateBoxDerefs;
impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
if let Some(def_id) = tcx.lang_items().owned_box() {
let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[0].did;
let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
};
let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
let patch = MirPatch::new(body);
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
let mut visitor =
ElaborateBoxDerefVistor { tcx, unique_did, nonnull_did, local_decls, patch };
for (block, BasicBlockData { statements, terminator, .. }) in
basic_blocks.iter_enumerated_mut()
{
let mut index = 0;
for statement in statements {
let location = Location { block, statement_index: index };
visitor.visit_statement(statement, location);
index += 1;
}
if let Some(terminator) = terminator
&& !matches!(terminator.kind, TerminatorKind::Yield{..})
{
let location = Location { block, statement_index: index };
visitor.visit_terminator(terminator, location);
}
let location = Location { block, statement_index: index };
match terminator {
// yielding into a box is handed when lowering generators
Some(Terminator { kind: TerminatorKind::Yield { value, .. }, .. }) => {
visitor.visit_operand(value, location);
}
Some(terminator) => {
visitor.visit_terminator(terminator, location);
}
None => {}
}
}
visitor.patch.apply(body);
} else {
// box is not present, this pass doesn't need to do anything
}
}
}

View file

@ -56,7 +56,7 @@ use crate::MirPass;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_index::bit_set::{BitMatrix, BitSet};
use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet};
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::dump_mir;
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
@ -206,7 +206,7 @@ struct SuspensionPoint<'tcx> {
/// Which block to jump to if the generator is dropped in this state.
drop: Option<BasicBlock>,
/// Set of locals that have live storage while at this suspension point.
storage_liveness: BitSet<Local>,
storage_liveness: GrowableBitSet<Local>,
}
struct TransformVisitor<'tcx> {
@ -362,7 +362,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
resume,
resume_arg,
drop,
storage_liveness: self.storage_liveness[block].clone().unwrap(),
storage_liveness: self.storage_liveness[block].clone().unwrap().into(),
});
VariantIdx::new(state)
@ -1177,6 +1177,8 @@ fn create_cases<'tcx>(
transform: &TransformVisitor<'tcx>,
operation: Operation,
) -> Vec<(usize, BasicBlock)> {
let tcx = transform.tcx;
let source_info = SourceInfo::outermost(body.span);
transform
@ -1209,13 +1211,84 @@ fn create_cases<'tcx>(
if operation == Operation::Resume {
// Move the resume argument to the destination place of the `Yield` terminator
let resume_arg = Local::new(2); // 0 = return, 1 = self
statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
point.resume_arg,
Rvalue::Use(Operand::Move(resume_arg.into())),
))),
});
// handle `box yield` properly
let box_place = if let [projection @ .., ProjectionElem::Deref] =
&**point.resume_arg.projection
{
let box_place =
Place::from(point.resume_arg.local).project_deeper(projection, tcx);
let box_ty = box_place.ty(&body.local_decls, tcx).ty;
if box_ty.is_box() { Some((box_place, box_ty)) } else { None }
} else {
None
};
if let Some((box_place, box_ty)) = box_place {
let unique_did = box_ty
.ty_adt_def()
.expect("expected Box to be an Adt")
.non_enum_variant()
.fields[0]
.did;
let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
};
let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
let substs = tcx.intern_substs(&[box_ty.boxed_ty().into()]);
let unique_ty = tcx.bound_type_of(unique_did).subst(tcx, substs);
let nonnull_ty = tcx.bound_type_of(nonnull_did).subst(tcx, substs);
let ptr_ty = tcx.mk_imm_ptr(box_ty.boxed_ty());
let ptr_local = body.local_decls.push(LocalDecl::new(ptr_ty, body.span));
statements.push(Statement {
source_info,
kind: StatementKind::StorageLive(ptr_local),
});
statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
Place::from(ptr_local),
Rvalue::Use(Operand::Copy(box_place.project_deeper(
&[
PlaceElem::Field(Field::new(0), unique_ty),
PlaceElem::Field(Field::new(0), nonnull_ty),
PlaceElem::Field(Field::new(0), ptr_ty),
],
tcx,
))),
))),
});
statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
Place::from(ptr_local)
.project_deeper(&[ProjectionElem::Deref], tcx),
Rvalue::Use(Operand::Move(resume_arg.into())),
))),
});
statements.push(Statement {
source_info,
kind: StatementKind::StorageDead(ptr_local),
});
} else {
statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
point.resume_arg,
Rvalue::Use(Operand::Move(resume_arg.into())),
))),
});
}
}
// Then jump to the real target

View file

@ -57,6 +57,7 @@ mod deref_separator;
mod dest_prop;
pub mod dump_mir;
mod early_otherwise_branch;
mod elaborate_box_derefs;
mod elaborate_drops;
mod function_item_references;
mod generator;
@ -427,6 +428,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
// `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
// but before optimizations begin.
&deref_separator::Derefer,
&elaborate_box_derefs::ElaborateBoxDerefs,
&add_retag::AddRetag,
&lower_intrinsics::LowerIntrinsics,
&simplify::SimplifyCfg::new("elaborate-drops"),