-
Notifications
You must be signed in to change notification settings - Fork 14
Analyze a Simple Rust Program
We use a simple Rust program shape.rs to explain the points-to analysis results of RUPTA:
trait Shape {
fn area(&self) {}
}
struct Circle<'a> { r: &'a f32 }
struct Square<'a> { s: &'a f32 }
impl<'a> Shape for Circle<'a> {
fn area(&self) { let _ = self.r * self.r * 3.14; }
}
impl<'a> Shape for Square<'a> {
fn area(&self) { let _ = self.s * self.s; }
}
fn shape_area_static<T: Shape>(s: &T) {
s.area();
}
fn shape_area_dynamic(s: &dyn Shape) {
s.area();
}
fn main() {
let x = 3.0;
let y = 2.0;
let c = Circle { r: &x };
let r = Square { s: &y };
shape_area_static(&c);
shape_area_static(&r);
let s: &dyn Shape = &c;
shape_area_dynamic(s);
}Use pta to analyze a single Rust file. By default, 1-callsite-sensitive pointer analysis is performed.
pta shape.rs
pta shape.rs --dump-mir mir.txt
RUPTA plugs into the nightly Rust compiler rustc. The output MIR may vary depending on the version of rustc. The MIR below is based on rustc 1.77.0-nightly (2024-02-01).
Generic types persist in Rust MIR, for example, T in the function shape_area_static. RUPTA monomorphizes generic code during analysis, treating a generic function with different specialized types as distinct functions and analyzing them separately. As shown below, shape_area_static is analyzed twice in this example, corresponding to FuncId(1) and FuncId(2).
[FuncId(0) - "shape::main"]
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn main() -> () {
let mut _0: ();
let _1: f32;
let _4: &f32;
let _6: &f32;
let _7: ();
let _8: &Circle<'_>;
let _9: ();
let _10: &Square<'_>;
let _12: &Circle<'_>;
let _13: ();
scope 1 {
debug x => const 3f32;
let _2: f32;
scope 2 {
debug y => const 2f32;
let _3: Circle<'_>;
scope 3 {
debug c => _3;
let _5: Square<'_>;
scope 4 {
debug r => _5;
let _11: &dyn Shape;
scope 5 {
debug s => _11;
}
}
}
}
}
bb0: {
_1 = const 3f32;
_2 = const 2f32;
_4 = &_1;
_3 = Circle::<'_> { r: _4 };
_6 = &_2;
_5 = Square::<'_> { s: _6 };
_8 = &_3;
_7 = shape_area_static::<Circle<'_>>(_8) -> [return: bb1, unwind continue];
}
bb1: {
_10 = &_5;
_9 = shape_area_static::<Square<'_>>(_10) -> [return: bb2, unwind continue];
}
bb2: {
_12 = &_3;
_11 = _12 as &dyn Shape (PointerCoercion(Unsize));
_13 = shape_area_dynamic(_11) -> [return: bb3, unwind continue];
}
bb3: {
return;
}
}
[FuncId(1) - "shape::shape_area_static<Circle<ReErased>>"]
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn shape_area_static(_1: &T) -> () {
debug s => _1;
let mut _0: ();
let _2: ();
bb0: {
_2 = <T as Shape>::area(_1) -> [return: bb1, unwind continue];
}
bb1: {
return;
}
}
[FuncId(2) - "shape::shape_area_static<Square<ReErased>>"]
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn shape_area_static(_1: &T) -> () {
debug s => _1;
let mut _0: ();
let _2: ();
bb0: {
_2 = <T as Shape>::area(_1) -> [return: bb1, unwind continue];
}
bb1: {
return;
}
}
[FuncId(3) - "shape::shape_area_dynamic"]
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn shape_area_dynamic(_1: &dyn Shape) -> () {
debug s => _1;
let mut _0: ();
let _2: ();
bb0: {
_2 = <dyn Shape as Shape>::area(_1) -> [return: bb1, unwind continue];
}
bb1: {
return;
}
}
[FuncId(4) - "shape::{impl#0}::area<>"]
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn <impl at shape.rs:8:1: 8:30>::area(_1: &Circle<'_>) -> () {
debug self => _1;
let mut _0: ();
let mut _2: f32;
let mut _3: &f32;
let mut _4: &f32;
scope 1 {
}
bb0: {
_3 = ((*_1).0: &f32);
_4 = ((*_1).0: &f32);
_2 = <&f32 as std::ops::Mul>::mul(move _3, move _4) -> [return: bb1, unwind continue];
}
bb1: {
return;
}
}
[FuncId(5) - "shape::{impl#1}::area<>"]
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn <impl at shape.rs:12:1: 12:30>::area(_1: &Square<'_>) -> () {
debug self => _1;
let mut _0: ();
let mut _2: f32;
let mut _3: &f32;
let mut _4: &f32;
scope 1 {
}
bb0: {
_3 = ((*_1).0: &f32);
_4 = ((*_1).0: &f32);
_2 = <&f32 as std::ops::Mul>::mul(move _3, move _4) -> [return: bb1, unwind continue];
}
bb1: {
return;
}
}
[FuncId(6) - "core::ops::arith::{impl#164}::mul<>"]
// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn <&f32 as std::ops::Mul<&f32>>::mul(_1: &f32, _2: &f32) -> f32 {
debug self => _1;
debug other => _2;
let mut _0: f32;
let mut _3: f32;
let mut _4: f32;
scope 1 (inlined #[track_caller] <f32 as std::ops::Mul>::mul) {
debug self => _3;
debug other => _4;
}
bb0: {
StorageLive(_3);
_3 = (*_1);
StorageLive(_4);
_4 = (*_2);
_0 = Mul(_3, _4);
StorageDead(_4);
StorageDead(_3);
return;
}
}pta shape.rs --dump-call-graph call_graph.dot
The content of call_graph.dot is given below:
digraph {
0 [ label = "shape::main" ]
1 [ label = "shape::shape_area_dynamic" ]
2 [ label = "shape::{impl#0}::area<>" ]
3 [ label = "core::ops::arith::{impl#164}::mul<>" ]
4 [ label = "shape::shape_area_static<Square<ReErased>>" ]
5 [ label = "shape::{impl#1}::area<>" ]
6 [ label = "shape::shape_area_static<Circle<ReErased>>" ]
0 -> 1 [ label = "bb2[2]" ]
2 -> 3 [ label = "bb0[2]" ]
1 -> 2 [ label = "bb0[0]" ]
4 -> 5 [ label = "bb0[0]" ]
6 -> 2 [ label = "bb0[0]" ]
0 -> 6 [ label = "bb0[7]" ]
0 -> 4 [ label = "bb1[1]" ]
5 -> 3 [ label = "bb0[2]" ]
}
The callgraph looks like this:

