Bug in `AddThreadBlockMap` with non-standard Memlets
- Dominant language
- Python
- Stars
- 593
- Forks
- 163
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 60
Description
I found an issue in `AddThreadBlockMap`.
The issue has to do with "splitting Memlets", i.e. there is one Memlet that passes an AccessNode into a (GPU) Map and then multiple Memlets that read from it.
The issue is if the `.data` attribute of the different Memlets do not refer to the data of the AccessNode on the outside (that this only happens if the Memlet leads to an AccessNode on the inside).
If `AddThreadBlockMap` is run in that case it generates `InvalidSDFGNodeError: Connector 'IN_a1' cannot have more than one incoming edge, found 2 (at state block, node comp[__i=b___i:Min(9, b___i + 31) + 1])`
Note in the added reproducer this is hard to see because there are multiple edges on top of each other.
Reproducer:
```python
import dace
import sys
def make_sdfg():
sdfg = dace.SDFG("test")
state = sdfg.add_state(is_start_block=True)
for aname in "ab":
sdfg.add_array(
aname,
shape=(10, 2),
dtype=dace.float64,
storage=dace.dtypes.StorageType.GPU_Global,
transient=False,
)
sdfg.add_scalar(
"s",
dtype=dace.float64,
transient=True,
)
a, b, s = (state.add_access(name) for name in "abs")
me, mx = state.add_map("comp", ndrange={"__i": "0:10"}, schedule=dace.dtypes.ScheduleType.GPU_Device)
tlet = state.add_tasklet(
"tlet",
inputs={"__in"},
outputs={"__out"},
code="__out = __in + 1.0",
)
state.add_edge(
a,
None,
me,
"IN_a1",
dace.Memlet("a[0:10, 0]"),
)
state.add_edge(
me,
"OUT_a1",
tlet,
"__in",
dace.Memlet("a[__i, 0]"),
)
me.add_scope_connectors("a1")
state.add_edge(
tlet,
"__out",
mx,
"IN_b1",
dace.Memlet("b[__i, 0]"),
)
state.add_edge(
mx,
"OUT_b1",
b,
None,
dace.Memlet("b[0:10, 0]"),
)
mx.add_scope_connectors("b1")
state.add_edge(
me,
# It is also important that we read from the same as the tasklet.
"OUT_a1",
s,
None,
# According to my understanding the error is here, that the data of this
# Memlet refers to `s` instead of `a` as the outer data does.
dace.Memlet("s[0] -> [__i, 0]"),
)
state.add_edge(
s,
None,
mx,
"IN_b2",
dace.Memlet("b[__i, 1] -> [0]"),
)
state.add_edge(
mx,
"OUT_b2",
b,
None,
dace.Memlet("b[0:10, 1]"),
)
mx.add_scope_connectors("b2")
sdfg.validate()
return sdfg
def main():
from dace.transformation.dataflow.add_threadblock_map import AddThreadBlockMap
sdfg = make_sdfg()
sdfg.apply_transformations_once_everywhere(
AddThreadBlockMap,
validate=True,
validate_all=True,
)
sys.exit(0)
if "__main__" == __name__:
main()
```
Contributor guide
Research direction
Start with the provided Python reproducer and the AddThreadBlockMap entry point in dace/transformation/dataflow/add_threadblock_map.py. Run it with validation enabled, then inspect how split Memlets are handled when their data attributes differ from the outer AccessNode. Done means the reproducer completes without InvalidSDFGNodeError and SDFG validation passes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- hpc
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100