1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::ir::*;
use crate::{HashSet, OptimizationContext};
/// Remove stores from `eval_comb` whose target addresses are not live.
///
/// A store's address is considered live if:
/// - It is in `externally_live` (user-specified observable signals), OR
/// - Any execution unit Loads from it (or Commits from it), OR
/// - It has a dynamic offset (conservative), OR
/// - The store has non-empty triggers (edge-detection side effect), OR
/// - The store has non-empty comb capture sites (observer activation side effect).
pub(crate) fn eliminate_dead_stores(
program: &mut OptimizationContext,
externally_live: &HashSet<AbsoluteAddr>,
) {
// 1. Collect all addresses loaded across ALL execution units.
let mut loaded_addrs: HashSet<AbsoluteAddr> = HashSet::default();
let mut dynamic_addrs: HashSet<AbsoluteAddr> = HashSet::default();
let all_eus = program
.sir
.eval_comb
.iter()
.chain(
program
.sir
.eval_apply_ffs
.values()
.flat_map(|units| units.iter()),
)
.chain(
program
.sir
.eval_comb_apply_ffs
.values()
.flat_map(|units| units.iter()),
)
.chain(
program
.sir
.eval_only_ffs
.values()
.flat_map(|units| units.iter()),
)
.chain(
program
.sir
.apply_ffs
.values()
.flat_map(|units| units.iter()),
);
for eu in all_eus {
for block in eu.blocks.values() {
for inst in &block.instructions {
match inst {
SIRInstruction::Load(_, addr, offset, _)
if offset.constant_bit_offset().is_some() =>
{
loaded_addrs.insert(addr.absolute_addr());
}
SIRInstruction::Load(
_,
addr,
SIROffset::Dynamic(_) | SIROffset::Element { .. },
_,
) => {
let key = addr.absolute_addr();
loaded_addrs.insert(key);
dynamic_addrs.insert(key);
}
SIRInstruction::Commit(src, _, offset, _, _)
if offset.constant_bit_offset().is_some() =>
{
loaded_addrs.insert(src.absolute_addr());
}
SIRInstruction::Commit(
src,
_,
SIROffset::Dynamic(_) | SIROffset::Element { .. },
_,
_,
) => {
let key = src.absolute_addr();
loaded_addrs.insert(key);
dynamic_addrs.insert(key);
}
_ => {}
}
}
}
}
// 2. Remove dead stores from eval_comb.
for eu in program.sir.eval_comb.iter_mut() {
for block in eu.blocks.values_mut() {
block.instructions.retain(|inst| {
match inst {
SIRInstruction::Store(addr, offset, _, _, triggers, comb_capture_sites)
if offset.constant_bit_offset().is_some()
&& triggers.is_empty()
&& comb_capture_sites.is_empty() =>
{
let abs = addr.absolute_addr();
externally_live.contains(&abs)
|| loaded_addrs.contains(&abs)
|| dynamic_addrs.contains(&abs)
}
// Keep stores with dynamic offsets or triggers unconditionally.
_ => true,
}
});
}
}
}