Skip to main content

weir/
escapes.rs

1//! Escape analysis: does a value escape the function scope?
2//!
3//! Composes the call graph with the points-to set: a value `v`
4//! escapes iff `pts(v)` intersects the set of nodes reachable from
5//! a function's return / global / heap-store sites. Exposed as a
6//! per-node bitset for dataflow consumers to read.
7//!
8//! Soundness: [`MayOver`](super::soundness::Soundness::MayOver)  -  the
9//! points-to is sound (over-approximates), so the escape set is sound.
10
11use vyre::ir::Program;
12use vyre_primitives::bitset::and::bitset_and;
13use vyre_primitives::graph::csr_forward_traverse::bitset_words;
14
15pub(crate) const OP_ID: &str = "weir::escapes";
16
17/// Build an escape-query Program. Inputs:
18/// - `pts_buf`: per-variable points-to bitset (host-supplied).
19/// - `escape_set_buf`: per-node bitset of escape sites (return, global, heap-store).
20/// - `out`: per-variable bitset, bit set iff that variable's
21///   points-to set overlaps the escape set.
22#[must_use]
23pub fn escapes(node_count: u32, pts_buf: &str, escape_set_buf: &str, out: &str) -> Program {
24    let words = bitset_words(node_count);
25    vyre_harness::region::tag_program(OP_ID, bitset_and(pts_buf, escape_set_buf, out, words))
26}
27
28/// reference oracle.
29#[must_use]
30#[cfg(any(test, feature = "cpu-parity"))]
31#[deprecated(
32    note = "reference oracle only; production code must dispatch the Weir Program on a concrete GPU backend or use weir::oracle for parity evidence"
33)]
34pub(crate) fn cpu_ref(pts: &[u32], escape_set: &[u32]) -> Vec<u32> {
35    vyre_primitives::bitset::and::cpu_ref(pts, escape_set)
36}
37
38/// Marker type for the escape primitive.
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct Escapes;
41
42impl super::soundness::SoundnessTagged for Escapes {
43    fn soundness(&self) -> super::soundness::Soundness {
44        super::soundness::Soundness::MayOver
45    }
46}
47
48inventory::submit! {
49    vyre_harness::OpEntry::new(
50        OP_ID,
51        || escapes(4, "pts", "escape", "out"),
52        Some(|| {
53            let u32s = crate::dispatch_decode::pack_u32;
54            vec![vec![u32s(&[0b1100]), u32s(&[0b1010]), u32s(&[0])]]
55        }),
56        Some(|| {
57            let u32s = crate::dispatch_decode::pack_u32;
58            vec![vec![u32s(&[0b1000])]]
59        }),
60    )
61}
62
63#[cfg(test)]
64#[allow(deprecated)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn empty_pts_means_no_escape() {
70        assert_eq!(cpu_ref(&[0], &[0xFFFF_FFFF]), vec![0]);
71    }
72
73    #[test]
74    fn full_pts_in_escape_set_escapes() {
75        assert_eq!(cpu_ref(&[0xFFFF], &[0xFFFF]), vec![0xFFFF]);
76    }
77
78    #[test]
79    fn disjoint_means_no_escape() {
80        assert_eq!(cpu_ref(&[0xFF00], &[0x00FF]), vec![0]);
81    }
82
83    #[test]
84    fn partial_overlap_escapes_partially() {
85        assert_eq!(cpu_ref(&[0x0FF0], &[0x00FF]), vec![0x00F0]);
86    }
87}