Skip to content

Commit 1701b19

Browse files
committed
Simplification to residue trait
1 parent 14ceece commit 1701b19

File tree

7 files changed

+117
-82
lines changed

7 files changed

+117
-82
lines changed

jingle/src/analysis/compound/reducer.rs

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,21 @@ pub struct CompoundReducer<'op, A: ConfigurableProgramAnalysis, B: ConfigurableP
99
b: B::Reducer<'op>,
1010
}
1111

12-
impl<'op, A: ConfigurableProgramAnalysis, B: ConfigurableProgramAnalysis>
13-
Residue<'op, CompoundState2<A::State, B::State>> for CompoundReducer<'op, A, B>
12+
impl<'r, A: ConfigurableProgramAnalysis, B: ConfigurableProgramAnalysis>
13+
Residue<CompoundState2<A::State, B::State>> for CompoundReducer<'r, A, B>
14+
where
15+
A::Reducer<'r>: Residue<A::State>,
16+
B::Reducer<'r>: Residue<B::State>,
1417
{
1518
type Output = (
16-
<A::Reducer<'op> as Residue<'op, A::State>>::Output,
17-
<B::Reducer<'op> as Residue<'op, B::State>>::Output,
19+
<A::Reducer<'r> as Residue<A::State>>::Output,
20+
<B::Reducer<'r> as Residue<B::State>>::Output,
1821
);
1922

2023
fn new() -> Self {
2124
Self {
22-
a: <A::Reducer<'op> as Residue<'op, A::State>>::new(),
23-
b: <B::Reducer<'op> as Residue<'op, B::State>>::new(),
25+
a: <A::Reducer<'r> as Residue<A::State>>::new(),
26+
b: <B::Reducer<'r> as Residue<B::State>>::new(),
2427
}
2528
}
2629

@@ -29,7 +32,9 @@ impl<'op, A: ConfigurableProgramAnalysis, B: ConfigurableProgramAnalysis>
2932
(a.finalize(), b.finalize())
3033
}
3134

32-
fn merged_state(
35+
// Method-level op lifetime is `'op`; it is late-bound by the trait and
36+
// is independent from the impl's `'r` lifetime parameter.
37+
fn merged_state<'op>(
3338
&mut self,
3439
curr_state: &CompoundState2<A::State, B::State>,
3540
dest_state: &CompoundState2<A::State, B::State>,
@@ -42,7 +47,7 @@ impl<'op, A: ConfigurableProgramAnalysis, B: ConfigurableProgramAnalysis>
4247
.merged_state(&curr_state.s2, &dest_state.s2, &merged_state.s2, op);
4348
}
4449

45-
fn new_state(
50+
fn new_state<'op>(
4651
&mut self,
4752
state: &CompoundState2<A::State, B::State>,
4853
dest_state: &CompoundState2<A::State, B::State>,
@@ -59,18 +64,21 @@ pub struct CompoundReducer2<'op, A: ConfigurableProgramAnalysis, B: Configurable
5964
b: B::Reducer<'op>,
6065
}
6166

62-
impl<'op, A: ConfigurableProgramAnalysis, B: ConfigurableProgramAnalysis>
63-
Residue<'op, CompoundState2<A::State, B::State>> for CompoundReducer2<'op, A, B>
67+
impl<'r, A: ConfigurableProgramAnalysis, B: ConfigurableProgramAnalysis>
68+
Residue<CompoundState2<A::State, B::State>> for CompoundReducer2<'r, A, B>
69+
where
70+
A::Reducer<'r>: Residue<A::State>,
71+
B::Reducer<'r>: Residue<B::State>,
6472
{
6573
type Output = (
66-
<A::Reducer<'op> as Residue<'op, A::State>>::Output,
67-
<B::Reducer<'op> as Residue<'op, B::State>>::Output,
74+
<A::Reducer<'r> as Residue<A::State>>::Output,
75+
<B::Reducer<'r> as Residue<B::State>>::Output,
6876
);
6977

7078
fn new() -> Self {
7179
Self {
72-
a: <A::Reducer<'op> as Residue<'op, A::State>>::new(),
73-
b: <B::Reducer<'op> as Residue<'op, B::State>>::new(),
80+
a: <A::Reducer<'r> as Residue<A::State>>::new(),
81+
b: <B::Reducer<'r> as Residue<B::State>>::new(),
7482
}
7583
}
7684

@@ -79,7 +87,7 @@ impl<'op, A: ConfigurableProgramAnalysis, B: ConfigurableProgramAnalysis>
7987
(a.finalize(), b.finalize())
8088
}
8189

