Skip to content

Commit 11e0138

Browse files
committed
Add 'poisoned' field for latency counting
1 parent a4e84e6 commit 11e0138

File tree

3 files changed

+78
-35
lines changed

3 files changed

+78
-35
lines changed

src/flattening/flatten.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::alloc::{ArenaAllocator, UUIDAllocator, UUIDRange, UUID};
2-
use crate::flattening::port_latency_inference::make_port_latency_inference_info;
32
use crate::typing::abstract_type::{AbstractType, DomainType};
43
use crate::{alloc::UUIDRangeIter, prelude::*};
54

@@ -1785,7 +1784,7 @@ fn flatten_global(linker: &mut Linker, global_obj: GlobalUUID, cursor: &mut Curs
17851784
decl.typ.domain = DomainType::Physical(port.domain);
17861785
}
17871786

1788-
md.latency_inference_info = make_port_latency_inference_info(
1787+
md.latency_inference_info = PortLatencyInferenceInfo::make(
17891788
&md.ports,
17901789
&instructions,
17911790
md.link_info.template_parameters.len(),

src/flattening/port_latency_inference.rs

+73-31
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ struct PortGroup {
2929
///
3030
/// portA'4 -> portB'L-3
3131
///
32-
/// That would create a inference candidate for template var 'L' with an offset of 7. (L == 7 iff portA' - portB' == 0)
32+
/// That would create a inference candidate for template var 'L' with an offset of 7. (L == 7 iff |portA| - |portB| == 0)
33+
///
34+
/// So |from| + offset + L == |to|
35+
///
36+
/// L = |to| - |from| - offset
3337
#[derive(Debug, Clone)]
3438
struct LatencyInferenceCandidate {
35-
template_id: TemplateID,
3639
from: PortID,
3740
to: PortID,
3841
offset: i64,
@@ -162,43 +165,82 @@ fn recurse_down_expression(
162165
}
163166
}
164167

168+
/// The basic way latency count inference works is as follows:
169+
/// On the module interface, ports may be marked with latency annotations.
170+
/// These annotations can be simple constants (portA'0, portB'-3, etc),
171+
/// or larger expressions involving some template parameter, such as portC'L, portD'L+3-2
172+
///
173+
/// Whereever there is a difference in the latency annitation between two ports of exactly
174+
/// 1x a variable + some constant offset, the port pair becomes eligible for latency inference
175+
///
176+
/// When the module is flattened, we can immediately construct for every template parameter,
177+
/// a list of all port pairs that may be used to infer the value of this parameter.
178+
///
179+
/// Once we come to actually performing said inference [Self::try_infer_var], we take the list
180+
/// of absolute latencies we know for these ports, and take the minimum latency we could find.
181+
/// This ensures that instantiating the module cannot ever expand beyond the context in which
182+
/// it is inferred. Finally, all
165183
#[derive(Default, Debug)]
166184
pub struct PortLatencyInferenceInfo {
167185
//port_latency_groups: Vec<Vec<PortGroup>>,
168-
inference_candidates: Vec<LatencyInferenceCandidate>,
186+
inference_candidates: TVec<Vec<LatencyInferenceCandidate>>,
169187
}
170188

171-
pub fn make_port_latency_inference_info(
172-
ports: &FlatAlloc<Port, PortIDMarker>,
173-
instructions: &FlatAlloc<Instruction, FlatIDMarker>,
174-
num_template_args: usize,
175-
) -> PortLatencyInferenceInfo {
176-
let port_infos = ports.map(|(_port_id, port)| {
177-
let decl = instructions[port.declaration_instruction].unwrap_declaration();
178-
179-
decl.latency_specifier.and_then(|latency_spec| {
180-
recurse_down_expression(instructions, latency_spec, num_template_args)
181-
})
182-
});
183-
184-
let mut inference_candidates = Vec::new();
185-
186-
for (from, from_info) in port_infos.iter_valids() {
187-
for (to, to_info) in port_infos.iter_valids() {
188-
if let Some((template_id, offset)) =
189-
PortLatencyLinearity::is_pair_latency_candidate(from_info, to_info)
190-
{
191-
inference_candidates.push(LatencyInferenceCandidate {
192-
template_id,
193-
from,
194-
to,
195-
offset,
196-
});
189+
impl PortLatencyInferenceInfo {
190+
pub fn make(
191+
ports: &FlatAlloc<Port, PortIDMarker>,
192+
instructions: &FlatAlloc<Instruction, FlatIDMarker>,
193+
num_template_args: usize,
194+
) -> PortLatencyInferenceInfo {
195+
let port_infos = ports.map(|(_port_id, port)| {
196+
let decl = instructions[port.declaration_instruction].unwrap_declaration();
197+
198+
decl.latency_specifier.and_then(|latency_spec| {
199+
recurse_down_expression(instructions, latency_spec, num_template_args)
200+
})
201+
});
202+
203+
let mut inference_candidates = FlatAlloc::with_size(num_template_args, Vec::new());
204+
205+
for (from, from_info) in port_infos.iter_valids() {
206+
for (to, to_info) in port_infos.iter_valids() {
207+
if ports[from].domain != ports[to].domain {
208+
continue; // ports on different domains cannot be related in latency counting
209+
}
210+
if let Some((template_id, offset)) =
211+
PortLatencyLinearity::is_pair_latency_candidate(from_info, to_info)
212+
{
213+
inference_candidates[template_id].push(LatencyInferenceCandidate {
214+
from,
215+
to,
216+
offset,
217+
});
218+
}
197219
}
198220
}
221+
222+
PortLatencyInferenceInfo {
223+
inference_candidates,
224+
}
199225
}
200226

201-
PortLatencyInferenceInfo {
202-
inference_candidates,
227+
/// To infer a specific specific variable, we look at all
228+
pub fn try_infer_var(
229+
&self,
230+
template_var: TemplateID,
231+
port_latencies: &FlatAlloc<Option<i64>, PortIDMarker>,
232+
) -> Option<i64> {
233+
let mut inferred_variable_value = i64::MAX;
234+
235+
for candidate in &self.inference_candidates[template_var] {
236+
let from_latency = port_latencies[candidate.from]?;
237+
let to_latency = port_latencies[candidate.to]?;
238+
239+
let this_pair_infers_to = to_latency - from_latency - candidate.offset;
240+
241+
inferred_variable_value = i64::min(inferred_variable_value, this_pair_infers_to);
242+
}
243+
244+
(inferred_variable_value != i64::MAX).then_some(inferred_variable_value)
203245
}
204246
}

src/instantiation/latency_algorithm.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,19 @@ struct LatencyStackElem<'d> {
6262
}
6363

6464
/// The node for the latency-counting graph. See [solve_latencies]
65-
///
66-
/// TODO make this only take up 8 bytes with bitfield
6765
#[derive(Clone, Copy)]
6866
struct LatencyNode {
6967
abs_lat: i64,
7068
pinned: bool,
69+
#[allow(unused)]
70+
poisoned: bool,
7171
}
7272

7373
impl LatencyNode {
7474
const UNSET: LatencyNode = LatencyNode {
7575
abs_lat: i64::MIN,
7676
pinned: false,
77+
poisoned: false,
7778
};
7879

7980
fn get(&self) -> i64 {
@@ -102,6 +103,7 @@ impl LatencyNode {
102103
LatencyNode {
103104
abs_lat,
104105
pinned: true,
106+
poisoned: false,
105107
}
106108
}
107109
fn assert_is_set(&self) {

0 commit comments

Comments
 (0)