pta shape.rs -dump-pts pts.txt
The content of pts.txt is given below:
FuncId(0) - "shape::main"
FuncId(0)::local_6 (1) ==> { FuncId(0)::local_2 }
FuncId(0)::local_10 (1) ==> { FuncId(0)::local_5 }
FuncId(0)::local_4 (1) ==> { FuncId(0)::local_1 }
FuncId(0)::local_3.0 (1) ==> { FuncId(0)::local_1 }
FuncId(0)::local_5.0 (1) ==> { FuncId(0)::local_2 }
FuncId(0)::local_11 (1) ==> { FuncId(0)::local_3 }
FuncId(0)::local_8 (1) ==> { FuncId(0)::local_3 }
FuncId(0)::local_12 (1) ==> { FuncId(0)::local_3 }
FuncId(1) - "shape::shape_area_static<Circle<ReErased>>"
FuncId(1)::param_1 (1) ==> { FuncId(0)::local_3 }
FuncId(2) - "shape::shape_area_static<Square<ReErased>>"
FuncId(2)::param_1 (1) ==> { FuncId(0)::local_5 }
FuncId(3) - "shape::shape_area_dynamic"
FuncId(3)::param_1 (1) ==> { FuncId(0)::local_3 }
FuncId(4) - "shape::{impl#0}::area<>"
FuncId(4)::param_1 (1) ==> { FuncId(0)::local_3 }
FuncId(4)::local_4 (1) ==> { FuncId(0)::local_1 }
FuncId(4)::local_3 (1) ==> { FuncId(0)::local_1 }
FuncId(5) - "shape::{impl#1}::area<>"
FuncId(5)::local_4 (1) ==> { FuncId(0)::local_2 }
FuncId(5)::local_3 (1) ==> { FuncId(0)::local_2 }
FuncId(5)::param_1 (1) ==> { FuncId(0)::local_5 }
FuncId(6) - "core::ops::arith::{impl#164}::mul<>"
FuncId(6)::param_1 (2) ==> { FuncId(0)::local_2 FuncId(0)::local_1 }
FuncId(6)::param_2 (2) ==> { FuncId(0)::local_2 FuncId(0)::local_1 }