1
2use crate::analysis::range::domain::domain::*;
3use crate::analysis::range::{Range, RangeType};
4
5use crate::analysis::range::domain::symbolic_expr::*;
6use crate::compat::Spanned;
7use rustc_abi::FieldIdx;
8use rustc_hir::def_id::LOCAL_CRATE;
9use rustc_hir::def_id::DefId;
10use rustc_index::IndexVec;
11use rustc_middle::{
12 mir::*,
13 ty::{self},
14};
15
16use std::{
17 collections::{HashMap, HashSet},
18 fmt::Debug,
19};
20
21use super::ConstraintGraph;
22
23impl<'tcx, T> ConstraintGraph<'tcx, T>
24where
25 T: IntervalArithmetic + ConstConvert + Debug,
26{
27 fn register_op(&mut self, op: BasicOpKind<'tcx, T>, sink: &'tcx Place<'tcx>) -> usize {
28 let idx = self.oprs.len();
29 self.oprs.push(op);
30 self.defmap.insert(sink, idx);
31 idx
32 }
33
34 pub fn add_varnode(&mut self, v: &'tcx Place<'tcx>) -> &mut VarNode<'tcx, T> {
35 let local_decls = &self.body.local_decls;
36
37 let node = VarNode::new(v);
38 let node_ref: &mut VarNode<'tcx, T> = self
39 .vars
40 .entry(v)
41 .or_insert(node);
42 self.usemap.entry(v).or_insert(HashSet::new());
43
44 let ty = local_decls[v.local].ty;
45 let place_ty = v.ty(local_decls, self.tcx);
46
47 if v.projection.is_empty() || self.defmap.contains_key(v) {
48 return node_ref;
49 }
50
51 if !v.projection.is_empty() {
52 let matches: Vec<(_, _)> = self
53 .defmap
54 .iter()
55 .filter(|(p, _)| p.local == v.local && p.projection.is_empty())
56 .map(|(p, def_op)| (*p, *def_op))
57 .collect();
58
59 for (base_place, def_op) in matches {
60 let mut v_op = self.oprs[def_op].clone();
61 v_op.set_sink(v);
62
63 for source in v_op.get_sources() {
64 self.usemap
65 .entry(source)
66 .or_insert(HashSet::new())
67 .insert(self.oprs.len());
68 }
69
70 self.oprs.push(v_op);
71 self.defmap.insert(v, self.oprs.len() - 1);
72 }
73 }
74
75 node_ref
76 }
77
78 pub fn use_add_varnode_sym(
79 &mut self,
80 v: &'tcx Place<'tcx>,
81 rvalue: &'tcx Rvalue<'tcx>,
82 ) -> &mut VarNode<'tcx, T> {
83 if !self.vars.contains_key(v) {
84 let place_ctx: Vec<&Place<'tcx>> = self.vars.keys().map(|p| *p).collect();
85 let node = VarNode::new_symb(v, SymbExpr::from_rvalue(rvalue, place_ctx.clone()));
86 rap_debug!("use node:{:?}", node);
87
88 self.vars.insert(v, node);
89 self.usemap.entry(v).or_insert(HashSet::new());
90
91 if !(v.projection.is_empty() || self.defmap.contains_key(v)) {
92 let matches: Vec<_> = self
93 .defmap
94 .iter()
95 .filter(|(p, _)| p.local == v.local && p.projection.is_empty())
96 .map(|(p, &def_op)| (*p, def_op))
97 .collect();
98
99 for (base_place, def_op) in matches {
100 let mut v_op = self.oprs[def_op].clone();
101 v_op.set_sink(v);
102
103 for source in v_op.get_sources() {
104 self.usemap
105 .entry(source)
106 .or_insert(HashSet::new())
107 .insert(self.oprs.len());
108 }
109
110 self.oprs.push(v_op);
111 self.defmap.insert(v, self.oprs.len() - 1);
112 }
113 }
114 }
115
116 self.vars.get_mut(v).unwrap()
117 }
118
119 pub fn def_add_varnode_sym(
120 &mut self,
121 v: &'tcx Place<'tcx>,
122 rvalue: &'tcx Rvalue<'tcx>,
123 ) -> &mut VarNode<'tcx, T> {
124 let place_ctx: Vec<&Place<'tcx>> = self.vars.keys().map(|p| *p).collect();
125
126 let local_decls = &self.body.local_decls;
127 let node = VarNode::new_symb(v, SymbExpr::from_rvalue(rvalue, place_ctx.clone()));
128 rap_debug!("def node:{:?}", node);
129 let node_ref: &mut VarNode<'tcx, T> = self
130 .vars
131 .entry(v)
132 .and_modify(|old| *old = node.clone())
133 .or_insert(node);
134 self.usemap.entry(v).or_insert(HashSet::new());
135
136 let ty = local_decls[v.local].ty;
137 let place_ty = v.ty(local_decls, self.tcx);
138
139 if v.projection.is_empty() || self.defmap.contains_key(v) {
140 return node_ref;
141 }
142
143 if !v.projection.is_empty() {
144 let matches: Vec<(_, _)> = self
145 .defmap
146 .iter()
147 .filter(|(p, _)| p.local == v.local && p.projection.is_empty())
148 .map(|(p, &def_op)| (*p, def_op))
149 .collect();
150
151 for (base_place, def_op) in matches {
152 let mut v_op = self.oprs[def_op].clone();
153 v_op.set_sink(v);
154
155 for source in v_op.get_sources() {
156 self.usemap
157 .entry(source)
158 .or_insert(HashSet::new())
159 .insert(self.oprs.len());
160 }
161
162 self.oprs.push(v_op);
163 self.defmap.insert(v, self.oprs.len() - 1);
164 }
165 }
166 node_ref
167 }
168
169 pub fn resolve_all_symexpr(&mut self) {
170 let lookup_context = self.vars.clone();
171 let mut nodes: Vec<&mut VarNode<'tcx, T>> = self.vars.values_mut().collect();
172 nodes.sort_by(|a, b| a.v.local.as_usize().cmp(&b.v.local.as_usize()));
173 for node in nodes {
174 if let IntervalType::Basic(basic) = &mut node.interval {
175 rap_debug!("======{}=====", node.v.local.as_usize());
176 rap_debug!("Before resolve: lower_expr: {}\n", basic.lower);
177 basic.lower.resolve_lower_bound(&lookup_context);
178 basic.lower.simplify();
179 rap_debug!("After resolve: lower_expr: {}\n", basic.lower);
180 rap_debug!("Before resolve: upper_expr: {}\n", basic.upper);
181 basic.upper.resolve_upper_bound(&lookup_context);
182 basic.upper.simplify();
183
184 rap_debug!("After resolve: upper_expr: {}\n", basic.upper);
185 }
186 }
187 }
188
189 pub fn postprocess_defmap(&mut self) {
190 for place in self.vars.keys() {
191 if !place.projection.is_empty() {
192 if let Some((&base_place, &base_value)) = self
193 .defmap
194 .iter()
195 .find(|(p, _)| p.local == place.local && p.projection.is_empty())
196 {
197 self.defmap.insert(place, base_value);
198 } else {
199 rap_trace!("postprocess_defmap: No base place found for {:?}", place);
200 }
201 }
202 }
203 }
204
205 pub fn build_graph(&mut self, body: &'tcx Body<'tcx>) {
206 self.build_value_maps(body);
207 for block in body.basic_blocks.indices() {
208 let block_data: &BasicBlockData<'tcx> = &body[block];
209 for statement in block_data.statements.iter() {
210 self.build_operations(statement, block, body);
211 }
212 self.build_terminator(block, block_data.terminator.as_ref().unwrap());
213 }
214 self.resolve_all_symexpr();
215 self.print_vars();
216 self.print_defmap();
217 self.print_usemap();
218 self.print_symbexpr();
219 }
220
221 pub fn build_value_maps(&mut self, body: &'tcx Body<'tcx>) {
222 for bb in body.basic_blocks.indices() {
223 let block_data = &body[bb];
224 if let Some(terminator) = &block_data.terminator {
225 match &terminator.kind {
226 TerminatorKind::SwitchInt { discr, targets } => {
227 if targets.iter().count() == 1 {
228 self.build_value_branch_map(body, discr, targets, bb, block_data);
229 }
230 }
231 _ => {}
232 }
233 }
234 }
235 }
236
237 fn trace_operand_origin(
238 &self,
239 body: &'tcx Body<'tcx>,
240 mut current_block: BasicBlock,
241 target_place: Place<'tcx>,
242 original: &'tcx Operand<'tcx>,
243 ) -> &'tcx Operand<'tcx> {
244 let mut visited = HashSet::new();
245 let target_local = target_place.local;
246 while visited.insert(current_block) {
247 let data = &body.basic_blocks[current_block];
248 for stmt in data.statements.iter().rev() {
249 if let StatementKind::Assign(assign) = &stmt.kind {
250 let (lhs, rvalue) = &**assign;
251 if lhs.local == target_local {
252 return match rvalue {
253 Rvalue::Use(op, ..) => op,
254 _ => original,
255 };
256 }
257 }
258 }
259 let preds = &body.basic_blocks.predecessors()[current_block];
260 if preds.len() == 1 {
261 current_block = preds[0];
262 } else {
263 break;
264 }
265 }
266 original
267 }
268
269 pub fn build_value_branch_map(
270 &mut self,
271 body: &'tcx Body<'tcx>,
272 discr: &'tcx Operand<'tcx>,
273 targets: &'tcx SwitchTargets,
274 switch_block: BasicBlock,
275 block_data: &'tcx BasicBlockData<'tcx>,
276 ) {
277 if let Operand::Copy(place) | Operand::Move(place) = discr {
278 if let Some((op1, op2, cmp_op)) = self.extract_condition(place, block_data) {
279 rap_debug!(
280 "extract_condition op1:{:?} op2:{:?} cmp_op:{:?}\n",
281 op1,
282 op2,
283 cmp_op
284 );
285 let op1 = if let Some(p1) = op1.place() {
286 self.trace_operand_origin(body, switch_block, p1, op1)
287 } else {
288 op1
289 };
290
291 let op2 = if let Some(p2) = op2.place() {
292 self.trace_operand_origin(body, switch_block, p2, op2)
293 } else {
294 op2
295 };
296 rap_debug!(
297 "build_value_branch_map op1:{:?} op2:{:?} cmp_op:{:?}\n",
298 op1,
299 op2,
300 cmp_op
301 );
302 let const_op1 = op1.constant();
303 let const_op2 = op2.constant();
304 match (const_op1, const_op2) {
305 (Some(_), Some(_)) => {}
306 (Some(c), None) | (None, Some(c)) => {
307 let const_in_left: bool;
308 let variable;
309 if const_op1.is_some() {
310 const_in_left = true;
311 variable = match op2 {
312 Operand::Copy(p) | Operand::Move(p) => p,
313 _ => panic!("Expected a place"),
314 };
315 } else {
316 const_in_left = false;
317 variable = match op1 {
318 Operand::Copy(p) | Operand::Move(p) => p,
319 _ => panic!("Expected a place"),
320 };
321 }
322 self.add_varnode(variable);
323 rap_trace!("add_vbm_varnode{:?}\n", variable.clone());
324
325 let Some(value) = T::from_const(&c.const_) else {
326 rap_trace!("from_const returned None for const {:?}, skipping VBM", c);
327 return;
328 };
329 let const_range =
330 Range::new(value.clone(), value.clone(), RangeType::Unknown);
331 rap_trace!("cmp_op {:?}\n", cmp_op);
332 rap_trace!("const_in_left {:?}\n", const_in_left);
333 let mut true_range =
334 self.apply_comparison(value.clone(), cmp_op, true, const_in_left);
335 let mut false_range =
336 self.apply_comparison(value.clone(), cmp_op, false, const_in_left);
337 true_range.set_regular();
338 false_range.set_regular();
339 let target_vec = targets.all_targets();
340
341 let vbm = ValueBranchMap::new(
342 variable,
343 &target_vec[0],
344 &target_vec[1],
345 IntervalType::Basic(BasicInterval::new(false_range)),
346 IntervalType::Basic(BasicInterval::new(true_range)),
347 );
348 self.values_branchmap.insert(variable, vbm);
349 }
350 (None, None) => {
351 let CR = Range::new(T::min_value(), T::max_value(), RangeType::Unknown);
352
353 let p1 = match op1 {
354 Operand::Copy(p) | Operand::Move(p) => p,
355 _ => panic!("Expected a place"),
356 };
357 let p2 = match op2 {
358 Operand::Copy(p) | Operand::Move(p) => p,
359 _ => panic!("Expected a place"),
360 };
361 let target_vec = targets.all_targets();
362 self.add_varnode(&p1);
363 rap_trace!("add_vbm_varnode{:?}\n", p1.clone());
364
365 self.add_varnode(&p2);
366 rap_trace!("add_vbm_varnode{:?}\n", p2.clone());
367 let flipped_cmp_op = match Self::flipped_binop(cmp_op) {
368 Some(op) => op,
369 None => {
370 rap_debug!(
371 "build_value_branch_map: unsupported binop {:?}, skipping\n",
372 cmp_op
373 );
374 return;
375 }
376 };
377 let reversed_cmp_op = match Self::reverse_binop(cmp_op) {
378 Some(op) => op,
379 None => {
380 rap_debug!(
381 "build_value_branch_map: unsupported binop {:?}, skipping\n",
382 cmp_op
383 );
384 return;
385 }
386 };
387 let reversed_flippedd_cmp_op = match Self::flipped_binop(reversed_cmp_op) {
388 Some(op) => op,
389 None => {
390 rap_debug!(
391 "build_value_branch_map: unsupported binop {:?}, skipping\n",
392 reversed_cmp_op
393 );
394 return;
395 }
396 };
397 let STOp1 = IntervalType::Symb(SymbInterval::new(CR.clone(), p2, cmp_op));
398 let SFOp1 =
399 IntervalType::Symb(SymbInterval::new(CR.clone(), p2, flipped_cmp_op));
400 let STOp2 =
401 IntervalType::Symb(SymbInterval::new(CR.clone(), p1, reversed_cmp_op));
402 let SFOp2 = IntervalType::Symb(SymbInterval::new(
403 CR.clone(),
404 p1,
405 reversed_flippedd_cmp_op,
406 ));
407 rap_trace!("SFOp1{:?}\n", SFOp1);
408 rap_trace!("SFOp2{:?}\n", SFOp2);
409 rap_trace!("STOp1{:?}\n", STOp1);
410 rap_trace!("STOp2{:?}\n", STOp2);
411 let vbm_1 =
412 ValueBranchMap::new(p1, &target_vec[0], &target_vec[1], SFOp1, STOp1);
413 let vbm_2 =
414 ValueBranchMap::new(p2, &target_vec[0], &target_vec[1], SFOp2, STOp2);
415 self.values_branchmap.insert(&p1, vbm_1);
416 self.values_branchmap.insert(&p2, vbm_2);
417 self.switchbbs.insert(switch_block, (*p1, *p2));
418 }
419 }
420 };
421 }
422 }
423
424 pub fn flipped_binop(op: BinOp) -> Option<BinOp> {
425 use BinOp::*;
426 Some(match op {
427 Eq => Eq,
428 Ne => Ne,
429 Lt => Ge,
430 Le => Gt,
431 Gt => Le,
432 Ge => Lt,
433 Add => Add,
434 Mul => Mul,
435 BitXor => BitXor,
436 BitAnd => BitAnd,
437 BitOr => BitOr,
438 _ => {
439 return None;
440 }
441 })
442 }
443
444 fn reverse_binop(op: BinOp) -> Option<BinOp> {
445 use BinOp::*;
446 Some(match op {
447 Eq => Eq,
448 Ne => Ne,
449 Lt => Gt,
450 Le => Ge,
451 Gt => Lt,
452 Ge => Le,
453 Add => Add,
454 Mul => Mul,
455 BitXor => BitXor,
456 BitAnd => BitAnd,
457 BitOr => BitOr,
458 _ => {
459 return None;
460 }
461 })
462 }
463
464 fn extract_condition(
465 &mut self,
466 place: &'tcx Place<'tcx>,
467 switch_block: &'tcx BasicBlockData<'tcx>,
468 ) -> Option<(&'tcx Operand<'tcx>, &'tcx Operand<'tcx>, BinOp)> {
469 for stmt in &switch_block.statements {
470 if let StatementKind::Assign(assign) = &stmt.kind {
471 let (lhs, rvalue) = &**assign;
472 if let Rvalue::BinaryOp(bin_op, pair) = rvalue {
473 let (op1, op2) = &**pair;
474 if lhs == place {
475 let return_op1: &Operand<'tcx> = &op1;
476 let return_op2: &Operand<'tcx> = &op2;
477
478 return Some((return_op1, return_op2, *bin_op));
479 }
480 }
481 }
482 }
483 None
484 }
485
486 fn apply_comparison<U: IntervalArithmetic>(
487 &self,
488 constant: U,
489 cmp_op: BinOp,
490 is_true_branch: bool,
491 const_in_left: bool,
492 ) -> Range<U> {
493 match cmp_op {
494 BinOp::Lt => {
495 if is_true_branch ^ const_in_left {
496 Range::new(U::min_value(), constant.sub(U::one()), RangeType::Unknown)
497 } else {
498 Range::new(constant, U::max_value(), RangeType::Unknown)
499 }
500 }
501
502 BinOp::Le => {
503 if is_true_branch ^ const_in_left {
504 Range::new(U::min_value(), constant, RangeType::Unknown)
505 } else {
506 Range::new(constant.add(U::one()), U::max_value(), RangeType::Unknown)
507 }
508 }
509
510 BinOp::Gt => {
511 if is_true_branch ^ const_in_left {
512 Range::new(U::min_value(), constant, RangeType::Unknown)
513 } else {
514 Range::new(constant.add(U::one()), U::max_value(), RangeType::Unknown)
515 }
516 }
517
518 BinOp::Ge => {
519 if is_true_branch ^ const_in_left {
520 Range::new(U::min_value(), constant, RangeType::Unknown)
521 } else {
522 Range::new(constant, U::max_value().sub(U::one()), RangeType::Unknown)
523 }
524 }
525
526 BinOp::Eq => {
527 if is_true_branch ^ const_in_left {
528 Range::new(U::min_value(), constant, RangeType::Unknown)
529 } else {
530 Range::new(constant, U::max_value(), RangeType::Unknown)
531 }
532 }
533
534 _ => Range::new(constant.clone(), constant.clone(), RangeType::Empty),
535 }
536 }
537
538 pub fn build_symbolic_intersect_map(&mut self) {
539 for i in 0..self.oprs.len() {
540 if let BasicOpKind::Essa(essaop) = &self.oprs[i] {
541 if let IntervalType::Symb(symbi) = essaop.get_intersect() {
542 let v = symbi.get_bound();
543 self.symbmap.entry(v).or_insert_with(HashSet::new).insert(i);
544 rap_trace!("symbmap insert {:?} {:?}\n", v, essaop);
545 }
546 }
547 }
548 }
549
550 pub fn build_use_map(
551 &mut self,
552 component: &HashSet<&'tcx Place<'tcx>>,
553 ) -> HashMap<&'tcx Place<'tcx>, HashSet<usize>> {
554 let mut comp_use_map = HashMap::new();
556 for &place in component {
557 if let Some(uses) = self.usemap.get(place) {
558 for op in uses.iter() {
559 let sink = self.oprs[*op].get_sink();
560 if component.contains(&sink) {
561 comp_use_map
562 .entry(place)
563 .or_insert_with(HashSet::new)
564 .insert(*op);
565 }
566 }
567 }
568 }
569
570 self.print_compusemap(component, &comp_use_map);
571 comp_use_map
572 }
573
574 pub fn build_terminator(&mut self, block: BasicBlock, terminator: &'tcx Terminator<'tcx>) {
575 match &terminator.kind {
576 TerminatorKind::Call {
577 func,
578 args,
579 destination,
580 target: _,
581 unwind: _,
582 fn_span: _,
583 call_source,
584 } => {
585 rap_trace!(
586 "TerminatorKind::Call in block {:?} with function {:?} destination {:?} args {:?}\n",
587 block,
588 func,
589 destination,
590 args
591 );
592 self.add_call_op(destination, args, terminator, func, block);
594 }
595 TerminatorKind::Return => {}
596 TerminatorKind::Goto { target } => {
597 rap_trace!(
598 "TerminatorKind::Goto in block {:?} targeting block {:?}\n",
599 block,
600 target
601 );
602 }
603 TerminatorKind::SwitchInt { discr, targets } => {
604 rap_trace!(
605 "TerminatorKind::SwitchInt in block {:?} with discr {:?} and targets {:?}\n",
606 block,
607 discr,
608 targets
609 );
610 }
611 _ => {
612 rap_trace!(
613 "Unsupported terminator kind in block {:?}: {:?}",
614 block,
615 terminator.kind
616 );
617 }
618 }
619 }
620
621 pub fn build_operations(
622 &mut self,
623 inst: &'tcx Statement<'tcx>,
624 block: BasicBlock,
625 body: &'tcx Body<'tcx>,
626 ) {
627 match &inst.kind {
628 StatementKind::Assign(assign) => {
629 let (sink, rvalue) = &**assign;
630 match rvalue {
631 Rvalue::BinaryOp(op, pair) => {
632 let (op1, op2) = &**pair;
633 match op {
634 BinOp::Add
635 | BinOp::Sub
636 | BinOp::Mul
637 | BinOp::Div
638 | BinOp::Rem
639 | BinOp::AddUnchecked => {
640 self.add_binary_op(sink, inst, rvalue, op1, op2, *op);
641 }
642 BinOp::AddWithOverflow => {
643 self.add_binary_op(sink, inst, rvalue, op1, op2, *op);
644 }
645 BinOp::SubUnchecked => {
646 self.add_binary_op(sink, inst, rvalue, op1, op2, *op);
647 }
648 BinOp::SubWithOverflow => {
649 self.add_binary_op(sink, inst, rvalue, op1, op2, *op);
650 }
651 BinOp::MulUnchecked => {
652 self.add_binary_op(sink, inst, rvalue, op1, op2, *op);
653 }
654 BinOp::MulWithOverflow => {
655 self.add_binary_op(sink, inst, rvalue, op1, op2, *op);
656 }
657
658 _ => {}
659 }
660 }
661 Rvalue::UnaryOp(unop, operand) => {
662 self.add_unary_op(sink, inst, rvalue, operand, *unop);
663 }
664 Rvalue::Aggregate(kind, operends) => match **kind {
665 AggregateKind::Adt(def_id, _, _, _, _) => match def_id {
666 _ if def_id == self.essa => {
667 self.add_essa_op(sink, inst, rvalue, operends, block)
668 }
669 _ if def_id == self.ssa => {
670 self.add_ssa_op(sink, inst, rvalue, operends)
671 }
672 _ => match self.unique_adt_handler(def_id) {
673 1 => {
674 self.add_aggregate_op(sink, inst, rvalue, operends, 1);
675 }
676 _ => {
677 rap_trace!(
678 "AggregateKind::Adt with def_id {:?} in statement {:?} is not handled specially.\n",
679 def_id,
680 inst
681 );
682 }
683 },
684 },
685 _ => {}
686 },
687 Rvalue::Use(operend, ..) => {
688 self.add_use_op(sink, inst, rvalue, operend);
689 }
690 Rvalue::Ref(_, borrowkind, place) => {
691 self.add_ref_op(sink, inst, rvalue, place, *borrowkind);
692 }
693 _ => {}
694 }
695 }
696 _ => {}
697 }
698 }
699
700 fn unique_adt_handler(&mut self, def_id: DefId) -> usize {
701 let adt_path = self.tcx.def_path_str(def_id);
702 rap_trace!("adt_path: {:?}\n", adt_path);
703 if self.unique_adt_path.contains_key(&adt_path) {
704 rap_trace!(
705 "unique_adt_handler for def_id: {:?} -> {}\n",
706 def_id,
707 adt_path
708 );
709 return *self.unique_adt_path.get(&adt_path).unwrap();
710 }
711 0
712 }
713 fn add_call_op(
716 &mut self,
717 sink: &'tcx Place<'tcx>,
718 args: &'tcx Box<[Spanned<Operand<'tcx>>]>,
719 terminator: &'tcx Terminator<'tcx>,
720 func: &'tcx Operand<'tcx>,
721 block: BasicBlock,
722 ) {
723 rap_trace!("add_call_op for sink: {:?} {:?}\n", sink, terminator);
724 let sink_node = self.add_varnode(&sink);
725
726 let mut path = String::new();
730 let mut func_def_id = None;
731 if let Operand::Constant(c_box) = func {
732 let const_operand = &**c_box;
733 let fn_ty = const_operand.ty();
734 if let ty::TyKind::FnDef(def_id, _substs) = fn_ty.kind() {
735 rap_debug!("fn_ty: {:?}\n", fn_ty);
737 if def_id.krate != LOCAL_CRATE {
738 path = self.tcx.def_path_str(*def_id);
739
740 rap_debug!("called external/no-MIR fn: {:?} -> {}", def_id, path);
741 }
742 func_def_id = Some(def_id);
743 }
744 }
745
746 if let Some(def_id) = func_def_id {
747 rap_trace!(
748 "TerminatorKind::Call in block {:?} with DefId {:?}\n",
749 block,
750 def_id
751 );
752 } else {
754 rap_trace!(
755 "TerminatorKind::Call in block {:?} is an indirect call (e.g., function pointer)\n",
756 block
757 );
758 }
761 let mut constant_count = 0 as usize;
762 let arg_count = args.len();
763 let mut arg_operands: Vec<Operand<'tcx>> = Vec::new();
764 let mut places = Vec::new();
765 for op in args.iter() {
766 match &op.node {
767 Operand::Copy(place) | Operand::Move(place) => {
768 arg_operands.push(op.node.clone());
769 places.push(place);
770 self.add_varnode(place);
771 self.usemap
772 .entry(place)
773 .or_default()
774 .insert(self.oprs.len());
775 }
776
777 Operand::Constant(_) => {
778 arg_operands.push(op.node.clone());
781 constant_count += 1;
782 }
783 #[cfg(rapx_ge_99)]
784 Operand::RuntimeChecks(_) => {}
785 }
786 }
787 {
788 let bi = BasicInterval::default();
789
790 let Some(def_id) = func_def_id else {
791 rap_debug!("Call to function without DefId, skipping\n");
792 return;
793 };
794 let call_op = CallOp::new(
795 IntervalType::Basic(bi),
796 &sink,
797 terminator,
798 arg_operands,
799 *def_id,
800 path,
801 places,
802 );
803 rap_debug!("call_op: {:?}\n", call_op);
804 let bop_index = self.oprs.len();
805
806 self.oprs.push(BasicOpKind::Call(call_op));
808
809 self.defmap.insert(&sink, bop_index);
811 if constant_count == arg_count {
812 rap_trace!("all args are constants\n");
813 self.const_func_place.insert(&sink, bop_index);
814 }
815 }
816 }
817
818 fn add_ssa_op(
819 &mut self,
820 sink: &'tcx Place<'tcx>,
821 inst: &'tcx Statement<'tcx>,
822 rvalue: &'tcx Rvalue<'tcx>,
823
824 operands: &'tcx IndexVec<FieldIdx, Operand<'tcx>>,
825 ) {
826 rap_trace!("ssa_op{:?}\n", inst);
827
828 let sink_node: &mut VarNode<'_, T> = self.def_add_varnode_sym(sink, rvalue);
829 rap_trace!("addsink_in_ssa_op{:?}\n", sink_node);
830
831 let BI: BasicInterval<T> = BasicInterval::default();
832 let mut phiop = PhiOp::new(IntervalType::Basic(BI), sink, inst);
833 let bop_index = self.oprs.len();
834 for i in 0..operands.len() {
835 let source = match &operands[FieldIdx::from_usize(i)] {
836 Operand::Copy(place) | Operand::Move(place) => {
837 self.use_add_varnode_sym(place, rvalue);
838 Some(place)
839 }
840 _ => None,
841 };
842 if let Some(source) = source {
843 self.use_add_varnode_sym(source, rvalue);
844 phiop.add_source(source);
845 rap_trace!("addvar_in_ssa_op{:?}\n", source);
846 self.usemap.entry(source).or_default().insert(bop_index);
847 }
848 }
849 self.oprs.push(BasicOpKind::Phi(phiop));
852
853 self.defmap.insert(sink, bop_index);
856 }
857
858 fn add_use_op(
859 &mut self,
860 sink: &'tcx Place<'tcx>,
861 inst: &'tcx Statement<'tcx>,
862 rvalue: &'tcx Rvalue<'tcx>,
863 op: &'tcx Operand<'tcx>,
864 ) {
865 rap_trace!("use_op{:?}\n", inst);
866
867 let BI: BasicInterval<T> = BasicInterval::default();
868 let source: Option<&'tcx Place<'tcx>> = None;
869
870 match op {
871 Operand::Copy(place) | Operand::Move(place) => {
872 if sink.local == RETURN_PLACE && sink.projection.is_empty() {
873 self.rerurn_places.insert(place);
874
875 let sink_node = self.def_add_varnode_sym(sink, rvalue);
876
877 rap_debug!("add_return_place{:?}\n", place);
878 } else {
879 self.use_add_varnode_sym(place, rvalue);
880 rap_trace!("addvar_in_use_op{:?}\n", place);
881 let sink_node = self.def_add_varnode_sym(sink, rvalue);
882 let useop = UseOp::new(IntervalType::Basic(BI), sink, inst, Some(place), None);
883 let bop_index = self.oprs.len();
885
886 self.oprs.push(BasicOpKind::Use(useop));
887 self.usemap.entry(place).or_default().insert(bop_index);
889
890 self.defmap.insert(sink, bop_index);
891 }
892 }
893 Operand::Constant(constant) => {
894 rap_trace!("add_constant_op{:?}\n", inst);
895 let Some(c) = op.constant() else {
896 rap_trace!("add_constant_op: constant is None\n");
897 return;
898 };
899 let useop = UseOp::new(IntervalType::Basic(BI), sink, inst, None, Some(c.const_));
900 let bop_index = self.oprs.len();
902
903 self.oprs.push(BasicOpKind::Use(useop));
904 self.defmap.insert(sink, bop_index);
907 let sink_node = self.def_add_varnode_sym(sink, rvalue);
908
909 if let Some(value) = T::from_const(&c.const_) {
910 sink_node.set_range(Range::new(
911 value.clone(),
912 value.clone(),
913 RangeType::Regular,
914 ));
915 rap_trace!("set_const {:?} value: {:?}\n", sink_node, value);
916 } else {
917 sink_node.set_range(Range::bottom());
918 };
919 }
920 #[cfg(rapx_ge_99)]
921 Operand::RuntimeChecks(_) => {}
922 }
923 }
924
925 fn add_essa_op(
926 &mut self,
927 sink: &'tcx Place<'tcx>,
928 inst: &'tcx Statement<'tcx>,
929 rvalue: &'tcx Rvalue<'tcx>,
930 operands: &'tcx IndexVec<FieldIdx, Operand<'tcx>>,
931 block: BasicBlock,
932 ) {
933 let sink_node = self.def_add_varnode_sym(sink, rvalue);
934
935
936 let loc_1: usize = 0;
937 let loc_2: usize = 1;
938 let source1 = match &operands[FieldIdx::from_usize(loc_1)] {
939 Operand::Copy(place) | Operand::Move(place) => {
940 self.use_add_varnode_sym(place, rvalue);
941 Some(place)
942 }
943 _ => None,
944 };
945 let op = &operands[FieldIdx::from_usize(loc_2)];
946 let bop_index = self.oprs.len();
947 let BI: IntervalType<'_, T>;
948 rap_trace!("essa_op operand1 {:?}\n", source1.unwrap());
949 if let Operand::Constant(c) = op {
950 let vbm = self.values_branchmap.get(source1.unwrap()).unwrap();
951 if block == *vbm.get_bb_true() {
952 rap_trace!("essa_op true branch{:?}\n", block);
953 BI = vbm.get_itv_t();
954 } else {
955 rap_trace!("essa_op false branch{:?}\n", block);
956 BI = vbm.get_itv_f();
957 }
958 self.usemap
959 .entry(source1.unwrap())
960 .or_default()
961 .insert(bop_index);
962
963 let essaop = EssaOp::new(BI, sink, inst, source1.unwrap(), false);
964 rap_trace!(
965 "addvar_in_essa_op {:?} from const {:?}\n",
966 essaop,
967 source1.unwrap()
968 );
969
970 self.oprs.push(BasicOpKind::Essa(essaop));
973 self.defmap.insert(sink, bop_index);
976 } else {
977 let vbm = self.values_branchmap.get(source1.unwrap()).unwrap();
978 if block == *vbm.get_bb_true() {
979 rap_trace!("essa_op true branch{:?}\n", block);
980 BI = vbm.get_itv_t();
981 } else {
982 rap_trace!("essa_op false branch{:?}\n", block);
983 BI = vbm.get_itv_f();
984 }
985 let source2 = match op {
986 Operand::Copy(place) | Operand::Move(place) => {
987 self.use_add_varnode_sym(place, rvalue);
988 Some(place)
989 }
990 _ => None,
991 };
992 self.usemap
993 .entry(source1.unwrap())
994 .or_default()
995 .insert(bop_index);
996 let essaop = EssaOp::new(BI, sink, inst, source1.unwrap(), true);
997 rap_trace!(
999 "addvar_in_essa_op {:?} from {:?}\n",
1000 essaop,
1001 source1.unwrap()
1002 );
1003
1004 self.oprs.push(BasicOpKind::Essa(essaop));
1005
1006 self.defmap.insert(sink, bop_index);
1007 }
1008 }
1009
1010 pub fn add_aggregate_op(
1011 &mut self,
1012 sink: &'tcx Place<'tcx>,
1013 inst: &'tcx Statement<'tcx>,
1014 rvalue: &'tcx Rvalue<'tcx>,
1015 operands: &'tcx IndexVec<FieldIdx, Operand<'tcx>>,
1016 unique_adt: usize,
1017 ) {
1018 rap_trace!("aggregate_op {:?}\n", inst);
1019
1020 let BI: BasicInterval<T> = BasicInterval::default();
1021 let mut agg_operands: Vec<AggregateOperand<'tcx>> = Vec::with_capacity(operands.len());
1022
1023 for operand in operands {
1024 match operand {
1025 Operand::Copy(place) | Operand::Move(place) => {
1026 if sink.local == RETURN_PLACE && sink.projection.is_empty() {
1027 self.rerurn_places.insert(place);
1028 self.def_add_varnode_sym(sink, rvalue);
1029 rap_debug!("add_return_place {:?}\n", place);
1030 } else {
1031 self.use_add_varnode_sym(place, rvalue);
1032 rap_trace!("addvar_in_aggregate_op {:?}\n", place);
1033 agg_operands.push(AggregateOperand::Place(place));
1034 }
1035 }
1036 Operand::Constant(c) => {
1037 rap_trace!("add_constant_aggregate_op {:?}\n", c);
1038 agg_operands.push(AggregateOperand::Const(c.const_));
1039
1040 let sink_node = self.def_add_varnode_sym(sink, rvalue);
1041 if let Some(value) = T::from_const(&c.const_) {
1042 sink_node.set_range(Range::new(
1043 value.clone(),
1044 value.clone(),
1045 RangeType::Regular,
1046 ));
1047 rap_trace!("set_const {:?} value: {:?}\n", sink_node, value);
1048 } else {
1049 sink_node.set_range(Range::bottom());
1050 }
1051 }
1052 #[cfg(rapx_ge_99)]
1053 Operand::RuntimeChecks(_) => {}
1054 }
1055 }
1056
1057 if agg_operands.is_empty() {
1058 rap_trace!("aggregate_op has no operands, skipping\n");
1059 return;
1060 }
1061
1062 let agg_op = AggregateOp::new(
1063 IntervalType::Basic(BI),
1064 sink,
1065 inst,
1066 agg_operands,
1067 unique_adt,
1068 );
1069 let bop_index = self.oprs.len();
1070 self.oprs.push(BasicOpKind::Aggregate(agg_op));
1071
1072 for operand in operands {
1073 if let Operand::Copy(place) | Operand::Move(place) = operand {
1074 self.usemap.entry(place).or_default().insert(bop_index);
1075 }
1076 }
1077
1078 self.defmap.insert(sink, bop_index);
1079
1080 self.def_add_varnode_sym(sink, rvalue);
1081 }
1082
1083 fn add_unary_op(
1084 &mut self,
1085 sink: &'tcx Place<'tcx>,
1086 inst: &'tcx Statement<'tcx>,
1087 rvalue: &'tcx Rvalue<'tcx>,
1088 operand: &'tcx Operand<'tcx>,
1089 op: UnOp,
1090 ) {
1091 rap_trace!("unary_op{:?}\n", inst);
1092
1093 let sink_node = self.def_add_varnode_sym(sink, rvalue);
1094 rap_trace!("addsink_in_unary_op{:?}\n", sink_node);
1095
1096 let BI: BasicInterval<T> = BasicInterval::default();
1097 let loc_1: usize = 0;
1098
1099 let source = match operand {
1100 Operand::Copy(place) | Operand::Move(place) => {
1101 self.add_varnode(place);
1102 Some(place)
1103 }
1104 _ => None,
1105 };
1106
1107 rap_trace!("addvar_in_unary_op{:?}\n", source.unwrap());
1108 self.use_add_varnode_sym(&source.unwrap(), rvalue);
1109
1110 let unaryop = UnaryOp::new(IntervalType::Basic(BI), sink, inst, source.unwrap(), op);
1111 let bop_index = self.oprs.len();
1113
1114 self.oprs.push(BasicOpKind::Unary(unaryop));
1115 self.defmap.insert(sink, bop_index);
1118 }
1119
1120 fn add_binary_op(
1121 &mut self,
1122 sink: &'tcx Place<'tcx>,
1123 inst: &'tcx Statement<'tcx>,
1124 rvalue: &'tcx Rvalue<'tcx>,
1125 op1: &'tcx Operand<'tcx>,
1126 op2: &'tcx Operand<'tcx>,
1127 bin_op: BinOp,
1128 ) {
1129 rap_trace!("binary_op{:?}\n", inst);
1130
1131 let sink_node = self.def_add_varnode_sym(sink, rvalue);
1133 rap_trace!("addsink_in_binary_op{:?}\n", sink_node);
1134
1135 let bop_index = self.oprs.len();
1136 let bi: BasicInterval<T> = BasicInterval::default();
1137
1138 let (source1_place, source2_place, const_val) = match (op1, op2) {
1141 (Operand::Copy(p1) | Operand::Move(p1), Operand::Copy(p2) | Operand::Move(p2)) => {
1143 self.use_add_varnode_sym(p1, rvalue);
1144 self.use_add_varnode_sym(p2, rvalue);
1145 rap_trace!("addvar_in_binary_op p1:{:?}, p2:{:?}\n", p1, p2);
1146
1147 (Some(p1), Some(p2), None)
1148 }
1149
1150 (Operand::Copy(p1) | Operand::Move(p1), Operand::Constant(c2)) => {
1152 self.use_add_varnode_sym(p1, rvalue);
1153 rap_trace!("addvar_in_binary_op p1:{:?}\n", p1);
1154
1155 (Some(p1), None, Some(c2.const_))
1156 }
1157
1158 (Operand::Constant(c1), Operand::Copy(p2) | Operand::Move(p2)) => {
1163 self.use_add_varnode_sym(p2, rvalue);
1164 rap_trace!("addvar_in_binary_op p2(as source1):{:?}\n", p2);
1165
1166 (Some(p2), None, Some(c1.const_))
1168 }
1169
1170 (Operand::Constant(c1), Operand::Constant(_)) => {
1172 (None, None, Some(c1.const_))
1175 }
1176 #[cfg(rapx_ge_99)]
1177 _ => (None, None, None),
1178 };
1179
1180 let bop = BinaryOp::new(
1182 IntervalType::Basic(bi),
1183 sink,
1184 inst,
1185 source1_place, source2_place,
1187 const_val,
1188 bin_op.clone(),
1189 );
1190
1191 self.oprs.push(BasicOpKind::Binary(bop));
1192
1193 self.defmap.insert(sink, bop_index);
1195
1196 if let Some(place) = source1_place {
1198 self.usemap.entry(place).or_default().insert(bop_index);
1199 }
1200
1201 if let Some(place) = source2_place {
1202 self.usemap.entry(place).or_default().insert(bop_index);
1203 }
1204 }
1205
1206 fn add_ref_op(
1207 &mut self,
1208 sink: &'tcx Place<'tcx>,
1209 inst: &'tcx Statement<'tcx>,
1210 rvalue: &'tcx Rvalue<'tcx>,
1211 place: &'tcx Place<'tcx>,
1212 borrowkind: BorrowKind,
1213 ) {
1214 rap_trace!("ref_op {:?}\n", inst);
1215
1216 let BI: BasicInterval<T> = BasicInterval::default();
1217
1218 let source_node = self.use_add_varnode_sym(place, rvalue);
1219
1220 let sink_node = self.def_add_varnode_sym(sink, rvalue);
1221
1222 let refop = RefOp::new(IntervalType::Basic(BI), sink, inst, place, borrowkind);
1223 let bop_index = self.oprs.len();
1224 self.oprs.push(BasicOpKind::Ref(refop));
1225
1226 self.usemap.entry(place).or_default().insert(bop_index);
1227
1228 self.defmap.insert(sink, bop_index);
1229
1230 rap_trace!(
1231 "add_ref_op: created RefOp from {:?} to {:?} at {:?}\n",
1232 place,
1233 sink,
1234 inst
1235 );
1236 }
1237}