rapx/analysis/alias_analysis/mfp/
transfer.rs

1/// Transfer functions for alias analysis
2use rustc_middle::mir::{Operand, Place, ProjectionElem};
3
4use super::intraproc::{AliasDomain, PlaceId, PlaceInfo};
5
6/// Convert a MIR Place to a PlaceId
7pub fn mir_place_to_place_id<'tcx>(place: Place<'tcx>) -> PlaceId {
8    let mut place_id = PlaceId::Local(place.local.as_usize());
9
10    // Process projections
11    for proj in place.projection {
12        match proj {
13            ProjectionElem::Field(field, _) => {
14                place_id = place_id.project_field(field.as_usize());
15            }
16            ProjectionElem::Deref => {
17                // For deref, we keep the current place (pointer itself)
18                // The actual memory is not tracked as a separate place
19            }
20            _ => {
21                // Other projections (index, downcast, etc.) are not tracked
22            }
23        }
24    }
25
26    place_id
27}
28
29/// Extract place from operand if it's Copy or Move
30pub fn operand_to_place_id<'tcx>(operand: &Operand<'tcx>) -> Option<PlaceId> {
31    match operand {
32        Operand::Copy(place) | Operand::Move(place) => Some(mir_place_to_place_id(*place)),
33        Operand::Constant(_) => None,
34        #[cfg(rapx_rustc_ge_196)]
35        Operand::RuntimeChecks(_) => None,
36    }
37}
38
39/// Transfer function for assignment: lv = rv
40pub fn transfer_assign<'tcx>(
41    state: &mut AliasDomain,
42    lv: Place<'tcx>,
43    rv: &Operand<'tcx>,
44    place_info: &PlaceInfo<'tcx>,
45) {
46    let lv_id = mir_place_to_place_id(lv);
47
48    // Get index for lv
49    let lv_idx = match place_info.get_index(&lv_id) {
50        Some(idx) => idx,
51        None => return, // Place not tracked
52    };
53
54    // Check if lv may drop
55    if !place_info.may_drop(lv_idx) {
56        return;
57    }
58
59    // Kill: remove old aliases for lv and all its fields (e.g., lv.0, lv.1.2, etc.)
60    state.remove_aliases_with_prefix(&lv_id, place_info);
61
62    // Gen: add alias lv ≈ rv if rv is a place
63    if let Some(rv_id) = operand_to_place_id(rv) {
64        if let Some(rv_idx) = place_info.get_index(&rv_id) {
65            if place_info.may_drop(rv_idx) {
66                state.union(lv_idx, rv_idx);
67
68                // Sync fields if both have fields
69                sync_fields(state, &lv_id, &rv_id, place_info);
70            }
71        }
72    }
73}
74
75/// Transfer function for reference: lv = &rv
76pub fn transfer_ref<'tcx>(
77    state: &mut AliasDomain,
78    lv: Place<'tcx>,
79    rv: Place<'tcx>,
80    place_info: &PlaceInfo<'tcx>,
81) {
82    // Reference creation is similar to assignment
83    let lv_id = mir_place_to_place_id(lv);
84    let rv_id = mir_place_to_place_id(rv);
85
86    let lv_idx = match place_info.get_index(&lv_id) {
87        Some(idx) => idx,
88        None => return,
89    };
90
91    let rv_idx = match place_info.get_index(&rv_id) {
92        Some(idx) => idx,
93        None => return,
94    };
95
96    if !place_info.may_drop(lv_idx) || !place_info.may_drop(rv_idx) {
97        return;
98    }
99
100    // Kill: remove old aliases for lv and all its fields
101    state.remove_aliases_with_prefix(&lv_id, place_info);
102
103    // Gen: add alias lv ≈ rv
104    state.union(lv_idx, rv_idx);
105
106    // Sync fields
107    sync_fields(state, &lv_id, &rv_id, place_info);
108}
109
110/// Transfer function for aggregate: lv = (operands...)
111pub fn transfer_aggregate<'tcx>(
112    state: &mut AliasDomain,
113    lv: Place<'tcx>,
114    operands: &[Operand<'tcx>],
115    place_info: &PlaceInfo<'tcx>,
116) {
117    let lv_id = mir_place_to_place_id(lv);
118
119    // Kill: remove old aliases for lv and all its fields
120    state.remove_aliases_with_prefix(&lv_id, place_info);
121
122    // Gen: for each field, add alias lv.i ≈ operand[i]
123    for (field_idx, operand) in operands.iter().enumerate() {
124        if let Some(rv_id) = operand_to_place_id(operand) {
125            let lv_field_id = lv_id.project_field(field_idx);
126
127            if let Some(lv_field_idx) = place_info.get_index(&lv_field_id) {
128                if let Some(rv_idx) = place_info.get_index(&rv_id) {
129                    if place_info.may_drop(lv_field_idx) && place_info.may_drop(rv_idx) {
130                        state.union(lv_field_idx, rv_idx);
131
132                        // Sync nested fields
133                        sync_fields(state, &lv_field_id, &rv_id, place_info);
134                    }
135                }
136            }
137        }
138    }
139}
140
141/// Transfer function for function call
142pub fn transfer_call<'tcx>(
143    state: &mut AliasDomain,
144    ret: Place<'tcx>,
145    _args: &[Operand<'tcx>],
146    place_info: &PlaceInfo<'tcx>,
147) {
148    let ret_id = mir_place_to_place_id(ret);
149
150    // Kill: remove old aliases for return value and all its fields
151    state.remove_aliases_with_prefix(&ret_id, place_info);
152
153    // Note: Function summary application will be handled separately
154    // in the Analysis implementation. This is just the basic kill effect.
155    // The gen effect (applying function summaries) happens in apply_call_return_effect
156}
157
158/// Synchronize field aliases
159/// If lv and rv are aliased, ensure all their corresponding fields are also aliased
160pub fn sync_fields<'tcx>(
161    state: &mut AliasDomain,
162    lv: &PlaceId,
163    rv: &PlaceId,
164    place_info: &PlaceInfo<'tcx>,
165) {
166    // Recursively sync fields up to a reasonable depth
167    const MAX_SYNC_DEPTH: usize = 3;
168    sync_fields_recursive(state, lv, rv, place_info, 0, MAX_SYNC_DEPTH);
169}
170
171/// Recursive helper for field synchronization
172fn sync_fields_recursive<'tcx>(
173    state: &mut AliasDomain,
174    lv: &PlaceId,
175    rv: &PlaceId,
176    place_info: &PlaceInfo<'tcx>,
177    depth: usize,
178    max_depth: usize,
179) {
180    if depth >= max_depth {
181        return;
182    }
183
184    // Try to sync common fields (0..N)
185    // We'll try up to 16 fields as a reasonable upper bound
186    for field_idx in 0..16 {
187        let lv_field = lv.project_field(field_idx);
188        let rv_field = rv.project_field(field_idx);
189
190        // Check if both field places exist
191        if let (Some(lv_field_idx), Some(rv_field_idx)) = (
192            place_info.get_index(&lv_field),
193            place_info.get_index(&rv_field),
194        ) {
195            // Both fields exist and may drop, union them
196            if place_info.may_drop(lv_field_idx) && place_info.may_drop(rv_field_idx) {
197                if state.union(lv_field_idx, rv_field_idx) {
198                    // If union succeeded (they weren't already aliased), recurse
199                    sync_fields_recursive(
200                        state,
201                        &lv_field,
202                        &rv_field,
203                        place_info,
204                        depth + 1,
205                        max_depth,
206                    );
207                }
208            }
209        } else {
210            // If either field doesn't exist, no more fields to sync
211            break;
212        }
213    }
214}