82-
fn merged_state(
90+
fn merged_state<'op>(
8391
&mut self,
8492
curr_state: &CompoundState2<A::State, B::State>,
8593
dest_state: &CompoundState2<A::State, B::State>,
@@ -92,7 +100,7 @@ impl<'op, A: ConfigurableProgramAnalysis, B: ConfigurableProgramAnalysis>
92100
.merged_state(&curr_state.s2, &dest_state.s2, &merged_state.s2, op);
93101
}
94102

95-
fn new_state(
103+
fn new_state<'op>(
96104
&mut self,
97105
state: &CompoundState2<A::State, B::State>,
98106
dest_state: &CompoundState2<A::State, B::State>,
@@ -116,23 +124,27 @@ pub struct CompoundReducer3<
116124
}
117125

118126
impl<
119-
'op,
127+
'r,
120128
A: ConfigurableProgramAnalysis,
121129
B: ConfigurableProgramAnalysis,
122130
C: ConfigurableProgramAnalysis,
123-
> Residue<'op, CompoundState3<A::State, B::State, C::State>> for CompoundReducer3<'op, A, B, C>
131+
> Residue<CompoundState3<A::State, B::State, C::State>> for CompoundReducer3<'r, A, B, C>
132+
where
133+
A::Reducer<'r>: Residue<A::State>,
134+
B::Reducer<'r>: Residue<B::State>,
135+
C::Reducer<'r>: Residue<C::State>,
124136
{
125137
type Output = (
126-
<A::Reducer<'op> as Residue<'op, A::State>>::Output,
127-
<B::Reducer<'op> as Residue<'op, B::State>>::Output,
128-
<C::Reducer<'op> as Residue<'op, C::State>>::Output,
138+
<A::Reducer<'r> as Residue<A::State>>::Output,
139+
<B::Reducer<'r> as Residue<B::State>>::Output,
140+
<C::Reducer<'r> as Residue<C::State>>::Output,
129141
);
130142

131143
fn new() -> Self {
132144
Self {
133-
a: <A::Reducer<'op> as Residue<'op, A::State>>::new(),
134-
b: <B::Reducer<'op> as Residue<'op, B::State>>::new(),
135-
c: <C::Reducer<'op> as Residue<'op, C::State>>::new(),
145+
a: <A::Reducer<'r> as Residue<A::State>>::new(),
146+
b: <B::Reducer<'r> as Residue<B::State>>::new(),
147+
c: <C::Reducer<'r> as Residue<C::State>>::new(),
136148
}
137149
}
138150

@@ -141,7 +153,7 @@ impl<
141153
(a.finalize(), b.finalize(), c.finalize())
142154
}
143155

144-
fn merged_state(
156+
fn merged_state<'op>(
145157
&mut self,
146158
curr_state: &CompoundState3<A::State, B::State, C::State>,
147159
dest_state: &CompoundState3<A::State, B::State, C::State>,
@@ -156,7 +168,7 @@ impl<
156168
.merged_state(&curr_state.s3, &dest_state.s3, &merged_state.s3, op);
157169
}
158170

159-
fn new_state(
171+
fn new_state<'op>(
160172
&mut self,
161173
state: &CompoundState3<A::State, B::State, C::State>,
162174
dest_state: &CompoundState3<A::State, B::State, C::State>,
@@ -183,27 +195,32 @@ pub struct CompoundReducer4<
183195
}
184196

