1use rustc_hir::def_id::DefId;
2use rustc_middle::{
3 mir::{
4 Operand::{self, Constant, Copy, Move},
5 SwitchTargets, TerminatorKind,
6 },
7 ty::{TyKind, TypingEnv},
8};
9
10use std::collections::HashSet;
11
12use crate::analysis::graphs::scc::{SccInfo, SccTree};
13
14use super::{block::Term, graph::*, *};
15
16impl<'tcx> MopGraph<'tcx> {
17 pub fn split_check(
18 &mut self,
19 bb_idx: usize,
20 fn_map: &mut MopFnAliasMap,
21 recursion_set: &mut HashSet<DefId>,
22 ) {
23 let backup_values = self.values.clone(); let backup_constant = self.constants.clone();
26 let backup_alias_sets = self.alias_sets.clone();
27 self.check(bb_idx, fn_map, recursion_set);
28 self.alias_sets = backup_alias_sets;
30 self.values = backup_values;
31 self.constants = backup_constant;
32 }
33 pub fn split_check_with_cond(
34 &mut self,
35 bb_idx: usize,
36 path_discr_id: usize,
37 path_discr_val: usize,
38 fn_map: &mut MopFnAliasMap,
39 recursion_set: &mut HashSet<DefId>,
40 ) {
41 let backup_values = self.values.clone(); let backup_constant = self.constants.clone();
44 let backup_alias_sets = self.alias_sets.clone();
45 self.constants.insert(path_discr_id, path_discr_val);
47 self.check(bb_idx, fn_map, recursion_set);
48 self.alias_sets = backup_alias_sets;
50 self.values = backup_values;
51 self.constants = backup_constant;
52 }
53
54 pub fn check(
56 &mut self,
57 bb_idx: usize,
58 fn_map: &mut MopFnAliasMap,
59 recursion_set: &mut HashSet<DefId>,
60 ) {
61 self.visit_times += 1;
62 if self.visit_times > VISIT_LIMIT {
63 return;
64 }
65 let scc_idx = self.blocks[bb_idx].scc.enter;
66 let cur_block = self.blocks[bb_idx].clone();
67
68 rap_debug!("check {:?}", bb_idx);
69 if bb_idx == scc_idx && !cur_block.scc.nodes.is_empty() {
70 rap_debug!("check {:?} as a scc", bb_idx);
71 self.check_scc(bb_idx, fn_map, recursion_set);
72 } else {
73 self.check_single_node(bb_idx, fn_map, recursion_set);
74 self.handle_nexts(bb_idx, fn_map, None, recursion_set);
75 }
76 }
77
78 pub fn check_scc(
79 &mut self,
80 bb_idx: usize,
81 fn_map: &mut MopFnAliasMap,
82 recursion_set: &mut HashSet<DefId>,
83 ) {
84 let cur_block = self.blocks[bb_idx].clone();
85
86 rap_debug!("Searchng paths in scc: {:?}, {:?}", bb_idx, cur_block.scc);
88 let scc_tree = self.sort_scc_tree(&cur_block.scc);
89 rap_debug!("scc_tree: {:?}", scc_tree);
90 let paths_in_scc = self.find_scc_paths(bb_idx, &scc_tree, &mut FxHashMap::default());
91 rap_debug!("Paths found in scc: {:?}", paths_in_scc);
92
93 let backup_values = self.values.clone(); let backup_constant = self.constants.clone();
95 let backup_alias_sets = self.alias_sets.clone();
96 let backup_recursion_set = recursion_set.clone();
97 for raw_path in paths_in_scc {
98 self.alias_sets = backup_alias_sets.clone();
99 self.values = backup_values.clone();
100 self.constants = backup_constant.clone();
101 *recursion_set = backup_recursion_set.clone();
102
103 let path = raw_path.0;
104 let path_constraints = &raw_path.1;
105 rap_debug!("checking path: {:?}", path);
106 if !path.is_empty() {
107 for idx in &path[..path.len() - 1] {
108 self.alias_bb(*idx);
109 self.alias_bbcall(*idx, fn_map, recursion_set);
110 }
111 }
112 if let Some(&last_node) = path.last() {
114 if self.blocks[last_node].scc.nodes.is_empty() {
115 self.check_single_node(last_node, fn_map, recursion_set);
116 self.handle_nexts(last_node, fn_map, Some(path_constraints), recursion_set);
117 } else {
118 }
120 }
121 }
122 }
123
124 pub fn check_single_node(
125 &mut self,
126 bb_idx: usize,
127 fn_map: &mut MopFnAliasMap,
128 recursion_set: &mut HashSet<DefId>,
129 ) {
130 rap_debug!("check {:?} as a node", bb_idx);
131 let cur_block = self.blocks[bb_idx].clone();
132 self.alias_bb(self.blocks[bb_idx].scc.enter);
133 self.alias_bbcall(self.blocks[bb_idx].scc.enter, fn_map, recursion_set);
134 if cur_block.next.is_empty() {
135 self.merge_results();
136 return;
137 }
138 }
139
140 pub fn handle_nexts(
141 &mut self,
142 bb_idx: usize,
143 fn_map: &mut MopFnAliasMap,
144 path_constraints: Option<&FxHashMap<usize, usize>>,
145 recursion_set: &mut HashSet<DefId>,
146 ) {
147 let cur_block = self.blocks[bb_idx].clone();
148 let tcx = self.tcx;
149
150 rap_debug!(
151 "handle nexts {:?} of node {:?}",
152 self.blocks[bb_idx].next,
153 bb_idx
154 );
155 if let Some(path_constraints) = path_constraints {
157 self.constants.extend(path_constraints);
158 }
159 let mut single_target = false;
161 let mut sw_val = 0;
162 let mut sw_target = 0; let mut path_discr_id = 0; let mut sw_targets = None; match cur_block.terminator {
167 Term::Switch(switch) => {
168 if let TerminatorKind::SwitchInt {
169 ref discr,
170 ref targets,
171 } = switch.kind
172 {
173 match discr {
174 Copy(p) | Move(p) => {
175 let value_idx = self.projection(*p);
176 let local_decls = &tcx.optimized_mir(self.def_id).local_decls;
177 let place_ty = (*p).ty(local_decls, tcx);
178 rap_debug!("value_idx: {:?}", value_idx);
179 match place_ty.ty.kind() {
180 TyKind::Bool => {
181 if let Some(constant) = self.constants.get(&value_idx) {
182 if *constant != usize::MAX {
183 single_target = true;
184 sw_val = *constant;
185 }
186 }
187 path_discr_id = value_idx;
188 sw_targets = Some(targets.clone());
189 }
190 _ => {
191 if let Some(father) =
192 self.discriminants.get(&self.values[value_idx].local)
193 {
194 if let Some(constant) = self.constants.get(father) {
195 if *constant != usize::MAX {
196 single_target = true;
197 sw_val = *constant;
198 }
199 }
200 if self.values[value_idx].local == value_idx {
201 path_discr_id = *father;
202 sw_targets = Some(targets.clone());
203 }
204 }
205 }
206 }
207 }
208 Constant(c) => {
209 single_target = true;
210 let ty_env = TypingEnv::post_analysis(tcx, self.def_id);
211 if let Some(val) = c.const_.try_eval_target_usize(tcx, ty_env) {
212 sw_val = val as usize;
213 }
214 }
215 }
216 if single_target {
217 rap_debug!("targets: {:?}; sw_val = {:?}", targets, sw_val);
222 for iter in targets.iter() {
223 if iter.0 as usize == sw_val {
224 sw_target = iter.1.as_usize();
225 break;
226 }
227 }
228 if sw_target == 0 {
233 let all_target = targets.all_targets();
234 sw_target = all_target[all_target.len() - 1].as_usize();
235 }
236 }
237 }
238 }
239 _ => {
240 }
242 }
243 if single_target {
246 rap_debug!("visit a single target: {:?}", sw_target);
247 self.check(sw_target, fn_map, recursion_set);
248 } else {
249 if let Some(targets) = sw_targets {
251 for iter in targets.iter() {
252 if self.visit_times > VISIT_LIMIT {
253 continue;
254 }
255 let next = iter.1.as_usize();
256 let path_discr_val = iter.0 as usize;
257 self.split_check_with_cond(
258 next,
259 path_discr_id,
260 path_discr_val,
261 fn_map,
262 recursion_set,
263 );
264 }
265 let all_targets = targets.all_targets();
266 let next_index = all_targets[all_targets.len() - 1].as_usize();
267 let path_discr_val = usize::MAX; self.split_check_with_cond(
269 next_index,
270 path_discr_id,
271 path_discr_val,
272 fn_map,
273 recursion_set,
274 );
275 } else {
276 for next in cur_block.next {
277 if self.visit_times > VISIT_LIMIT {
278 continue;
279 }
280 self.split_check(next, fn_map, recursion_set);
281 }
282 }
283 }
284 }
285
286 pub fn sort_scc_tree(&mut self, scc: &SccInfo) -> SccTree {
287 let mut child_sccs: FxHashMap<usize, SccInfo> = FxHashMap::default();
289
290 for &node in scc.nodes.iter() {
292 let node_scc = &self.blocks[node].scc;
293 if node_scc.enter != scc.enter && !node_scc.nodes.is_empty() {
294 child_sccs
295 .entry(node_scc.enter)
296 .or_insert_with(|| node_scc.clone());
297 }
298 }
299
300 let children = child_sccs
302 .into_values()
303 .map(|child_scc| self.sort_scc_tree(&child_scc))
304 .collect();
305
306 SccTree {
307 scc: scc.clone(),
308 children,
309 }
310 }
311
312 pub fn find_scc_paths(
313 &mut self,
314 start: usize,
315 scc_tree: &SccTree,
316 path_constraints: &mut FxHashMap<usize, usize>,
317 ) -> Vec<(Vec<usize>, FxHashMap<usize, usize>)> {
318 let mut all_paths = Vec::new();
319 let mut scc_path_set: HashSet<Vec<usize>> = HashSet::new();
320
321 self.find_scc_paths_inner(
322 start,
323 start,
324 scc_tree,
325 &mut vec![start],
326 path_constraints,
327 &mut all_paths,
328 &mut scc_path_set,
329 );
330
331 all_paths
332 }
333
334 fn find_scc_paths_inner(
335 &mut self,
336 start: usize,
337 cur: usize,
338 scc_tree: &SccTree,
339 path: &mut Vec<usize>,
340 path_constraints: &mut FxHashMap<usize, usize>,
341 paths_in_scc: &mut Vec<(Vec<usize>, FxHashMap<usize, usize>)>,
342 scc_path_set: &mut HashSet<Vec<usize>>,
343 ) {
344 rap_debug!("cur = {}", cur);
345 let scc = &scc_tree.scc;
346 if scc.nodes.is_empty() {
347 paths_in_scc.push((path.clone(), path_constraints.clone()));
348 return;
349 }
350 if path.len() > 100 || paths_in_scc.len() > 100 {
352 return;
353 }
354 if !scc.nodes.contains(&cur) && start != cur {
355 rap_debug!("new path: {:?}", path.clone());
356 paths_in_scc.push((path.clone(), path_constraints.clone()));
359 return;
360 }
361
362 if cur == start && path.len() > 1 {
363 let last_index = path[..path.len() - 1]
364 .iter()
365 .rposition(|&node| node == start)
366 .map(|i| i)
367 .unwrap_or(0);
368 let slice = &path[last_index..];
369 rap_debug!("path: {:?}", path);
370 rap_debug!("slice: {:?}", slice);
371 rap_debug!("set: {:?}", scc_path_set);
372 if scc_path_set.contains(slice) {
373 return;
374 } else {
375 scc_path_set.insert(slice.to_vec());
376 }
377 }
378
379 for local in &self.blocks[cur].assigned_locals {
382 rap_debug!(
383 "Remove path_constraints {:?}, because it has been reassigned.",
384 local
385 );
386 path_constraints.remove(&local);
387 }
388
389 for child_tree in &scc_tree.children {
391 let child_enter = child_tree.scc.enter;
392 if cur == child_enter {
393 let sub_paths = self.find_scc_paths(child_enter, child_tree, path_constraints);
394 rap_debug!("paths in sub scc: {}, {:?}", child_enter, sub_paths);
395 for (subp, subconst) in sub_paths {
396 let mut new_path = path.clone();
397 new_path.extend(&subp[1..]);
398 let mut new_const = path_constraints.clone();
399 new_const.extend(subconst.iter());
400 self.find_scc_paths_inner(
401 start,
402 *new_path.last().unwrap(),
403 scc_tree,
404 &mut new_path,
405 &mut new_const,
406 paths_in_scc,
407 scc_path_set,
408 );
409 }
410 return;
411 }
412 }
413
414 let term = &self.terminators[cur].clone();
415
416 rap_debug!("term: {:?}", term);
417 match term {
418 TerminatorKind::SwitchInt { discr, targets } => {
419 self.handle_switch_int_case(
420 start,
421 cur,
422 path,
423 scc_tree,
424 path_constraints,
425 paths_in_scc,
426 discr,
427 targets,
428 scc_path_set,
429 );
430 }
431 _ => {
432 for next in self.blocks[cur].next.clone() {
433 path.push(next);
434 self.find_scc_paths_inner(
435 start,
436 next,
437 scc_tree,
438 path,
439 path_constraints,
440 paths_in_scc,
441 scc_path_set,
442 );
443 rap_debug!("pop 1 : {:?}", path.last());
444 path.pop();
445 }
446 }
447 }
448 }
449
450 fn handle_switch_int_case(
451 &mut self,
452 start: usize,
453 _cur: usize,
454 path: &mut Vec<usize>,
455 scc_tree: &SccTree,
456 path_constraints: &mut FxHashMap<usize, usize>,
457 paths_in_scc: &mut Vec<(Vec<usize>, FxHashMap<usize, usize>)>,
458 discr: &Operand<'tcx>,
459 targets: &SwitchTargets,
460 scc_path_set: &mut std::collections::HashSet<Vec<usize>>,
461 ) {
462 let place = match discr {
463 Copy(p) | Move(p) => Some(self.projection(*p)),
464 _ => None,
465 };
466
467 if let Some(place) = place {
468 let discr_local = self
469 .discriminants
470 .get(&self.values[place].local)
471 .cloned()
472 .unwrap_or(place);
473
474 rap_debug!(
475 "Handle SwitchInt, discr = {:?}, local = {:?}",
476 discr,
477 discr_local
478 );
479 if let Some(&constant) = path_constraints.get(&discr_local) {
480 rap_debug!("constant = {:?}", constant);
481 rap_debug!("targets = {:?}", targets);
482 let mut otherwise = true;
483 targets
485 .iter()
486 .filter(|t| t.0 as usize == constant)
487 .for_each(|branch| {
488 let target = branch.1.as_usize();
489 path.push(target);
490 self.find_scc_paths_inner(
491 start,
492 target,
493 scc_tree,
494 path,
495 path_constraints,
496 paths_in_scc,
497 scc_path_set,
498 );
499 rap_debug!("pop 2: {:?}", path.last());
500 path.pop();
501 otherwise = false;
502 });
503 if otherwise {
504 let target = targets.otherwise().as_usize();
505 path.push(target);
506 path_constraints.insert(discr_local, targets.iter().len());
507 self.find_scc_paths_inner(
508 start,
509 target,
510 scc_tree,
511 path,
512 path_constraints,
513 paths_in_scc,
514 scc_path_set,
515 );
516 path_constraints.remove(&discr_local);
517 rap_debug!("pop 3: {:?}", path.last());
518 path.pop();
519 }
520 } else {
521 for branch in targets.iter() {
523 let constant = branch.0 as usize;
524 let target = branch.1.as_usize();
525 path.push(target);
526 path_constraints.insert(discr_local, constant);
527 self.find_scc_paths_inner(
528 start,
529 target,
530 scc_tree,
531 path,
532 path_constraints,
533 paths_in_scc,
534 scc_path_set,
535 );
536 path_constraints.remove(&discr_local);
537 rap_debug!("pop 4: {:?}", path.last());
538 path.pop();
539 }
540 let target = targets.otherwise().as_usize();
542 path.push(target);
543 path_constraints.insert(discr_local, targets.iter().len());
544 self.find_scc_paths_inner(
545 start,
546 target,
547 scc_tree,
548 path,
549 path_constraints,
550 paths_in_scc,
551 scc_path_set,
552 );
553 path_constraints.remove(&discr_local);
554 rap_debug!("pop 5: {:?}", path.last());
555 path.pop();
556 }
557 }
558 }
559}