use rustc_middle::mir::{Operand, Place, ProjectionElem};
use super::intraproc::{AliasDomain, PlaceId, PlaceInfo};
pub fn mir_place_to_place_id<'tcx>(place: Place<'tcx>) -> PlaceId {
let mut place_id = PlaceId::Local(place.local.as_usize());
for proj in place.projection {
match proj {
ProjectionElem::Field(field, _) => {
place_id = place_id.project_field(field.as_usize());
}
ProjectionElem::Deref => {
}
_ => {
}
}
}
place_id
}
pub fn operand_to_place_id<'tcx>(operand: &Operand<'tcx>) -> Option<PlaceId> {
match operand {
Operand::Copy(place) | Operand::Move(place) => Some(mir_place_to_place_id(*place)),
Operand::Constant(_) => None,
}
}
pub fn transfer_assign<'tcx>(
state: &mut AliasDomain,
lv: Place<'tcx>,
rv: &Operand<'tcx>,
place_info: &PlaceInfo<'tcx>,
) {
let lv_id = mir_place_to_place_id(lv);
let lv_idx = match place_info.get_index(&lv_id) {
Some(idx) => idx,
None => return, };
if !place_info.may_drop(lv_idx) {
return;
}
state.remove_aliases_with_prefix(&lv_id, place_info);
if let Some(rv_id) = operand_to_place_id(rv) {
if let Some(rv_idx) = place_info.get_index(&rv_id) {
if place_info.may_drop(rv_idx) {
state.union(lv_idx, rv_idx);
sync_fields(state, &lv_id, &rv_id, place_info);
}
}
}
}
pub fn transfer_ref<'tcx>(
state: &mut AliasDomain,
lv: Place<'tcx>,
rv: Place<'tcx>,
place_info: &PlaceInfo<'tcx>,
) {
let lv_id = mir_place_to_place_id(lv);
let rv_id = mir_place_to_place_id(rv);
let lv_idx = match place_info.get_index(&lv_id) {
Some(idx) => idx,
None => return,
};
let rv_idx = match place_info.get_index(&rv_id) {
Some(idx) => idx,
None => return,
};
if !place_info.may_drop(lv_idx) || !place_info.may_drop(rv_idx) {
return;
}
state.remove_aliases_with_prefix(&lv_id, place_info);
state.union(lv_idx, rv_idx);
sync_fields(state, &lv_id, &rv_id, place_info);
}
pub fn transfer_aggregate<'tcx>(
state: &mut AliasDomain,
lv: Place<'tcx>,
operands: &[Operand<'tcx>],
place_info: &PlaceInfo<'tcx>,
) {
let lv_id = mir_place_to_place_id(lv);
state.remove_aliases_with_prefix(&lv_id, place_info);
for (field_idx, operand) in operands.iter().enumerate() {
if let Some(rv_id) = operand_to_place_id(operand) {
let lv_field_id = lv_id.project_field(field_idx);
if let Some(lv_field_idx) = place_info.get_index(&lv_field_id) {
if let Some(rv_idx) = place_info.get_index(&rv_id) {
if place_info.may_drop(lv_field_idx) && place_info.may_drop(rv_idx) {
state.union(lv_field_idx, rv_idx);
sync_fields(state, &lv_field_id, &rv_id, place_info);
}
}
}
}
}
}
pub fn transfer_call<'tcx>(
state: &mut AliasDomain,
ret: Place<'tcx>,
_args: &[Operand<'tcx>],
place_info: &PlaceInfo<'tcx>,
) {
let ret_id = mir_place_to_place_id(ret);
state.remove_aliases_with_prefix(&ret_id, place_info);
}
pub fn sync_fields<'tcx>(
state: &mut AliasDomain,
lv: &PlaceId,
rv: &PlaceId,
place_info: &PlaceInfo<'tcx>,
) {
const MAX_SYNC_DEPTH: usize = 3;
sync_fields_recursive(state, lv, rv, place_info, 0, MAX_SYNC_DEPTH);
}
fn sync_fields_recursive<'tcx>(
state: &mut AliasDomain,
lv: &PlaceId,
rv: &PlaceId,
place_info: &PlaceInfo<'tcx>,
depth: usize,
max_depth: usize,
) {
if depth >= max_depth {
return;
}
for field_idx in 0..16 {
let lv_field = lv.project_field(field_idx);
let rv_field = rv.project_field(field_idx);
if let (Some(lv_field_idx), Some(rv_field_idx)) = (
place_info.get_index(&lv_field),
place_info.get_index(&rv_field),
) {
if place_info.may_drop(lv_field_idx) && place_info.may_drop(rv_field_idx) {
if state.union(lv_field_idx, rv_field_idx) {
sync_fields_recursive(
state,
&lv_field,
&rv_field,
place_info,
depth + 1,
max_depth,
);
}
}
} else {
break;
}
}
}