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}