rapx/analysis/ssa_transform/
ssa_transformer.rs1#![allow(unused_imports)]
2#![allow(unused_variables)]
3#![allow(dead_code)]
4
5use rustc_data_structures::graph::dominators::Dominators;
6use rustc_data_structures::graph::{Predecessors, dominators};
7use rustc_driver::args;
8use rustc_hir::def_id::DefId;
9use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefIndex, LOCAL_CRATE, LocalDefId};
10use rustc_middle::mir::*;
11use rustc_middle::{
12 mir::{Body, Local, Location, visit::Visitor},
13 ty::TyCtxt,
14};
15use rustc_span::symbol::Symbol;
16use std::collections::{HashMap, HashSet};
17
18pub struct SSATransformer<'tcx> {
19 pub tcx: TyCtxt<'tcx>,
20 pub body: Body<'tcx>,
21 pub cfg: HashMap<BasicBlock, Vec<BasicBlock>>,
22 pub dominators: Dominators<BasicBlock>,
23 pub dom_tree: HashMap<BasicBlock, Vec<BasicBlock>>,
24 pub df: HashMap<BasicBlock, HashSet<BasicBlock>>,
25 pub local_assign_blocks: HashMap<Local, HashSet<BasicBlock>>,
26 pub reaching_def: HashMap<Local, Option<Local>>,
27 pub local_index: usize,
28 pub local_defination_block: HashMap<Local, BasicBlock>,
29 pub skipped: HashSet<usize>,
30 pub phi_index: HashMap<Location, usize>,
31 pub phi_def_id: DefId,
32 pub essa_def_id: DefId,
33 pub ref_local_map: HashMap<Local, Local>,
34 pub places_map: HashMap<Place<'tcx>, HashSet<Place<'tcx>>>,
35 pub ssa_locals_map: HashMap<Place<'tcx>, HashSet<Place<'tcx>>>,
36}
37
38impl<'tcx> SSATransformer<'tcx> {
39
40 pub fn new(
41 tcx: TyCtxt<'tcx>,
42 body: &Body<'tcx>,
43 ssa_def_id: DefId,
44 essa_def_id: DefId,
45 arg_count: usize,
46 ) -> Self {
47 let cfg: HashMap<BasicBlock, Vec<BasicBlock>> = Self::extract_cfg_from_predecessors(&body);
48
49 let dominators: Dominators<BasicBlock> = body.basic_blocks.dominators().clone();
50
51 let dom_tree: HashMap<BasicBlock, Vec<BasicBlock>> = Self::construct_dominance_tree(&body);
52
53 let df: HashMap<BasicBlock, HashSet<BasicBlock>> =
54 Self::compute_dominance_frontier(&body, &dom_tree);
55
56 let local_assign_blocks: HashMap<Local, HashSet<BasicBlock>> =
57 Self::map_locals_to_assign_blocks(&body);
58 let local_defination_block: HashMap<Local, BasicBlock> =
59 Self::map_locals_to_definition_block(&body);
60 let len = body.local_decls.len() as usize;
61 let mut skipped = HashSet::new();
62 if len > 0 {
63 skipped.extend(arg_count + 1..len + 1);
64 }
66
67 SSATransformer {
68 tcx,
69 body: body.clone(),
70 cfg,
71 dominators,
72 dom_tree,
73 df,
74 local_assign_blocks,
75 reaching_def: HashMap::default(),
76 local_index: len,
77 local_defination_block: local_defination_block,
78 skipped: skipped,
79 phi_index: HashMap::default(),
80 phi_def_id: ssa_def_id,
81 essa_def_id: essa_def_id,
82 ref_local_map: HashMap::default(),
83 places_map: HashMap::default(),
84 ssa_locals_map: HashMap::default(),
85 }
86 }
87
88
89
90 fn map_locals_to_definition_block(body: &Body) -> HashMap<Local, BasicBlock> {
91 let mut local_to_block_map: HashMap<Local, BasicBlock> = HashMap::new();
92
93 for (bb, block_data) in body.basic_blocks.iter_enumerated() {
94 for statement in &block_data.statements {
95 match &statement.kind {
96 StatementKind::Assign(assign) => {
97 let (place, _) = &**assign;
98 if let Some(local) = place.as_local() {
99 if local.as_u32() == 0 {
100 continue; }
102 local_to_block_map.entry(local).or_insert(bb);
103 }
104 }
105 _ => {}
106 }
107 }
108 if let Some(terminator) = &block_data.terminator {
109 match &terminator.kind {
110 TerminatorKind::Call { destination, .. } => {
111 if let Some(local) = destination.as_local() {
112 if local.as_u32() == 0 {
113 continue; }
115 local_to_block_map.entry(local).or_insert(bb);
116 }
117 }
118 _ => {}
119 }
120 }
121 }
122
123 local_to_block_map
124 }
125 pub fn depth_first_search_preorder(
126 dom_tree: &HashMap<BasicBlock, Vec<BasicBlock>>,
127 root: BasicBlock,
128 ) -> Vec<BasicBlock> {
129 let mut visited: HashSet<BasicBlock> = HashSet::new();
130 let mut preorder = Vec::new();
131
132 fn dfs(
133 node: BasicBlock,
134 dom_tree: &HashMap<BasicBlock, Vec<BasicBlock>>,
135 visited: &mut HashSet<BasicBlock>,
136 preorder: &mut Vec<BasicBlock>,
137 ) {
138 if visited.insert(node) {
139 preorder.push(node);
140
141 if let Some(children) = dom_tree.get(&node) {
142 for &child in children {
143 dfs(child, dom_tree, visited, preorder);
144 }
145 }
146 }
147 }
148
149 dfs(root, dom_tree, &mut visited, &mut preorder);
150 preorder
151 }
152
153
154 fn map_locals_to_assign_blocks(body: &Body) -> HashMap<Local, HashSet<BasicBlock>> {
155 let mut local_to_blocks: HashMap<Local, HashSet<BasicBlock>> = HashMap::new();
156
157 for (bb, data) in body.basic_blocks.iter_enumerated() {
158 for stmt in &data.statements {
159 if let StatementKind::Assign(assign) = &stmt.kind {
160 let (place, _) = &**assign;
161 let local = place.local;
162 if local.as_u32() == 0 {
163 continue; }
165 local_to_blocks
166 .entry(local)
167 .or_insert_with(HashSet::new)
168 .insert(bb);
169 }
170 }
171 }
172 for arg in body.args_iter() {
173 local_to_blocks
174 .entry(arg)
175 .or_insert_with(HashSet::new)
176 .insert(BasicBlock::from_u32(0)); }
178 local_to_blocks
179 }
180 fn construct_dominance_tree(body: &Body<'_>) -> HashMap<BasicBlock, Vec<BasicBlock>> {
181 let mut dom_tree: HashMap<BasicBlock, Vec<BasicBlock>> = HashMap::new();
182 let dominators = body.basic_blocks.dominators();
183 for (block, _) in body.basic_blocks.iter_enumerated() {
184 if let Some(idom) = dominators.immediate_dominator(block) {
185 dom_tree.entry(idom).or_default().push(block);
186 }
187 }
188
189 dom_tree
190 }
191 fn compute_dominance_frontier(
192 body: &Body<'_>,
193 dom_tree: &HashMap<BasicBlock, Vec<BasicBlock>>,
194 ) -> HashMap<BasicBlock, HashSet<BasicBlock>> {
195 let mut dominance_frontier: HashMap<BasicBlock, HashSet<BasicBlock>> = HashMap::new();
196 let dominators = body.basic_blocks.dominators();
197 let predecessors = body.basic_blocks.predecessors();
198 for (block, _) in body.basic_blocks.iter_enumerated() {
199 dominance_frontier.entry(block).or_default();
200 }
201
202 for (block, _) in body.basic_blocks.iter_enumerated() {
203 if predecessors[block].len() > 1 {
204 let preds = body.basic_blocks.predecessors()[block].clone();
205
206 for &pred in &preds {
207 let mut runner = pred;
208 while runner != dominators.immediate_dominator(block).unwrap() {
209 dominance_frontier.entry(runner).or_default().insert(block);
210 runner = dominators.immediate_dominator(runner).unwrap();
211 }
212 }
213 }
214 }
215
216 dominance_frontier
217 }
218 fn extract_cfg_from_predecessors(body: &Body<'_>) -> HashMap<BasicBlock, Vec<BasicBlock>> {
219 let mut cfg: HashMap<BasicBlock, Vec<BasicBlock>> = HashMap::new();
220
221 for (block, _) in body.basic_blocks.iter_enumerated() {
222 for &predecessor in body.basic_blocks.predecessors()[block].iter() {
223 cfg.entry(predecessor).or_default().push(block);
224 }
225 }
226
227 cfg
228 }
229
230
231 pub fn is_phi_statement(&self, statement: &Statement<'tcx>) -> bool {
232 if let StatementKind::Assign(assign) = &statement.kind {
233 let (_, rvalue) = &**assign;
234 if let Rvalue::Aggregate(k_box, _) = rvalue {
235 let aggregate_kind = &**k_box;
236 if let AggregateKind::Adt(def_id, ..) = aggregate_kind {
237 return *def_id == self.phi_def_id;
238 }
239 }
240 }
241 false
242 }
243
244 pub fn is_essa_statement(&self, statement: &Statement<'tcx>) -> bool {
245 if let StatementKind::Assign(assign) = &statement.kind {
246 let (_, rvalue) = &**assign;
247 if let Rvalue::Aggregate(k_box, _) = rvalue {
248 let aggregate_kind = &**k_box;
249 if let AggregateKind::Adt(def_id, ..) = aggregate_kind {
250 return *def_id == self.essa_def_id;
251 }
252 }
253 }
254 false
255 }
256 pub fn get_essa_source_block(&self, statement: &Statement<'tcx>) -> Option<BasicBlock> {
257 if !self.is_essa_statement(statement) {
258 return None;
259 }
260
261 if let StatementKind::Assign(assign) = &statement.kind {
262 let (_, rvalue) = &**assign;
263 if let Rvalue::Aggregate(_, operands) = rvalue {
264 if let Some(last_op) = operands.into_iter().last() {
265 if let Operand::Constant(c_box) = last_op {
266 let ConstOperand { const_: c, .. } = &**c_box;
267 if let Some(val) = self.try_const_to_usize(c) {
268 return Some(BasicBlock::from_usize(val as usize));
269 }
270 }
271 }
272 }
273 }
274 None
275 }
276
277 fn try_const_to_usize(&self, c: &Const<'tcx>) -> Option<u64> {
278 if let Some(scalar_int) = c.try_to_scalar_int() {
279 let size = scalar_int.size();
280 let bits = scalar_int.to_bits(size);
281 return Some(bits as u64);
282 }
283 None
284 }
285}