185197
impl<
186-
'op,
198+
'r,
187199
A: ConfigurableProgramAnalysis,
188200
B: ConfigurableProgramAnalysis,
189201
C: ConfigurableProgramAnalysis,
190202
D: ConfigurableProgramAnalysis,
191-
> Residue<'op, CompoundState4<A::State, B::State, C::State, D::State>>
192-
for CompoundReducer4<'op, A, B, C, D>
203+
> Residue<CompoundState4<A::State, B::State, C::State, D::State>>
204+
for CompoundReducer4<'r, A, B, C, D>
205+
where
206+
A::Reducer<'r>: Residue<A::State>,
207+
B::Reducer<'r>: Residue<B::State>,
208+
C::Reducer<'r>: Residue<C::State>,
209+
D::Reducer<'r>: Residue<D::State>,
193210
{
194211
type Output = (
195-
<A::Reducer<'op> as Residue<'op, A::State>>::Output,
196-
<B::Reducer<'op> as Residue<'op, B::State>>::Output,
197-
<C::Reducer<'op> as Residue<'op, C::State>>::Output,
198-
<D::Reducer<'op> as Residue<'op, D::State>>::Output,
212+
<A::Reducer<'r> as Residue<A::State>>::Output,
213+
<B::Reducer<'r> as Residue<B::State>>::Output,
214+
<C::Reducer<'r> as Residue<C::State>>::Output,
215+
<D::Reducer<'r> as Residue<D::State>>::Output,
199216
);
200217

201218
fn new() -> Self {
202219
Self {
203-
a: <A::Reducer<'op> as Residue<'op, A::State>>::new(),
204-
b: <B::Reducer<'op> as Residue<'op, B::State>>::new(),
205-
c: <C::Reducer<'op> as Residue<'op, C::State>>::new(),
206-
d: <D::Reducer<'op> as Residue<'op, D::State>>::new(),
220+
a: <A::Reducer<'r> as Residue<A::State>>::new(),
221+
b: <B::Reducer<'r> as Residue<B::State>>::new(),
222+
c: <C::Reducer<'r> as Residue<C::State>>::new(),
223+
d: <D::Reducer<'r> as Residue<D::State>>::new(),
207224
}
208225
}
209226

@@ -212,7 +229,7 @@ impl<
212229
(a.finalize(), b.finalize(), c.finalize(), d.finalize())
213230
}
214231

215-
fn merged_state(
232+
fn merged_state<'op>(
216233
&mut self,
217234
curr_state: &CompoundState4<A::State, B::State, C::State, D::State>,
218235
dest_state: &CompoundState4<A::State, B::State, C::State, D::State>,
@@ -229,7 +246,7 @@ impl<
229246
.merged_state(&curr_state.s4, &dest_state.s4, &merged_state.s4, op);
230247
}
231248

232-
fn new_state(
249+
fn new_state<'op>(
233250
&mut self,
234251
state: &CompoundState4<A::State, B::State, C::State, D::State>,
235252
dest_state: &CompoundState4<A::State, B::State, C::State, D::State>,

jingle/src/analysis/cpa/final_reducer.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ where
6666
}
6767
}
6868

