1
Fork 0

validate derefer, run derefer inside generator

This commit is contained in:
ouz-a 2022-05-12 00:27:06 +03:00
parent abc7681a76
commit e71913e847
39 changed files with 300 additions and 60 deletions

View file

@ -1,9 +1,11 @@
use crate::MirPass;
use rustc_index::vec::IndexVec;
use rustc_middle::mir::patch::MirPatch;
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
pub struct Derefer;
pub struct DerefChecker<'tcx> {
@ -17,63 +19,68 @@ impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
self.tcx
}
fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
let mut place_local = place.local;
let mut last_len = 0;
let mut last_deref_idx = 0;
fn visit_place(&mut self, place: &mut Place<'tcx>, cntxt: PlaceContext, loc: Location) {
if !place.projection.is_empty()
&& cntxt != PlaceContext::NonUse(VarDebugInfo)
&& place.projection[1..].contains(&ProjectionElem::Deref)
{
let mut place_local = place.local;
let mut last_len = 0;
let mut last_deref_idx = 0;
let mut prev_temp: Option<Local> = None;
let mut prev_temp: Option<Local> = None;
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
last_deref_idx = idx;
}
}
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
let temp = self.patcher.new_local_with_info(
ty,
self.local_decls[p_ref.local].source_info.span,
Some(Box::new(LocalInfo::DerefTemp)),
);
self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
// We are adding current p_ref's projections to our
// temp value, excluding projections we already covered.
let deref_place = Place::from(place_local)
.project_deeper(&p_ref.projection[last_len..], self.tcx);
self.patcher.add_assign(
loc,
Place::from(temp),
Rvalue::Use(Operand::Move(deref_place)),
);
place_local = temp;
last_len = p_ref.projection.len();
// Change `Place` only if we are actually at the Place's last deref
if idx == last_deref_idx {
let temp_place =
Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
*place = temp_place;
for (idx, elem) in place.projection[0..].iter().enumerate() {
if *elem == ProjectionElem::Deref {
last_deref_idx = idx;
}
// We are destroying the previous temp since it's no longer used.
if let Some(prev_temp) = prev_temp {
self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
}
prev_temp = Some(temp);
}
}
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
if !p_ref.projection.is_empty() && p_elem == ProjectionElem::Deref {
let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
let temp = self.patcher.new_local_with_info(
ty,
self.local_decls[p_ref.local].source_info.span,
Some(Box::new(LocalInfo::DerefTemp)),
);
// Since we won't be able to reach final temp, we destroy it outside the loop.
if let Some(prev_temp) = prev_temp {
let last_loc = Location { block: loc.block, statement_index: loc.statement_index + 1 };
self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
// We are adding current p_ref's projections to our
// temp value, excluding projections we already covered.
let deref_place = Place::from(place_local)
.project_deeper(&p_ref.projection[last_len..], self.tcx);
self.patcher.add_assign(
loc,
Place::from(temp),
Rvalue::Use(Operand::Move(deref_place)),
);
place_local = temp;
last_len = p_ref.projection.len();
// Change `Place` only if we are actually at the Place's last deref
if idx == last_deref_idx {
let temp_place =
Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
*place = temp_place;
}
// We are destroying the previous temp since it's no longer used.
if let Some(prev_temp) = prev_temp {
self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
}
prev_temp = Some(temp);
}
}
// Since we won't be able to reach final temp, we destroy it outside the loop.
if let Some(prev_temp) = prev_temp {
let last_loc =
Location { block: loc.block, statement_index: loc.statement_index + 1 };
self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
}
}
}
}
@ -92,5 +99,6 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
impl<'tcx> MirPass<'tcx> for Derefer {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
deref_finder(tcx, body);
body.phase = MirPhase::Derefered;
}
}

View file

@ -49,6 +49,7 @@
//! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
//! Otherwise it drops all the values in scope at the last suspension point.
use crate::deref_separator::deref_finder;
use crate::simplify;
use crate::util::expand_aggregate;
use crate::MirPass;
@ -1368,6 +1369,9 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
// Create the Generator::resume function
create_generator_resume_function(tcx, transform, body, can_return);
// Run derefer to fix Derefs that are not in the first place
deref_finder(tcx, body);
}
}

View file

@ -1,5 +1,5 @@
//! Inlining pass for MIR functions
use crate::deref_separator::deref_finder;
use rustc_attr::InlineAttr;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::Idx;
@ -53,6 +53,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
debug!("running simplify cfg on {:?}", body.source);
CfgSimplifier::new(body).simplify();
remove_dead_blocks(tcx, body);
deref_finder(tcx, body);
}
}
}