spcl / spcl/dace

Bug in `AddThreadBlockMap` with non-standard Memlets

Open
#2,220 0 comments 0 reactions 0 assignees View on GitHub
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).

Image

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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.