69-
impl<'a, S> Residue<'a, S> for FinalReducer<S>
69+
impl<S> Residue<S> for FinalReducer<S>
7070
where
7171
S: AbstractState,
7272
{
@@ -76,11 +76,11 @@ where
7676
///
7777
/// The source `state` has at least one successor, so it is not final.
7878
/// The `dest_state` is recorded as a potential final state (to be verified later).
79-
fn new_state(
79+
fn new_state<'op>(
8080
&mut self,
8181
state: &S,
8282
dest_state: &S,
83-
_op: &Option<crate::analysis::pcode_store::PcodeOpRef<'a>>,
83+
_op: &Option<crate::analysis::pcode_store::PcodeOpRef<'op>>,
8484
) {
8585
// The source state has successors, so it's not final
8686
if !self.non_final_states.iter().any(|s| s == state) {
@@ -98,12 +98,12 @@ where
9898
/// When states are merged, the current state is also a non-final state since
9999
/// it produced successors. We update references to the original destination
100100
/// state to point to the merged state.
101-
fn merged_state(
101+
fn merged_state<'op>(
102102
&mut self,
103103
curr_state: &S,
104104
original_merged_state: &S,
105105
merged_state: &S,
106-
_op: &Option<crate::analysis::pcode_store::PcodeOpRef<'a>>,
106+
_op: &Option<crate::analysis::pcode_store::PcodeOpRef<'op>>,
107107
) {
108108
// The current state has successors, so it's not final
109109
if !self.non_final_states.iter().any(|s| s == curr_state) {

jingle/src/analysis/cpa/mod.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ pub trait ConfigurableProgramAnalysis: Sized {
3737
type State: AbstractState + Debug;
3838

3939
// Reducer is a generic associated type parameterized by an op lifetime `'op`.
40-
// This allows reducers to be expressed that accept/store `PcodeOpRef<'op>`
41-
// without requiring clones.
42-
type Reducer<'op>: residue::Residue<'op, Self::State>;
40+
// Reducer types are expected to implement `Residue<S>` (the op lifetime is
41+
// carried by the methods). This lets reducer implementations accept
42+
// `PcodeOpRef<'op>` in method calls without putting the lifetime on the trait.
43+
type Reducer<'op>: residue::Residue<Self::State>;
4344
}
4445

4546
/**
@@ -63,13 +64,13 @@ where
6364
&self,
6465
initial: I,
6566
pcode_store: &'op P,
66-
) -> <<Self as ConfigurableProgramAnalysis>::Reducer<'op> as residue::Residue<'op, Self::State>>::Output
67-
where
68-
Self::State: 'op,
67+
) -> <<Self as ConfigurableProgramAnalysis>::Reducer<'op> as residue::Residue<Self::State>>::Output
6968
{
7069
let initial = initial.borrow();
7170
// Construct the reducer specialized for the `'op` lifetime.
72-
let mut reducer = <Self::Reducer<'op> as residue::Residue<'op, Self::State>>::new();
71+
// The Reducer GAT yields a type whose `new()` constructs the reducer
72+
// value; the `Residue` trait itself accepts op lifetimes on its methods.
73+
let mut reducer = <Self::Reducer<'op> as residue::Residue<Self::State>>::new();
7374

7475
let mut waitlist: VecDeque<Self::State> = VecDeque::new();
7576
let mut reached: VecDeque<Self::State> = VecDeque::new();

jingle/src/analysis/cpa/reducer.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,41 +45,44 @@ where
4545
}
4646
}
4747

48-
impl<'a, N> Residue<'a, N> for CfgReducer<'a, N>
48+
impl<'a, N> Residue<N> for CfgReducer<'a, N>
4949
where
5050
N: LocationState + CfgState,
5151
{
5252
type Output = PcodeCfg<N, PcodeOpRef<'a>>;
53+
5354
/// Record a reduction/transition from `state` to `dest_state` into the CFG.
5455
///
55-
/// This mirrors the logic previously implemented in the unwinding CPA's
56-
/// `reduce` method, but generalized: we convert both CPA states into `N`
57-
/// via the mapper and add nodes/edges to the cfg. If `op` is `None`,
58-
/// only the source node is added (no edge).
59-
fn new_state(&mut self, state: &N, dest_state: &N, op: &Option<PcodeOpRef<'a>>) {
56+
/// The op lifetime is generic per-call (`'op`). We accept an `Option<PcodeOpRef<'op>>`
57+
/// and convert the referenced operation into an owned `PcodeOpRef<'a>` for storage
58+
/// inside the CFG so we don't retain references tied to the caller's borrow.
59+
fn new_state<'op>(&mut self, state: &N, dest_state: &N, op: &Option<PcodeOpRef<'op>>) {
6060
self.cfg.add_node(state);
6161

6262
if let Some(op) = op {
63-
// Convert the wrapped op into an owned PcodeOperation before inserting.
64-
// `PcodeOpRef` derefs to `PcodeOperation`; call `as_ref().clone()` to obtain an owned op.
65-
let owned_op = op.clone();
63+
// Clone the inner PcodeOperation into an owned value, then wrap it in a
64+
// `PcodeOpRef<'a>` so the CFG stores an owned operation rather than borrowing
65+
// from the caller's lifetime.
66+
let owned = op.as_ref().clone();
67+
let stored: PcodeOpRef<'a> = PcodeOpRef::from(owned);
6668
// add_edge will insert nodes if missing
67-
self.cfg.add_edge(state, dest_state, owned_op);
69+
self.cfg.add_edge(state, dest_state, stored);
6870
}
6971
}
7072

7173
/// When two abstract states are merged in the CPA, adjust the recorded CFG
7274
/// so edges that previously pointed to the original `dest_state` now point
7375
/// to the `merged_state`.
7476
///
75-
/// This duplicates the behavior from the unwinding CPA's `merged` method in
76-
/// a generic way.
77-
fn merged_state(
77+
/// The op lifetime is generic per-call (`'op`). Convert any provided op into
78+
/// an owned `PcodeOpRef<'a>` before storing it in the CFG to avoid borrowing
79+
/// across the caller's lifetime.
80+
fn merged_state<'op>(
7881
&mut self,
7982
state: &N,
8083
dest_state: &N,
8184
merged_state: &N,
82-
op: &Option<PcodeOpRef<'a>>,
85+
op: &Option<PcodeOpRef<'op>>,
8386
) {
8487
tracing::debug!("merged called: dest_state and merged_state provided");
8588
// If operation is not present we can't deterministically reconstruct
@@ -88,8 +91,9 @@ where
8891
// We only proceed when there is an op provided (matches unwinding impl).
8992
self.cfg.replace_and_combine_nodes(dest_state, merged_state);
9093
if let Some(op) = op {
91-
let owned_op = op.clone();
92-
self.cfg.add_edge(state, merged_state, owned_op);
94+
let owned = op.as_ref().clone();
95+
let stored: PcodeOpRef<'a> = PcodeOpRef::from(owned);
96+
self.cfg.add_edge(state, merged_state, stored);
9397
}
9498
}
9599

0 commit comments

Comments
 (0)