Skip to content

Commit a2acfb9

Browse files
committed
Fix: frame dependency calculation
1 parent 3df866d commit a2acfb9

15 files changed

+267
-31
lines changed

src/program/graph.rs

+108-15
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,75 @@ pub struct InstructionBlock {
200200
pub terminator: BlockTerminator,
201201
}
202202

203+
/// PreviousNodes is a structure which helps maintain ordering among instructions which operate on a given frame.
204+
/// It works similarly to a multiple-reader-single-writer queue, where an instruction which "uses" a frame is like
205+
/// a writer and an instruction which blocks that frame is like a reader. Multiple instructions may concurrently
206+
/// block a frame, but an instruction may not use a frame while it is concurrently used or blocked.
207+
///
208+
/// ## Examples
209+
///
210+
/// Note that "depends on" is equivalent to "must execute after completion of".
211+
///
212+
/// ```ignore
213+
/// user --> user # a second user takes a dependency on the first
214+
///
215+
/// user --> blocker # multiple blockers take a dependency on the most recent user
216+
/// \-> blocker
217+
/// \-> blocker
218+
///
219+
/// blocker --> user --> blocker # users and blockers take dependencies on one another,
220+
/// # but blockers do not depend on other blocking instructions
221+
/// ```
222+
struct PreviousNodes {
223+
using: Option<ScheduledGraphNode>,
224+
blocking: HashSet<ScheduledGraphNode>,
225+
}
226+
227+
impl Default for PreviousNodes {
228+
/// The default value for [PreviousNodes] is useful in that, if no previous nodes have been recorded
229+
/// as using a frame, we should consider that the start of the instruction block "blocks" use of that frame
230+
/// (in other words, this instruction cannot be scheduled prior to the start of the instruction block).
231+
fn default() -> Self {
232+
Self {
233+
using: None,
234+
blocking: vec![ScheduledGraphNode::BlockStart].into_iter().collect(),
235+
}
236+
}
237+
}
238+
239+
impl PreviousNodes {
240+
/// Register a node as using a frame, and return the instructions on which it should depend/wait for scheduling (if any).
241+
///
242+
/// A node which uses a frame will block on any previous user or blocker of the frame, much like a writer in a read-write lock.
243+
pub fn register_user(&mut self, node: ScheduledGraphNode) -> HashSet<ScheduledGraphNode> {
244+
let mut result = std::mem::take(&mut self.blocking);
245+
if let Some(previous_user) = self.using.replace(node) {
246+
result.insert(previous_user);
247+
}
248+
249+
result
250+
}
251+
252+
/// Register a node as blocking a frame, and return the instructions on which it should depend/wait for scheduling (if any).
253+
///
254+
/// A node which blocks a frame will block on any previous user of the frame, but not concurrent blockers.
255+
///
256+
/// If the frame is currently blocked by other nodes, it will add itself to the list of blockers,
257+
/// much like a reader in a read-write lock.
258+
pub fn register_blocker(&mut self, node: ScheduledGraphNode) -> Option<ScheduledGraphNode> {
259+
self.blocking.insert(node);
260+
self.using
261+
}
262+
263+
/// Consume the [PreviousNodes] and return all nodes within.
264+
pub fn drain(mut self) -> HashSet<ScheduledGraphNode> {
265+
if let Some(using) = self.using {
266+
self.blocking.insert(using);
267+
}
268+
self.blocking
269+
}
270+
}
271+
203272
impl InstructionBlock {
204273
pub fn build(
205274
instructions: Vec<Instruction>,
@@ -213,8 +282,7 @@ impl InstructionBlock {
213282
let mut last_classical_instruction = ScheduledGraphNode::BlockStart;
214283

215284
// Store the instruction index of the last instruction to block that frame
216-
let mut last_instruction_by_frame: HashMap<FrameIdentifier, ScheduledGraphNode> =
217-
HashMap::new();
285+
let mut last_instruction_by_frame: HashMap<FrameIdentifier, PreviousNodes> = HashMap::new();
218286

219287
// Store memory access reads and writes. Key is memory region name.
220288
// NOTE: this may be refined to serialize by memory region offset rather than by entire region.
@@ -233,24 +301,47 @@ impl InstructionBlock {
233301
Ok(())
234302
}
235303
InstructionRole::RFControl => {
236-
let used_frames = program
304+
let used_frames: HashSet<&FrameIdentifier> = program
237305
.get_frames_for_instruction(instruction, false)
238-
.unwrap_or_default();
239-
let blocked_frames = program
306+
.unwrap_or_default()
307+
.into_iter()
308+
.collect();
309+
let blocked_frames: HashSet<&FrameIdentifier> = program
240310
.get_frames_for_instruction(instruction, true)
241-
.unwrap_or_default();
311+
.unwrap_or_default()
312+
.into_iter()
313+
.filter(|f| !used_frames.contains(f))
314+
.collect();
242315

243-
// Take a dependency on any previous instructions to _block_ a frame which this instruction _uses_.
244316
for frame in used_frames {
245-
let previous_node_id = last_instruction_by_frame
246-
.get(frame)
247-
.unwrap_or(&ScheduledGraphNode::BlockStart);
248-
add_dependency!(graph, *previous_node_id => node, ExecutionDependency::ReferenceFrame);
317+
let previous_node_ids = last_instruction_by_frame
318+
.entry(frame.clone())
319+
.or_insert(PreviousNodes {
320+
using: None,
321+
blocking: vec![ScheduledGraphNode::BlockStart]
322+
.into_iter()
323+
.collect(),
324+
})
325+
.register_user(node);
326+
327+
for previous_node_id in previous_node_ids {
328+
add_dependency!(graph, previous_node_id => node, ExecutionDependency::ReferenceFrame);
329+
}
249330
}
250331

251-
// We mark all "blocked" frames as such for later instructions to take a dependency on
252332
for frame in blocked_frames {
253-
last_instruction_by_frame.insert(frame.clone(), node);
333+
if let Some(previous_node_id) = last_instruction_by_frame
334+
.entry(frame.clone())
335+
.or_insert(PreviousNodes {
336+
using: None,
337+
blocking: vec![ScheduledGraphNode::BlockStart]
338+
.into_iter()
339+
.collect(),
340+
})
341+
.register_blocker(node)
342+
{
343+
add_dependency!(graph, previous_node_id => node, ExecutionDependency::ReferenceFrame);
344+
}
254345
}
255346

256347
Ok(())
@@ -295,8 +386,10 @@ impl InstructionBlock {
295386
// does not terminate until these are complete
296387
add_dependency!(graph, last_classical_instruction => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
297388

298-
for (_, last_instruction) in last_instruction_by_frame {
299-
add_dependency!(graph, last_instruction => ScheduledGraphNode::BlockEnd, ExecutionDependency::ReferenceFrame);
389+
for (_, last_instructions) in last_instruction_by_frame {
390+
for node in last_instructions.drain() {
391+
add_dependency!(graph, node => ScheduledGraphNode::BlockEnd, ExecutionDependency::ReferenceFrame);
392+
}
300393
}
301394

302395
// Examine all "pending" memory operations for all regions

src/program/graphviz_dot.rs

+29
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,35 @@ NONBLOCKING PULSE 2 \"rf\" test(duration: 1e6)
300300
"
301301
);
302302

303+
build_dot_format_snapshot_test_case!(
304+
blocking_pulses_wrap_nonblocking,
305+
"
306+
PULSE 0 \"rf\" test(duration: 1e6)
307+
NONBLOCKING PULSE 0 \"ro_tx\" test(duration: 1e6)
308+
PULSE 0 \"rf\" test(duration: 1e6)
309+
FENCE 0
310+
FENCE 0
311+
"
312+
);
313+
314+
build_dot_format_snapshot_test_case!(
315+
blocking_pulses_after_nonblocking,
316+
"
317+
NONBLOCKING PULSE 0 \"ro_tx\" test(duration: 1e6)
318+
PULSE 0 \"rf\" test(duration: 1e6)
319+
PULSE 0 \"ro_rx\" test(duration: 1e6)
320+
"
321+
);
322+
323+
build_dot_format_snapshot_test_case!(
324+
blocking_2q_pulse,
325+
"
326+
PULSE 0 \"rf\" test(duration: 1e-6)
327+
PULSE 1 \"rf\" test(duration: 1e-6)
328+
PULSE 0 1 \"cz\" test(duration: 1e-6)
329+
"
330+
);
331+
303332
build_dot_format_snapshot_test_case!(
304333
fence_all_with_nonblocking_pulses,
305334
"

src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__active_reset_single_frame.snap

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ frame"];
2626
node [style="filled"];
2727
"feedback_start" [shape=circle, label="start"];
2828
"feedback_start" -> "feedback_0" [label="frame"];
29-
"feedback_start" -> "feedback_end" [label="ordering"];
29+
"feedback_start" -> "feedback_end" [label="frame
30+
ordering"];
3031
"feedback_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1000000.0)"];
3132
"feedback_0" -> "feedback_end" [label="frame"];
3233
"feedback_end" [shape=circle, label="end"];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
source: src/program/graphviz_dot.rs
3+
expression: dot_format
4+
---
5+
digraph {
6+
entry -> "block_0_start";
7+
entry [label="Entry Point"];
8+
subgraph cluster_0 {
9+
label="block_0";
10+
node [style="filled"];
11+
"block_0_start" [shape=circle, label="start"];
12+
"block_0_start" -> "block_0_0" [label="frame"];
13+
"block_0_start" -> "block_0_1" [label="frame"];
14+
"block_0_start" -> "block_0_2" [label="frame"];
15+
"block_0_start" -> "block_0_end" [label="frame
16+
ordering"];
17+
"block_0_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1e-6)"];
18+
"block_0_0" -> "block_0_2" [label="frame"];
19+
"block_0_0" -> "block_0_end" [label="frame"];
20+
"block_0_1" [shape=rectangle, label="[1] PULSE 1 \"rf\" test(duration: 1e-6)"];
21+
"block_0_1" -> "block_0_2" [label="frame"];
22+
"block_0_1" -> "block_0_end" [label="frame"];
23+
"block_0_2" [shape=rectangle, label="[2] PULSE 0 1 \"cz\" test(duration: 1e-6)"];
24+
"block_0_2" -> "block_0_end" [label="frame"];
25+
"block_0_end" [shape=circle, label="end"];
26+
}
27+
}
28+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
source: src/program/graphviz_dot.rs
3+
expression: dot_format
4+
---
5+
digraph {
6+
entry -> "block_0_start";
7+
entry [label="Entry Point"];
8+
subgraph cluster_0 {
9+
label="block_0";
10+
node [style="filled"];
11+
"block_0_start" [shape=circle, label="start"];
12+
"block_0_start" -> "block_0_0" [label="frame"];
13+
"block_0_start" -> "block_0_1" [label="frame"];
14+
"block_0_start" -> "block_0_2" [label="frame"];
15+
"block_0_start" -> "block_0_end" [label="frame
16+
ordering"];
17+
"block_0_0" [shape=rectangle, label="[0] NONBLOCKING PULSE 0 \"ro_tx\" test(duration: 1000000.0)"];
18+
"block_0_0" -> "block_0_1" [label="frame"];
19+
"block_0_0" -> "block_0_2" [label="frame"];
20+
"block_0_0" -> "block_0_end" [label="frame"];
21+
"block_0_1" [shape=rectangle, label="[1] PULSE 0 \"rf\" test(duration: 1000000.0)"];
22+
"block_0_1" -> "block_0_2" [label="frame"];
23+
"block_0_1" -> "block_0_end" [label="frame"];
24+
"block_0_2" [shape=rectangle, label="[2] PULSE 0 \"ro_rx\" test(duration: 1000000.0)"];
25+
"block_0_2" -> "block_0_end" [label="frame"];
26+
"block_0_end" [shape=circle, label="end"];
27+
}
28+
}
29+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
source: src/program/graphviz_dot.rs
3+
expression: dot_format
4+
---
5+
digraph {
6+
entry -> "block_0_start";
7+
entry [label="Entry Point"];
8+
subgraph cluster_0 {
9+
label="block_0";
10+
node [style="filled"];
11+
"block_0_start" [shape=circle, label="start"];
12+
"block_0_start" -> "block_0_0" [label="frame"];
13+
"block_0_start" -> "block_0_1" [label="frame"];
14+
"block_0_start" -> "block_0_3" [label="frame"];
15+
"block_0_start" -> "block_0_end" [label="ordering"];
16+
"block_0_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1000000.0)"];
17+
"block_0_0" -> "block_0_1" [label="frame"];
18+
"block_0_0" -> "block_0_2" [label="frame"];
19+
"block_0_0" -> "block_0_3" [label="frame"];
20+
"block_0_1" [shape=rectangle, label="[1] NONBLOCKING PULSE 0 \"ro_tx\" test(duration: 1000000.0)"];
21+
"block_0_1" -> "block_0_2" [label="frame"];
22+
"block_0_1" -> "block_0_3" [label="frame"];
23+
"block_0_2" [shape=rectangle, label="[2] PULSE 0 \"rf\" test(duration: 1000000.0)"];
24+
"block_0_2" -> "block_0_3" [label="frame"];
25+
"block_0_3" [shape=rectangle, label="[3] FENCE 0"];
26+
"block_0_3" -> "block_0_4" [label="frame"];
27+
"block_0_4" [shape=rectangle, label="[4] FENCE 0"];
28+
"block_0_4" -> "block_0_end" [label="frame"];
29+
"block_0_end" [shape=circle, label="end"];
30+
}
31+
}
32+

src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__chained_pulses.snap

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ digraph {
1010
node [style="filled"];
1111
"block_0_start" [shape=circle, label="start"];
1212
"block_0_start" -> "block_0_0" [label="frame"];
13-
"block_0_start" -> "block_0_end" [label="ordering"];
13+
"block_0_start" -> "block_0_end" [label="frame
14+
ordering"];
1415
"block_0_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1000000.0)"];
1516
"block_0_0" -> "block_0_1" [label="frame"];
17+
"block_0_0" -> "block_0_end" [label="frame"];
1618
"block_0_1" [shape=rectangle, label="[1] PULSE 0 \"rf\" test(duration: 1000000.0)"];
1719
"block_0_1" -> "block_0_2" [label="frame"];
20+
"block_0_1" -> "block_0_end" [label="frame"];
1821
"block_0_2" [shape=rectangle, label="[2] PULSE 0 \"rf\" test(duration: 1000000.0)"];
1922
"block_0_2" -> "block_0_3" [label="frame"];
23+
"block_0_2" -> "block_0_end" [label="frame"];
2024
"block_0_3" [shape=rectangle, label="[3] PULSE 0 \"rf\" test(duration: 1000000.0)"];
2125
"block_0_3" -> "block_0_4" [label="frame"];
26+
"block_0_3" -> "block_0_end" [label="frame"];
2227
"block_0_4" [shape=rectangle, label="[4] PULSE 0 \"rf\" test(duration: 1000000.0)"];
2328
"block_0_4" -> "block_0_end" [label="frame"];
2429
"block_0_end" [shape=circle, label="end"];

src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ digraph {
1212
"block_0_start" -> "block_0_0" [label="frame"];
1313
"block_0_start" -> "block_0_1" [label="frame"];
1414
"block_0_start" -> "block_0_2" [label="frame"];
15-
"block_0_start" -> "block_0_end" [label="ordering"];
15+
"block_0_start" -> "block_0_end" [label="frame
16+
ordering"];
1617
"block_0_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1000000.0)"];
1718
"block_0_0" -> "block_0_end" [label="frame"];
1819
"block_0_1" [shape=rectangle, label="[1] PULSE 1 \"rf\" test(duration: 1000000.0)"];

src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__jump.snap

+6-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ digraph {
1010
node [style="filled"];
1111
"first-block_start" [shape=circle, label="start"];
1212
"first-block_start" -> "first-block_0" [label="frame"];
13-
"first-block_start" -> "first-block_end" [label="ordering"];
13+
"first-block_start" -> "first-block_end" [label="frame
14+
ordering"];
1415
"first-block_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1000000.0)"];
1516
"first-block_0" -> "first-block_end" [label="frame"];
1617
"first-block_end" [shape=circle, label="end"];
@@ -22,7 +23,8 @@ digraph {
2223
node [style="filled"];
2324
"second-block_start" [shape=circle, label="start"];
2425
"second-block_start" -> "second-block_0" [label="frame"];
25-
"second-block_start" -> "second-block_end" [label="ordering"];
26+
"second-block_start" -> "second-block_end" [label="frame
27+
ordering"];
2628
"second-block_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1000000.0)"];
2729
"second-block_0" -> "second-block_end" [label="frame"];
2830
"second-block_end" [shape=circle, label="end"];
@@ -33,7 +35,8 @@ digraph {
3335
node [style="filled"];
3436
"third-block_start" [shape=circle, label="start"];
3537
"third-block_start" -> "third-block_0" [label="frame"];
36-
"third-block_start" -> "third-block_end" [label="ordering"];
38+
"third-block_start" -> "third-block_end" [label="frame
39+
ordering"];
3740
"third-block_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1000000.0)"];
3841
"third-block_0" -> "third-block_end" [label="frame"];
3942
"third-block_end" [shape=circle, label="end"];

src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulse.snap

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ digraph {
1010
node [style="filled"];
1111
"block_0_start" [shape=circle, label="start"];
1212
"block_0_start" -> "block_0_0" [label="frame"];
13-
"block_0_start" -> "block_0_end" [label="ordering"];
13+
"block_0_start" -> "block_0_1" [label="frame"];
14+
"block_0_start" -> "block_0_end" [label="frame
15+
ordering"];
1416
"block_0_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(a: param[0])"];
1517
"block_0_0" -> "block_0_1" [label="frame"];
16-
"block_0_0" -> "block_0_end" [label="await read"];
18+
"block_0_0" -> "block_0_end" [label="await read
19+
frame"];
1720
"block_0_1" [shape=rectangle, label="[1] CAPTURE 0 \"ro_rx\" test(a: param[0]) ro[0]"];
1821
"block_0_1" -> "block_0_end" [label="await capture
1922
await read

src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap

+8-3
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ digraph {
1010
node [style="filled"];
1111
"block_0_start" [shape=circle, label="start"];
1212
"block_0_start" -> "block_0_0" [label="frame"];
13+
"block_0_start" -> "block_0_1" [label="frame"];
1314
"block_0_start" -> "block_0_2" [label="frame"];
14-
"block_0_start" -> "block_0_end" [label="ordering"];
15+
"block_0_start" -> "block_0_end" [label="frame
16+
ordering"];
1517
"block_0_0" [shape=rectangle, label="[0] CAPTURE 0 \"ro_rx\" test(a: param[0]) ro[0]"];
1618
"block_0_0" -> "block_0_1" [label="await capture
1719
frame"];
1820
"block_0_0" -> "block_0_3" [label="frame"];
19-
"block_0_0" -> "block_0_end" [label="await read"];
21+
"block_0_0" -> "block_0_end" [label="await read
22+
frame"];
2023
"block_0_1" [shape=rectangle, label="[1] NONBLOCKING PULSE 0 \"rf\" test(a: ro[0])"];
21-
"block_0_1" -> "block_0_3" [label="await read"];
24+
"block_0_1" -> "block_0_3" [label="await read
25+
frame"];
26+
"block_0_1" -> "block_0_4" [label="frame"];
2227
"block_0_2" [shape=rectangle, label="[2] NONBLOCKING PULSE 1 \"rf\" test(a: ro[0])"];
2328
"block_0_2" -> "block_0_3" [label="await read"];
2429
"block_0_2" -> "block_0_5" [label="frame"];

0 commit comments

Comments
 (0)