Skip to content

Commit 5108297

Browse files
authored
feat: Import CFG regions without adding an entry block. (#2200)
This PR changes the way that CFG regions are imported, avoiding the need to insert an empty entry block.
1 parent f6163bf commit 5108297

File tree

4 files changed

+77
-75
lines changed

4 files changed

+77
-75
lines changed

hugr-core/src/import.rs

Lines changed: 26 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -868,86 +868,46 @@ impl<'a> Context<'a> {
868868
self.region_scope = region;
869869
}
870870

871-
let [region_source, region_targets] = self.get_func_type(
871+
let [_, region_targets] = self.get_func_type(
872872
region_data
873873
.signature
874874
.ok_or_else(|| error_uninferred!("region signature"))?,
875875
)?;
876876

877-
let region_source_types = self.import_closed_list(region_source)?;
878877
let region_target_types = self.import_closed_list(region_targets)?;
879878

880-
// Create the entry node for the control flow region.
881-
// Since the core hugr does not have explicit entry blocks yet, we create a dataflow block
882-
// that simply forwards its inputs to its outputs.
883-
{
884-
let types = {
885-
let [ctrl_type] = region_source_types.as_slice() else {
886-
return Err(table::ModelError::TypeError(region_source).into());
887-
};
888-
889-
let [types] = self.expect_symbol(*ctrl_type, model::CORE_CTRL)?;
890-
self.import_type_row(types)?
879+
// Identify the entry node of the control flow region by looking for
880+
// a block whose input is linked to the sole source port of the CFG region.
881+
let entry_node = 'find_entry: {
882+
let [entry_link] = region_data.sources else {
883+
return Err(table::ModelError::InvalidRegions(node_id).into());
891884
};
892885

893-
let entry = self.hugr.add_node_with_parent(
894-
node,
895-
OpType::DataflowBlock(DataflowBlock {
896-
inputs: types.clone(),
897-
other_outputs: TypeRow::default(),
898-
sum_rows: vec![types.clone()],
899-
}),
900-
);
901-
902-
self.record_links(entry, Direction::Outgoing, region_data.sources);
903-
904-
let node_input = self.hugr.add_node_with_parent(
905-
entry,
906-
OpType::Input(Input {
907-
types: types.clone(),
908-
}),
909-
);
910-
911-
let node_output = self.hugr.add_node_with_parent(
912-
entry,
913-
OpType::Output(Output {
914-
types: vec![Type::new_sum([types.clone()])].into(),
915-
}),
916-
);
917-
918-
let node_tag = self.hugr.add_node_with_parent(
919-
entry,
920-
OpType::Tag(Tag {
921-
tag: 0,
922-
variants: vec![types],
923-
}),
924-
);
925-
926-
// Connect the input node to the tag node
927-
let input_outputs = self.hugr.node_outputs(node_input);
928-
let tag_inputs = self.hugr.node_inputs(node_tag);
929-
let mut connections =
930-
Vec::with_capacity(input_outputs.size_hint().0 + tag_inputs.size_hint().0);
931-
932-
for (a, b) in input_outputs.zip(tag_inputs) {
933-
connections.push((node_input, a, node_tag, b));
934-
}
935-
936-
// Connect the tag node to the output node
937-
let tag_outputs = self.hugr.node_outputs(node_tag);
938-
let output_inputs = self.hugr.node_inputs(node_output);
886+
for child in region_data.children {
887+
let child_data = self.get_node(*child)?;
888+
let is_entry = child_data.inputs.iter().any(|link| link == entry_link);
939889

940-
for (a, b) in tag_outputs.zip(output_inputs) {
941-
connections.push((node_tag, a, node_output, b));
890+
if is_entry {
891+
break 'find_entry *child;
892+
}
942893
}
943894

944-
for (src, src_port, dst, dst_port) in connections {
945-
self.hugr.connect(src, src_port, dst, dst_port);
946-
}
947-
}
895+
// TODO: We should allow for the case in which control flows
896+
// directly from the source to the target of the region. This is
897+
// currently not allowed in hugr core directly, but may be simulated
898+
// by constructing an empty entry block.
899+
return Err(table::ModelError::InvalidRegions(node_id).into());
900+
};
901+
902+
// The entry node in core control flow regions is identified by being
903+
// the first child node of the CFG node. We therefore import the entry
904+
// node first and follow it up by every other node.
905+
self.import_node(entry_node, node)?;
948906

949907
for child in region_data.children {
950-
self.import_node(*child, node)?;
908+
if *child != entry_node {
909+
self.import_node(*child, node)?;
910+
}
951911
}
952912

953913
// Create the exit node for the control flow region.

hugr-core/tests/snapshots/model__roundtrip_cfg.snap

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,22 @@ expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-cfg.
1616

1717
(import core.adt)
1818

19-
(define-func example.cfg (param ?0 core.type) (core.fn [?0] [?0])
19+
(define-func example.cfg_loop (param ?0 core.type) (core.fn [?0] [?0])
20+
(dfg [%0] [%1]
21+
(signature (core.fn [?0] [?0]))
22+
(cfg [%0] [%1]
23+
(signature (core.fn [?0] [?0]))
24+
(cfg [%2] [%3]
25+
(signature (core.fn [(core.ctrl [?0])] [(core.ctrl [?0])]))
26+
(block [%2] [%3 %2]
27+
(signature
28+
(core.fn [(core.ctrl [?0])] [(core.ctrl [?0]) (core.ctrl [?0])]))
29+
(dfg [%4] [%5]
30+
(signature (core.fn [?0] [(core.adt [[?0] [?0]])]))
31+
((core.make_adt _ _ 0) [%4] [%5]
32+
(signature (core.fn [?0] [(core.adt [[?0] [?0]])])))))))))
33+
34+
(define-func example.cfg_order (param ?0 core.type) (core.fn [?0] [?0])
2035
(dfg [%0] [%1]
2136
(signature (core.fn [?0] [?0]))
2237
(cfg [%0] [%1]
@@ -29,10 +44,9 @@ expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-cfg.
2944
(signature (core.fn [?0] [(core.adt [[?0]])]))
3045
((core.make_adt _ _ 0) [%4] [%5]
3146
(signature (core.fn [?0] [(core.adt [[?0]])])))))
32-
(block [%6] [%3 %6]
33-
(signature
34-
(core.fn [(core.ctrl [?0])] [(core.ctrl [?0]) (core.ctrl [?0])]))
47+
(block [%6] [%3]
48+
(signature (core.fn [(core.ctrl [?0])] [(core.ctrl [?0])]))
3549
(dfg [%7] [%8]
36-
(signature (core.fn [?0] [(core.adt [[?0] [?0]])]))
50+
(signature (core.fn [?0] [(core.adt [[?0]])]))
3751
((core.make_adt _ _ 0) [%7] [%8]
38-
(signature (core.fn [?0] [(core.adt [[?0] [?0]])])))))))))
52+
(signature (core.fn [?0] [(core.adt [[?0]])])))))))))

