Skip to main content

luaur_analysis/methods/
data_flow_graph_builder_join_props.rs

1use crate::records::data_flow_graph_builder::DataFlowGraphBuilder;
2use crate::records::def::Def;
3use crate::records::dfg_scope::DfgScope;
4use crate::type_aliases::def_id_def::DefId;
5use alloc::collections::BTreeMap;
6use alloc::string::String;
7use alloc::vec::Vec;
8
9impl DataFlowGraphBuilder {
10    /// `void DataFlowGraphBuilder::joinProps(DfgScope* result, const DfgScope& a, const DfgScope& b)`.
11    /// Reference: `DataFlowGraph.cpp:246-294`.
12    ///
13    /// Borrow-safety note: the `while`-loop visitor calls `join(scope, scope, whileScope)`,
14    /// so `result` and `a` are the **same** `DfgScope`. The faithful C++ mutates
15    /// `result->props` while iterating `a->props` (and reads `scope->props` inside
16    /// `joinProps`' lambda while holding a reference into it) — in Rust that is `&`/`&mut`
17    /// aliasing UB: even without a rehash the optimizer assumes the `&mut` is unique and
18    /// miscompiles, corrupting the `props` map's `Vec` header (observed as a later
19    /// `find` indexing an empty `data`). We therefore **snapshot** `a`'s and `b`'s props
20    /// into owned values first, and have `phinodify` build each merged entry in a local
21    /// `BTreeMap` before writing it back — exactly the pattern `DfgScope::inherit` uses.
22    /// The observable result is identical to the C++ (`lookup_def_id_string` is only
23    /// consulted when the in-progress entry has no value for the key, so a deferred
24    /// write-back cannot change its answer).
25    pub fn join_props(&mut self, result: *mut DfgScope, a: &DfgScope, b: &DfgScope) {
26        let def_arena = self.def_arena;
27
28        // Owned snapshots so no borrow of `a`/`b`/`result` props is held across the
29        // mutations of `result.props` below (result may alias `a`).
30        let a_props: Vec<(DefId, BTreeMap<String, *const Def>)> =
31            a.props.iter().map(|(k, v)| (*k, v.clone())).collect();
32        let b_props: Vec<(DefId, BTreeMap<String, *const Def>)> =
33            b.props.iter().map(|(k, v)| (*k, v.clone())).collect();
34        let b_lookup = |def: DefId| -> Option<&BTreeMap<String, *const Def>> {
35            b_props.iter().find(|(k, _)| *k == def).map(|(_, v)| v)
36        };
37
38        // C++ lambda `phinodify`: merges per-key defs of `a`/`b` into `scope->props[parent]`.
39        // Builds the merged entry in a local map, then writes it back — never holds a
40        // `&mut` into `scope.props` across the `lookup_def_id_string` read of `scope.props`.
41        let phinodify = |scope: *mut DfgScope,
42                         a_props: &BTreeMap<String, *const Def>,
43                         b_props: &BTreeMap<String, *const Def>,
44                         parent: DefId| unsafe {
45            let mut p: BTreeMap<String, *const Def> =
46                (*scope).props.find(&parent).cloned().unwrap_or_default();
47
48            for (k, def_a) in a_props.iter() {
49                let merged = if let Some(it) = b_props.get(k) {
50                    (*def_arena).phi_def_id_def_id(*it, *def_a)
51                } else if let Some(it) = p.get(k).copied() {
52                    (*def_arena).phi_def_id_def_id(it, *def_a)
53                } else if let Some(def2) = (*scope).lookup_def_id_string(parent, k) {
54                    (*def_arena).phi_def_id_def_id(def2, *def_a)
55                } else {
56                    *def_a
57                };
58                p.insert(k.clone(), merged);
59            }
60
61            for (k, def_b) in b_props.iter() {
62                if a_props.get(k).is_some() {
63                    continue;
64                }
65                let merged = if let Some(it) = p.get(k).copied() {
66                    (*def_arena).phi_def_id_def_id(it, *def_b)
67                } else if let Some(def2) = (*scope).lookup_def_id_string(parent, k) {
68                    (*def_arena).phi_def_id_def_id(def2, *def_b)
69                } else {
70                    *def_b
71                };
72                p.insert(k.clone(), merged);
73            }
74
75            *(*scope).props.get_or_insert(parent) = p;
76        };
77
78        unsafe {
79            for (def, a1) in a_props.iter() {
80                (*result).props.try_insert(*def, BTreeMap::new());
81                if let Some(a2) = b_lookup(*def) {
82                    phinodify(result, a1, a2, *def);
83                } else if let Some(a2) = (*result).props.find(def) {
84                    let a2 = a2.clone();
85                    phinodify(result, a1, &a2, *def);
86                }
87            }
88
89            for (def, a1) in b_props.iter() {
90                (*result).props.try_insert(*def, BTreeMap::new());
91                if a_props.iter().any(|(k, _)| k == def) {
92                    continue;
93                } else if let Some(a2) = (*result).props.find(def) {
94                    let a2 = a2.clone();
95                    phinodify(result, a1, &a2, *def);
96                }
97            }
98        }
99    }
100}