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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Task 7 (NL-034): `ReadSetLattice` — the `sv_lang::dataflow::Lattice`
//! implementation used to compute a continuous assign's RHS read-set via
//! slang's own CFG walker (netlist-analysis.md Decision 1 — the single
//! biggest scope-reduction move: this crate never hand-recurses the
//! ~20k-line Expression/Statement AST surface itself). `write` events are
//! recorded too (used starting Task 8's driver graph), but this task only
//! consumes `reads`.
//!
//! **Deviation from the plan's literal field types, documented here
//! (binding for this whole module).** The plan's own illustrative shape is
//! `pub reads: BTreeSet<crate::netlist::SymbolKey>` — but `SymbolKey` is a
//! position-based index INTO ONE PARTICULAR SCOPE's `members()`
//! enumeration, and `Lattice::top()` (the entry state `run_dataflow`
//! constructs before ever calling `transfer`) takes no arguments at all:
//! there is no way for a per-scope `Symbol -> SymbolKey` table to reach the
//! lattice's own state at construction time, so `transfer` can never itself
//! produce a `SymbolKey` for the `Symbol` an event carries. `sv_lang::
//! SymbolId` (`Symbol::id()`, `ast.rs:5216`) is the context-free, `Hash +
//! Eq + Clone` stable identity `sv-lang` itself publishes for exactly this
//! kind of "remember a symbol without a live table" need (already used by
//! `dedup::DefinitionTable` for the same reason) — this lattice accumulates
//! `SymbolId`s during the run, and [`cont_assign_read_set`] (the caller,
//! `elaborate.rs`) converts the finished set to `SymbolKey`s via a table
//! built from the SAME scope the procedure symbol itself belongs to, once
//! `run_dataflow` has already returned.
use crate;
use crateSymbolUid;
use ;
/// **Review fix (r1, blocking finding on `build_own_cont_assigns`).**
/// `reads` used to be a `HashSet<SymbolId>`: a symbol that is both the
/// LHS lvalue-address-evaluation artifact `run_dataflow` reports for a
/// continuous assign's own target AND a genuine RHS operand (`assign y =
/// y | a;` — a legal LRM continuous-assign feedback/latch idiom that
/// compiles and simulates under iverilog) collapsed to one set entry, so
/// the old blanket `read_set.retain(|&key| key != target)` in
/// `elaborate.rs` silently dropped the real dependency along with the
/// artifact. Every `Read` event is now kept as its own entry, paired with
/// the expression node it occurred at (`DfaEvent::node`'s `source_range`),
/// so the caller can tell the two apart by whether the node lies inside
/// the assignment's RHS span (see [`cont_assign_read_set`]).
pub
/// Runs [`ReadSetLattice`] via `EvalSession::run_dataflow` over `procedure`
/// (this task's own use — a `ContinuousAssign` symbol; `run_dataflow`'s own
/// doc comment, `dataflow.rs:389-391`, also allows an `always`/`initial`/
/// `final` block or a subroutine, for a future task's reuse) and converts
/// the resulting read-set to `SymbolKey`s via `scope_keys` (built by the
/// caller from the SAME scope `procedure` is declared in — see this
/// module's own doc comment for why the conversion cannot happen inside the
/// `Lattice` itself). A read that resolves to a symbol NOT present in
/// `scope_keys` (e.g. a reference to a `parameter`/`localparam`, which this
/// wave's `Template.signals` never enumerates) is silently excluded from
/// the result — not an error: `scope_keys` is deliberately built from
/// `build_members`'s own SymbolKey-having member set, so "not in
/// scope_keys" means "not a signal this wave's `Template` tracks", not
/// "something went wrong".
///
/// **Review fix (r1, blocking finding on `build_own_cont_assigns`).**
/// `target` is the assignment's own LHS symbol id and `rhs_range` is
/// `assignment.right()`'s [`sv_lang::Expression::source_range`].
/// `run_dataflow` reports a `Read` for `target` BEFORE its `Write` as an
/// artifact of evaluating the LHS as an lvalue address (e.g. for `assign y
/// = a & b;` the raw event stream is `Read(y), Read(a), Read(b), Write(y)`)
/// — that artifact's `DfaEvent::node` is the LHS expression, outside
/// `rhs_range`, so it is dropped. Any OTHER read of `target` whose node
/// lies inside `rhs_range` is a genuine RHS self-reference (`assign y = y
/// | a;`, a legal LRM continuous-assign feedback/latch idiom) and is kept —
/// this is exactly what the old `HashSet<SymbolId>`-based
/// `read_set.retain(|&key| key != target)` in `elaborate.rs` could not
/// distinguish, since both the artifact and a genuine self-read collapsed
/// to the same set entry.
pub
/// True if `inner` lies fully within `outer` — same buffers, offsets
/// nested. Used to tell a genuine RHS read of a continuous assign's own
/// target apart from the LHS lvalue-address-evaluation artifact
/// `run_dataflow` also reports for it (see [`cont_assign_read_set`]).
pub