hugr-model/tests/fixtures/model-cfg.edn

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
(mod)
44

5-
(define-func example.cfg
5+
(define-func example.cfg_loop
66
(param ?a core.type)
77
(core.fn [?a] [?a])
88
(dfg [%0] [%1]
@@ -17,3 +17,25 @@
1717
(signature (core.fn [?a] [(core.adt [[?a] [?a]])]))
1818
((core.make_adt _ _ 0) [%5] [%6]
1919
(signature (core.fn [?a] [(core.adt [[?a] [?a]])])))))))))
20+
21+
(define-func example.cfg_order
22+
(param ?a core.type)
23+
(core.fn [?a] [?a])
24+
(dfg [%0] [%1]
25+
(signature (core.fn [?a] [?a]))
26+
(cfg [%0] [%1]
27+
(signature (core.fn [?a] [?a]))
28+
(cfg [%2] [%4]
29+
(signature (core.fn [(core.ctrl [?a])] [(core.ctrl [?a])]))
30+
(block [%3] [%4]
31+
(signature (core.fn [(core.ctrl [?a])] [(core.ctrl [?a])]))
32+
(dfg [%5] [%6]
33+
(signature (core.fn [?a] [(core.adt [[?a]])]))
34+
((core.make_adt _ _ 0) [%5] [%6]
35+
(signature (core.fn [?a] [(core.adt [[?a]])])))))
36+
(block [%2] [%3]
37+
(signature (core.fn [(core.ctrl [?a])] [(core.ctrl [?a])]))
38+
(dfg [%7] [%8]
39+
(signature (core.fn [?a] [(core.adt [[?a]])]))
40+
((core.make_adt _ _ 0) [%7] [%8]
41+
(signature (core.fn [?a] [(core.adt [[?a]])])))))))))

hugr-model/tests/fixtures/model-entrypoint.edn

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
(signature (core.fn [] []))
2525
(cfg [] []
2626
(signature (core.fn [] []))
27-
(cfg [%ctrl] [%ctrl]
27+
(cfg [%entry] [%exit]
2828
(signature (core.fn [(core.ctrl [])] [(core.ctrl [])]))
29-
(meta core.entrypoint)))))
29+
(meta core.entrypoint)
30+
(block [%entry] [%exit]
31+
(signature (core.fn [(core.ctrl [])] [(core.ctrl [])]))
32+
(dfg [] [%value]
33+
(signature (core.fn [] [(core.adt [[]])]))
34+
((core.make_adt _ _ 0) [] [%value]
35+
(signature (core.fn [] [(core.adt [[]])])))))))))

0 commit comments

Comments
 (0)