Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
5928fe8
Add validation check for empty SDFGs
tbennun Dec 3, 2023
0d84cec
Add explicit stree root node and SDFG conversion method
tbennun Dec 3, 2023
91d2bc6
Initial conversion function and test
tbennun Dec 3, 2023
33910d1
Structure conversion process and add tests
tbennun Dec 4, 2023
f74be2f
Non-data-dependency state boundary insertion
tbennun Dec 4, 2023
8863fbf
Merge branch 'master' into stree-to-sdfg
tbennun Dec 7, 2023
a0ca8e6
Merge branch 'master' into stree-to-sdfg
tbennun Jan 1, 2024
25a72b9
More test cases and design
tbennun Jan 1, 2024
2bf1da1
Implement memory-dependency state boundary pass, input/output memlet …
tbennun Jan 2, 2024
bb58e80
More tests
tbennun Jan 2, 2024
649b216
Copyright year
tbennun Jan 2, 2024
438683b
Copyright year
tbennun Jan 2, 2024
7569304
Use memlet sets
tbennun Jan 2, 2024
3c1a786
Implement stree scope memlet analysis, memlet propagation now accepts…
tbennun Jan 2, 2024
62527d0
Fix deprecation warning
tbennun Jan 3, 2024
2baa73c
Implement union-set of memlets
tbennun Jan 3, 2024
c00f8dc
Implement memlet dictionary
tbennun Jan 4, 2024
6fe9787
Minor fixes and adding tests
tbennun Jan 4, 2024
5eac791
Fix memlet propagation for undefined symbols, add stree tests
tbennun Jan 4, 2024
5a97334
Fix test
tbennun Jan 5, 2024
68237c9
Merge branch 'main' into stree-to-sdfg
tbennun Nov 18, 2024
553d430
Update test
tbennun Nov 20, 2024
00f05e6
Make sure CI runs for v1 maintenance branch PRs (#1810)
phschaad Dec 5, 2024
3466973
Unused imports backport (successor to #1808) (#1816)
phschaad Dec 7, 2024
41e64d4
Cherry pick regression fix from PR #1837
tbennun Jan 14, 2025
bb5fd17
scal2sym: Fix incorrect dimensionality in indirection removal (#1871)
tbennun Jan 14, 2025
03c9222
Fix Ubuntu version for maintenance branch
tbennun Jan 14, 2025
a3cd17b
Bump version to 1.0.1
tbennun Jan 23, 2025
117dc3a
Fix typos (backport) (#1918)
romanc Feb 2, 2025
56ab279
Fix typo (#1945)
romanc Feb 20, 2025
6e2585b
Fix: DDE removing read from access_set in read/write nodes (#1955)
romanc Mar 5, 2025
11d0e33
`StateFusion` misses read-write conflict due to early return (#1954)
FlorianDeconinck Mar 5, 2025
34f081a
Merge branch 'v1/maintenance' into romanc/stree-to-sdfg
romanc Mar 18, 2025
fff6010
Generate two states from state boundary node
romanc Mar 18, 2025
efdf839
WIP: use visitor to generate SDFG
romanc Mar 19, 2025
13402cb
Bump version to 1.0.2
tbennun Mar 20, 2025
45b4125
WIP: visit_ForScope done
romanc Mar 20, 2025
35cfd2b
WIP: visit_WhileScope done
romanc Mar 21, 2025
c1330d8
WIP: started working on conditional blocks
romanc Mar 21, 2025
e33a5d9
WIP: conditional blocks done
romanc Mar 24, 2025
937dc79
WIP: visit_AssignNode done
romanc Mar 24, 2025
38e4bf7
WIP: Add stub for MapScopes and the remaining nodes
romanc Mar 24, 2025
aae267e
unrelated: format schedule_tree/treenodes.py
romanc Mar 25, 2025
0477cc7
WIP: visit_MapScope done
romanc Mar 25, 2025
b6a5958
WIP: use nested sdfg inside map for state machine
romanc Mar 26, 2025
a623583
Add options to simplify/validate generated SDFG
romanc Mar 28, 2025
9ec3221
Unrelated: fix typo
romanc Apr 9, 2025
214eef1
Fix reading twice from the same memlet in map scope
romanc Apr 9, 2025
a864245
Fix: don't duplicate data descriptors
romanc Apr 9, 2025
c31c420
Fix: find if scope in parent's children with is operator
romanc Apr 9, 2025
c0a9db5
Fix: symbol assignment on edges
romanc Apr 9, 2025
8588a42
Fix passthrough connectors for multiple reads
romanc Apr 9, 2025
3d3f9c4
WIP (not working): iterating on map creation
romanc Apr 10, 2025
5b6c1b6
WIP: maps without state boundaries are now good again
romanc Apr 11, 2025
4922784
WIP: working map scopes with state boundaries
romanc Apr 11, 2025
e85c97a
WIP: fixed isssue with edge assignments
romanc Apr 11, 2025
1bae7fa
Unrelated: fix typos
romanc Apr 15, 2025
7d46d2d
WIP: memlet propagation seems to work like this (doesn't validate)
romanc Apr 15, 2025
ed5e48e
WIP: delete unreachable/commented code
romanc Apr 15, 2025
6570682
XPPM test generates an SDFG that validates \o/
romanc Apr 16, 2025
45d43c6
fixup: minor cleanups after passing xxpm test
romanc Apr 16, 2025
dab3fa7
Avoid "passthrough write nodes"
romanc Apr 23, 2025
bf5b9e1
Allow nested SDFGs inside other. Add CopyNode translation
romanc Apr 23, 2025
aa613dd
[to be reverted] test_delnflux_tmp
romanc Apr 24, 2025
9014752
Fix syntax issue (unexpected keyword arg "memlet")
romanc Apr 24, 2025
f775b17
Todo: write Nview visitor (for Fillz, Ray_Fast)
romanc Apr 25, 2025
be05ba5
"back propagate" state boundaries from nested SDFGs through maps
romanc May 5, 2025
c2e53c3
WIP: This seems to fix DelnFlux
romanc May 6, 2025
4accdb9
WIP: This seems to fix FvTp2d
romanc May 6, 2025
98071f7
WIP: force boundary after assigns, timings
romanc May 7, 2025
b4a9ecd
Perf: cache if one subset is contained in another
romanc May 8, 2025
4c60fdd
tmp: just added a couple roundtrip tests
romanc May 8, 2025
a14103d
Remove unnecessary symbols from the schedule tree descriptor repository
tbennun May 12, 2025
806aa5e
WIP: AssignNode array access inside nested SDFG
romanc May 12, 2025
40c00ea
Unrelated: Fixing typos in comments
romanc May 14, 2025
45e5d04
Go back to just simplify. Can't repo the problem anymore.
romanc May 15, 2025
04d442b
Unrelated: return empty set, not dict
romanc May 15, 2025
3ae906f
Backport: Memlet propagation: return set - as promised (#2008)
romanc May 16, 2025
954c088
[florian] fix type of symbols directory
romanc Jun 6, 2025
8d710cd
unrelated: memlet propagation with indices
romanc Jun 6, 2025
13ff3ec
Unrelated: subset intersection between ranges and indices
romanc Jun 6, 2025
f78ea5d
Stree to SDFG: allow to configure simplify()
romanc Jun 13, 2025
8a76ed8
Fixup: simplify sdfg, not stree
romanc Jun 13, 2025
89efd9a
Nested SDFGs inside maps inherit their schedule
romanc Jun 17, 2025
b9bcc3d
Don't loose symbols in tswds
romanc Jun 18, 2025
b29a263
Only report cycles if we actually find them
romanc Jun 18, 2025
a0d9125
Only report cycles if we found some (#2055)
romanc Jun 19, 2025
439b929
Avoid losing symbols in `traverse_sdfg_with_defined_symbols` (#2054)
romanc Jun 25, 2025
1bf8411
Move main visitor out and remove print statements
romanc Jul 8, 2025
3a38524
Fix networkx state space explosion for cycle detection in state propa…
phschaad Jul 9, 2025
6b26c1a
Merge branch 'v1/maintenance' into romanc/stree-to-sdfg
FlorianDeconinck Jul 10, 2025
a180a7e
Testing per scope access cache
romanc Jul 14, 2025
f014559
Cache per state & scope. Special case for write after read after write
romanc Jul 14, 2025
3af927f
WIP: revert special case for cached node is input
romanc Jul 15, 2025
37c5696
Fix write access caching (hopefully)
romanc Jul 15, 2025
6ed3890
Patch: DeadDataflowElimination can't inline pointers into Tasklets
romanc Jul 16, 2025
82541a9
Add support for NView nodes (+ minimal cleanup)
romanc Jul 22, 2025
bd9a047
NView support: second half
romanc Jul 25, 2025
7882153
Backport of #2098 for v1/maintenance (#2099)
phschaad Jul 26, 2025
74333ac
Re-add support for CopyNodes
romanc Aug 6, 2025
eb160ce
Mitigation: don't inline nested SDFG without unique connector names
romanc Aug 19, 2025
77dc937
fix: support de-aliasing SDFGs with in/out conns of the same name
romanc Aug 19, 2025
54c669e
fix: de-allocate arrays that are move from stack to heap
romanc Aug 20, 2025
830ffa5
Fix: delete array moved to heap (backport to `v1/maintenance`) (#2135)
romanc Sep 3, 2025
1810f49
Backport Disabling Failing FPGA tests (#2171)
phschaad Oct 9, 2025
c2e952f
Backport of #2165 (#2166)
phschaad Oct 10, 2025
77faa3d
Merge remote-tracking branch 'origin/v1/maintenance' into romanc/stre…
romanc Oct 14, 2025
1033dfc
Unrelated: fix a bunch of typos
romanc Oct 14, 2025
4c042ee
Backport fix from #1853 and support `dace.map` syntax for struct fiel…
tbennun Oct 29, 2025
976a8f1
Merge remote-tracking branch 'origin/v1/maintenance' into romanc/stre…
romanc Nov 3, 2025
22f9a30
limit networkx version <= 3.5
edopao Nov 25, 2025
4a9f460
Merge pull request #13 from GridTools/cartesian-networkx_version
FlorianDeconinck Nov 25, 2025
aa5f8b5
[v1 Maintenance] `networkx` under 3.6 (#2258)
FlorianDeconinck Dec 23, 2025
c7b5a10
[v1 Maintenance] Back port `MapExpansion` fixes from v2 (#2257)
FlorianDeconinck Dec 25, 2025
2ef9437
Merge remote-tracking branch 'origin/v1/maintenance' into romanc/stre…
romanc Jan 8, 2026
35144d9
Merge branch 'main' into romanc/stree-v2
romanc Jan 8, 2026
e85315c
cleanup after merging main
romanc Jan 8, 2026
163f7fd
running pre-commit
romanc Jan 8, 2026
32201be
fix import errors in tests
romanc Jan 9, 2026
e9ecf5c
remove duplicate code
romanc Jan 9, 2026
4631de6
fixup: fixing bad merge in sdfg_to_stree.py
romanc Jan 9, 2026
8cf485f
Merge remote-tracking branch 'origin/main' into romanc/stree-v2
romanc Jan 12, 2026
14b8fc0
WIP: re-introduce ForScope, WhileScope and DoWhileScope
romanc Jan 12, 2026
d3e615f
WIP: more work understanding LoopRegions and how to work with them
romanc Jan 13, 2026
abbac85
WIP: memlet propagation for simple cases
romanc Jan 14, 2026
509da80
wip: work in progress towards building a valid stree
romanc Jan 15, 2026
db1c1b8
WIP: fix parent/children relationship in scope nodes
romanc Jan 19, 2026
d817afe
fix memlet propagation for indices
romanc Jan 19, 2026
ecd0c2d
Merge remote-tracking branch 'origin/main' into romanc/stree-v2
romanc Jan 19, 2026
1729ddd
easy fix for some tests
romanc Jan 19, 2026
ee96f67
Explicit constructors for treenodes / simple test for children of scopes
romanc Jan 22, 2026
f81abc9
WIP: a bit of cleanup and the first new-style tests
romanc Jan 22, 2026
8e3cc90
WIP: use ConditionalBlock for if/else scopes
romanc Jan 23, 2026
6bba45c
WIP: Add support for basic ForScopes
romanc Jan 26, 2026
32d66d9
WIP: support for simple while-loops
romanc Jan 26, 2026
513038c
WIP: fix issues with nested SDFGs
romanc Jan 26, 2026
cd474d2
WIP: fix tests
romanc Jan 26, 2026
492061f
fix: add loops to sdfg
romanc Jan 27, 2026
0da5e3a
Fix nested if statements inside (triple) loop
romanc Jan 27, 2026
cff050a
fix broken sdfg.array access in loops
romanc Jan 28, 2026
96f747f
Merge remote-tracking branch 'origin/main' into romanc/stree-v2
Jan 28, 2026
aa1e4f4
propagate sdfg name into stree name (and back)
romanc Jan 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 128 additions & 42 deletions dace/sdfg/analysis/schedule_tree/sdfg_to_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def dealias_sdfg(sdfg: SDFG):

replacements: Dict[str, str] = {}
inv_replacements: Dict[str, List[str]] = {}
parent_edges: Dict[str, Memlet] = {}
parent_edges_inputs: Dict[str, Memlet] = {}
parent_edges_outputs: Dict[str, Memlet] = {}
to_unsqueeze: Set[str] = set()

parent_sdfg = nsdfg.parent_sdfg
Expand All @@ -53,19 +54,42 @@ def dealias_sdfg(sdfg: SDFG):
for name, desc in nsdfg.arrays.items():
if desc.transient:
continue
for edge in parent_state.edges_by_connector(parent_node, name):
for edge in parent_state.in_edges_by_connector(parent_node, name):
parent_name = edge.data.data
assert parent_name in parent_sdfg.arrays
if parent_name != name:
if name != parent_name:
parent_edges_inputs[name] = edge
replacements[name] = parent_name
parent_edges[name] = edge
if parent_name in inv_replacements:
inv_replacements[parent_name].append(name)
to_unsqueeze.add(parent_name)
else:
inv_replacements[parent_name] = [name]
# We found an incoming edge for name and we don't expect a second one.
break

for edge in parent_state.out_edges_by_connector(parent_node, name):
parent_name = edge.data.data
assert parent_name in parent_sdfg.arrays
if name != parent_name:
parent_edges_outputs[name] = edge

if replacements.get(name, None) is not None:
# There's an incoming and an outgoing connector with the same name.
# Make sure both map to the same memory in the parent sdfg
assert replacements[name] == parent_name
assert name in inv_replacements[parent_name]
break
else:
replacements[name] = parent_name
if parent_name in inv_replacements:
inv_replacements[parent_name].append(name)
to_unsqueeze.add(parent_name)
else:
inv_replacements[parent_name] = [name]
# We found an outgoing edge for name and we don't expect a second one.
break

if to_unsqueeze:
for parent_name in to_unsqueeze:
parent_arr = parent_sdfg.arrays[parent_name]
Expand Down Expand Up @@ -94,14 +118,18 @@ def dealias_sdfg(sdfg: SDFG):
# destination subset
if isinstance(src, nd.AccessNode) and src.data in child_names:
src_data = src.data
new_src_memlet = unsqueeze_memlet(e.data, parent_edges[src.data].data, use_src_subset=True)
new_src_memlet = unsqueeze_memlet(e.data,
parent_edges_inputs[src.data].data,
use_src_subset=True)
else:
src_data = None
new_src_memlet = None
# We need to take directionality of the memlet into account
if isinstance(dst, nd.AccessNode) and dst.data in child_names:
dst_data = dst.data
new_dst_memlet = unsqueeze_memlet(e.data, parent_edges[dst.data].data, use_dst_subset=True)
new_dst_memlet = unsqueeze_memlet(e.data,
parent_edges_outputs[dst.data].data,
use_dst_subset=True)
else:
dst_data = None
new_dst_memlet = None
Expand All @@ -120,23 +148,26 @@ def dealias_sdfg(sdfg: SDFG):
syms = e.data.read_symbols()
for memlet in e.data.get_read_memlets(nsdfg.arrays):
if memlet.data in child_names:
repl_dict[str(memlet)] = unsqueeze_memlet(memlet, parent_edges[memlet.data].data)
repl_dict[str(memlet)] = unsqueeze_memlet(memlet, parent_edges_inputs[memlet.data].data)
if memlet.data in syms:
syms.remove(memlet.data)
for s in syms:
if s in parent_edges:
if s in parent_edges_inputs:
if s in nsdfg.arrays:
repl_dict[s] = parent_edges[s].data.data
repl_dict[s] = parent_edges_inputs[s].data.data
else:
repl_dict[s] = str(parent_edges[s].data)
repl_dict[s] = str(parent_edges_inputs[s].data)
e.data.replace_dict(repl_dict)
for name in child_names:
edge = parent_edges[name]
for e in parent_state.memlet_tree(edge):
if e.data.data == parent_name:
e.data.subset = subsets.Range.from_array(parent_arr)
else:
e.data.other_subset = subsets.Range.from_array(parent_arr)
for edge in [parent_edges_inputs.get(name, None), parent_edges_outputs.get(name, None)]:
if edge is None:
continue

for e in parent_state.memlet_tree(edge):
if e.data.data == parent_name:
e.data.subset = subsets.Range.from_array(parent_arr)
else:
e.data.other_subset = subsets.Range.from_array(parent_arr)

if replacements:
struct_outside_replacements: Dict[str, str] = {}
Expand Down Expand Up @@ -523,6 +554,10 @@ def _state_schedule_tree(state: SDFGState) -> List[tn.ScheduleTreeNode]:
result = subnodes
elif isinstance(node, dace.nodes.ExitNode):
result = scopes.pop()
parent = result[-1]
assert isinstance(parent, tn.ScheduleTreeScope)
for child in parent.children:
child.parent = parent
elif isinstance(node, dace.nodes.NestedSDFG):
nested_array_mapping_input = {}
nested_array_mapping_output = {}
Expand Down Expand Up @@ -565,6 +600,12 @@ def _state_schedule_tree(state: SDFGState) -> List[tn.ScheduleTreeNode]:
# Insert the nested SDFG flattened
nested_stree = as_schedule_tree(node.sdfg, in_place=True, toplevel=False)
result.extend(nested_stree.children)

if generated_nviews:
# Insert matching NViewEnd nodes to define the scope NView nodes.
for target in generated_nviews:
result.append(tn.NViewEnd(target=target))

elif isinstance(node, dace.nodes.Tasklet):
in_memlets = {e.dst_conn: e.data for e in state.in_edges(node) if e.dst_conn}
out_memlets = {e.src_conn: e.data for e in state.out_edges(node) if e.src_conn}
Expand Down Expand Up @@ -664,11 +705,24 @@ def _block_schedule_tree(block: ControlFlowBlock) -> List[tn.ScheduleTreeNode]:
pivot = None

if isinstance(block, LoopRegion):
# If this is a loop region, wrap everything in a LoopScope node.
loop_node = tn.LoopScope(loop=block, children=children)
return [loop_node]
# If this is a loop region, wrap everything in a loop scope node.
variant = tn.loop_variant(block)
if variant == "for":
return [tn.ForScope(loop=block, children=children)]

if variant == "while":
return [tn.WhileScope(loop=block, children=children)]

if variant == "do-while":
return [tn.DoWhileScope(loop=block, children=children)]

# If we end up here, we don't need more granularity and just use
# a general loop scope
return [tn.LoopScope(loop=block, children=children)]

return children
elif isinstance(block, ConditionalBlock):

if isinstance(block, ConditionalBlock):
result: List[tn.ScheduleTreeNode] = []
if_node = tn.IfScope(condition=block.branches[0][0], children=_block_schedule_tree(block.branches[0][1]))
result.append(if_node)
Expand All @@ -680,18 +734,20 @@ def _block_schedule_tree(block: ControlFlowBlock) -> List[tn.ScheduleTreeNode]:
else_node = tn.ElseScope(children=_block_schedule_tree(branch_body))
result.append(else_node)
return result
elif isinstance(block, SDFGState):

if isinstance(block, SDFGState):
return _state_schedule_tree(block)
elif isinstance(block, ReturnBlock):

if isinstance(block, ReturnBlock):
# For return blocks, add a goto node to the end of the schedule tree.
# NOTE: Return blocks currently always exit the entire SDFG context they are contained in, meaning that the exit
# goto has target=None. However, in the future we want to adapt Return blocks to be able to return only a
# function region, if contained inside of one - in which case the target will need to be changed to NOT exit the
# entire SDFG.
goto_node = tn.GotoNode(target=None)
return [goto_node]
else:
raise tn.UnsupportedScopeException(type(block).__name__)

raise tn.UnsupportedScopeException(type(block).__name__)


def _generate_views_in_scope(
Expand Down Expand Up @@ -725,7 +781,46 @@ def _generate_views_in_scope(
return result


def as_schedule_tree(sdfg: SDFG, in_place: bool = False, toplevel: bool = True) -> tn.ScheduleTreeScope:
def _prepare_sdfg_for_conversion(sdfg: SDFG, *, toplevel: bool) -> None:
from dace.transformation import helpers as xfh # Avoid import loop

# Split edges with assignments and conditions
xfh.split_interstate_edges(sdfg)

# Replace code->code edges with data<->code edges
xfh.replace_code_to_code_edges(sdfg)

if toplevel: # Top-level SDFG preparation (only perform once)
# Handle name collisions (in arrays, state labels, symbols)
remove_name_collisions(sdfg)

# Ensure no arrays alias in SDFG tree
dealias_sdfg(sdfg)


def _create_unified_descriptor_repository(sdfg: SDFG, stree: tn.ScheduleTreeRoot):
"""
Creates a single descriptor repository from an SDFG and all nested SDFGs. This includes
data containers, symbols, constants, etc.
:param sdfg: The top-level SDFG to create the repository from.
:param stree: The tree root in which to make the unified descriptor repository.
"""
stree.containers = sdfg.arrays
stree.symbols = sdfg.symbols
stree.constants = sdfg.constants_prop

# Since the SDFG is assumed to be de-aliased and contain unique names, we union the contents of
# the nested SDFGs' descriptor repositories
for nsdfg in sdfg.all_sdfgs_recursive():
transients = {k: v for k, v in nsdfg.arrays.items() if v.transient}
symbols = {k: v for k, v in nsdfg.symbols.items() if k not in stree.symbols}
constants = {k: v for k, v in nsdfg.constants_prop.items() if k not in stree.constants}
stree.containers.update(transients)
stree.symbols.update(symbols)
stree.constants.update(constants)


def as_schedule_tree(sdfg: SDFG, *, in_place: bool = False, toplevel: bool = True) -> tn.ScheduleTreeRoot:
"""
Converts an SDFG into a schedule tree. The schedule tree is a tree of nodes that represent the execution order of
the SDFG.
Expand All @@ -741,30 +836,21 @@ def as_schedule_tree(sdfg: SDFG, in_place: bool = False, toplevel: bool = True)
usable after the conversion if ``in_place`` is True!
:return: A schedule tree representing the given SDFG.
"""
from dace.transformation import helpers as xfh # Avoid import loop

if not in_place:
sdfg = copy.deepcopy(sdfg)

# Prepare SDFG for conversion
#############################

# Split edges with assignments and conditions
xfh.split_interstate_edges(sdfg)

# Replace code->code edges with data<->code edges
xfh.replace_code_to_code_edges(sdfg)

if toplevel: # Top-level SDFG preparation (only perform once)
# Handle name collisions (in arrays, state labels, symbols)
remove_name_collisions(sdfg)
# Ensure no arrays alias in SDFG tree
dealias_sdfg(sdfg)
_prepare_sdfg_for_conversion(sdfg, toplevel=toplevel)

#############################
if toplevel:
result = tn.ScheduleTreeRoot(name=sdfg.name, children=[])
_create_unified_descriptor_repository(sdfg, result)
result.add_children(_block_schedule_tree(sdfg))
else:
result = tn.ScheduleTreeScope(children=_block_schedule_tree(sdfg))

result = tn.ScheduleTreeScope(children=_block_schedule_tree(sdfg))
tn.validate_has_no_other_node_types(result)
tn.validate_children_and_parents_align(result, root=toplevel)

# Clean up tree
stpasses.remove_unused_and_duplicate_labels(result)
Expand Down
Loading