rapx/analysis/core/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    }
35}
36
37/// Transfer function for assignment: lv = rv
38pub fn transfer_assign<'tcx>(
39    state: &mut AliasDomain,
40    lv: Place<'tcx>,
41    rv: &Operand<'tcx>,
42    place_info: &PlaceInfo<'tcx>,
43) {
44    let lv_id = mir_place_to_place_id(lv);
45
46    // Get index for lv
47    let lv_idx = match place_info.get_index(&lv_id) {
48        Some(idx) => idx,
49        None => return, // Place not tracked
50    };
51
52    // Check if lv may drop
53    if !place_info.may_drop(lv_idx) {
54        return;
55    }
56
57    // Kill: remove old aliases for lv and all its fields (e.g., lv.0, lv.1.2, etc.)
58    state.remove_aliases_with_prefix(&lv_id, place_info);
59
60    // Gen: add alias lv ≈ rv if rv is a place
61    if let Some(rv_id) = operand_to_place_id(rv) {
62        if let Some(rv_idx) = place_info.get_index(&rv_id) {
63            if place_info.may_drop(rv_idx) {
64                state.union(lv_idx, rv_idx);
65
66                // Sync fields if both have fields
67                sync_fields(state, &lv_id, &rv_id, place_info);
68            }
69        }
70    }
71}
72
73/// Transfer function for reference: lv = &rv
74pub fn transfer_ref<'tcx>(
75    state: &mut AliasDomain,
76    lv: Place<'tcx>,
77    rv: Place<'tcx>,
78    place_info: &PlaceInfo<'tcx>,
79) {
80    // Reference creation is similar to assignment
81    let lv_id = mir_place_to_place_id(lv);
82    let rv_id = mir_place_to_place_id(rv);
83
84    let lv_idx = match place_info.get_index(&lv_id) {
85        Some(idx) => idx,
86        None => return,
87    };
88
89    let rv_idx = match place_info.get_index(&rv_id) {
90        Some(idx) => idx,
91        None => return,
92    };
93
94    if !place_info.may_drop(lv_idx) || !place_info.may_drop(rv_idx) {
95        return;
96    }
97
98    // Kill: remove old aliases for lv and all its fields
99    state.remove_aliases_with_prefix(&lv_id, place_info);
100
101    // Gen: add alias lv ≈ rv
102    state.union(lv_idx, rv_idx);
103
104    // Sync fields
105    sync_fields(state, &lv_id, &rv_id, place_info);
106}
107
108/// Transfer function for field assignment: lv = rv.field
109pub fn transfer_field_assign<'tcx>(
110    state: &mut AliasDomain,
111    lv: Place<'tcx>,
112    rv_base: Place<'tcx>,
113    field_idx: usize,
114    place_info: &PlaceInfo<'tcx>,
115) {
116    let lv_id = mir_place_to_place_id(lv);
117    let rv_base_id = mir_place_to_place_id(rv_base);
118    let rv_field_id = rv_base_id.project_field(field_idx);
119
120    let lv_idx = match place_info.get_index(&lv_id) {
121        Some(idx) => idx,
122        None => return,
123    };
124
125    let rv_field_idx = match place_info.get_index(&rv_field_id) {
126        Some(idx) => idx,
127        None => return,
128    };
129
130    if !place_info.may_drop(lv_idx) || !place_info.may_drop(rv_field_idx) {
131        return;
132    }
133
134    // Kill: remove old aliases for lv and all its fields
135    state.remove_aliases_with_prefix(&lv_id, place_info);
136
137    // Gen: add alias lv ≈ rv.field
138    state.union(lv_idx, rv_field_idx);
139
140    // Sync fields
141    sync_fields(state, &lv_id, &rv_field_id, place_info);
142}
143
144/// Transfer function for aggregate: lv = (operands...)
145pub fn transfer_aggregate<'tcx>(
146    state: &mut AliasDomain,
147    lv: Place<'tcx>,
148    operands: &[Operand<'tcx>],
149    place_info: &PlaceInfo<'tcx>,
150) {
151    let lv_id = mir_place_to_place_id(lv);
152
153    // Kill: remove old aliases for lv and all its fields
154    state.remove_aliases_with_prefix(&lv_id, place_info);
155
156    // Gen: for each field, add alias lv.i ≈ operand[i]
157    for (field_idx, operand) in operands.iter().enumerate() {
158        if let Some(rv_id) = operand_to_place_id(operand) {
159            let lv_field_id = lv_id.project_field(field_idx);
160
161            if let Some(lv_field_idx) = place_info.get_index(&lv_field_id) {
162                if let Some(rv_idx) = place_info.get_index(&rv_id) {
163                    if place_info.may_drop(lv_field_idx) && place_info.may_drop(rv_idx) {
164                        state.union(lv_field_idx, rv_idx);
165
166                        // Sync nested fields
167                        sync_fields(state, &lv_field_id, &rv_id, place_info);
168                    }
169                }
170            }
171        }
172    }
173}
174
175/// Transfer function for function call
176pub fn transfer_call<'tcx>(
177    state: &mut AliasDomain,
178    ret: Place<'tcx>,
179    _args: &[Operand<'tcx>],
180    place_info: &PlaceInfo<'tcx>,
181) {
182    let ret_id = mir_place_to_place_id(ret);
183
184    // Kill: remove old aliases for return value and all its fields
185    state.remove_aliases_with_prefix(&ret_id, place_info);
186
187    // Note: Function summary application will be handled separately
188    // in the Analysis implementation. This is just the basic kill effect.
189    // The gen effect (applying function summaries) happens in apply_call_return_effect
190}
191
192/// Synchronize field aliases
193/// If lv and rv are aliased, ensure all their corresponding fields are also aliased
194pub fn sync_fields<'tcx>(
195    state: &mut AliasDomain,
196    lv: &PlaceId,
197    rv: &PlaceId,
198    place_info: &PlaceInfo<'tcx>,
199) {
200    // Recursively sync fields up to a reasonable depth
201    const MAX_SYNC_DEPTH: usize = 3;
202    sync_fields_recursive(state, lv, rv, place_info, 0, MAX_SYNC_DEPTH);
203}
204
205/// Recursive helper for field synchronization
206fn sync_fields_recursive<'tcx>(
207    state: &mut AliasDomain,
208    lv: &PlaceId,
209    rv: &PlaceId,
210    place_info: &PlaceInfo<'tcx>,
211    depth: usize,
212    max_depth: usize,
213) {
214    if depth >= max_depth {
215        return;
216    }
217
218    // Try to sync common fields (0..N)
219    // We'll try up to 16 fields as a reasonable upper bound
220    for field_idx in 0..16 {
221        let lv_field = lv.project_field(field_idx);
222        let rv_field = rv.project_field(field_idx);
223
224        // Check if both field places exist
225        if let (Some(lv_field_idx), Some(rv_field_idx)) = (
226            place_info.get_index(&lv_field),
227            place_info.get_index(&rv_field),
228        ) {
229            // Both fields exist and may drop, union them
230            if place_info.may_drop(lv_field_idx) && place_info.may_drop(rv_field_idx) {
231                if state.union(lv_field_idx, rv_field_idx) {
232                    // If union succeeded (they weren't already aliased), recurse
233                    sync_fields_recursive(
234                        state,
235                        &lv_field,
236                        &rv_field,
237                        place_info,
238                        depth + 1,
239                        max_depth,
240                    );
241                }
242            }
243        } else {
244            // If either field doesn't exist, no more fields to sync
245            break;
246        }
247    }
248}