Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested Data Proposal (SDFG only) #1324

Merged
merged 46 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
57abd28
Added NestedDataClassProperty for nested data.
alexnick83 Jul 18, 2023
09465d2
Added Structures and StructArrays.
alexnick83 Jul 18, 2023
51776a1
Break array lengths down to their symbolic tokents.
alexnick83 Jul 18, 2023
b23ed86
Allow structures to have fields whose name doesn't start with undersc…
alexnick83 Jul 18, 2023
777821f
Structures now have a "members" dictionary. Their dtype is a pointer …
alexnick83 Jul 19, 2023
ebf7206
dtype.structs store their ctype in `_FFI_CTYPES`.
alexnick83 Jul 19, 2023
c52a482
Reverted underscore exception for Structures.
alexnick83 Jul 19, 2023
40cc858
Small fixes.
alexnick83 Jul 19, 2023
dd73aaa
WIP: Replace ',' with '->' to quickly support nested data.
alexnick83 Jul 19, 2023
623a7f8
Recursively add to arglist nested data descriptors.
alexnick83 Jul 19, 2023
1e5badd
Recursively look into nested data to emit definitions.
alexnick83 Jul 19, 2023
36d4e82
SDFG data (_arrays) are now stored in a NestedDict.
alexnick83 Jul 19, 2023
38a4265
Adjusted the matching check for memlet data and src/dst nodes to not …
alexnick83 Jul 19, 2023
479cb2a
Added tests.
alexnick83 Jul 19, 2023
8365ab3
Serialization fixes.
alexnick83 Jul 19, 2023
14ba665
Fixed NestedDict for non-str keys.
alexnick83 Jul 19, 2023
80d6f10
Added support for transient Structures.
alexnick83 Jul 20, 2023
9658c22
Edited tests.
alexnick83 Jul 20, 2023
b1dbb6b
Structures have name attribute (instead of subclassing).
alexnick83 Jul 20, 2023
5de2ae3
Updated tests.
alexnick83 Jul 20, 2023
1fbc45f
Removed nested data connectors.
alexnick83 Jul 20, 2023
6fa7e53
Added support for direct access to nested data.
alexnick83 Jul 20, 2023
71d7c3d
WIP: Add nested data free symbols to SDFG.
alexnick83 Jul 20, 2023
e0a4409
Added test for direct nested data access.
alexnick83 Jul 20, 2023
0593ea4
Added test for direct double-nested data accesses.
alexnick83 Jul 20, 2023
0df9c35
Added free-symbols and repr.
alexnick83 Jul 21, 2023
909c1aa
Recursively add free symbols from nested data.
alexnick83 Jul 21, 2023
e2b0d8b
Updated tests.
alexnick83 Jul 21, 2023
52afc72
Scrapped structure private symbols for now.
alexnick83 Jul 21, 2023
a0e3458
Merge branch 'master' into nested-data-sdfg
alexnick83 Jul 21, 2023
0924644
Updated tests.
alexnick83 Jul 21, 2023
8296a6d
Added setitem.
alexnick83 Jul 21, 2023
61d2ddc
Merge branch 'master' into nested-data-sdfg
alexnick83 Jul 27, 2023
a98fce0
Serialize Structure members and struct data/length as list of tuples.
alexnick83 Jul 27, 2023
f431a8d
Switched Structures and structs to OrderedDicts.
alexnick83 Jul 28, 2023
86d9cf2
Removed order from properties.
alexnick83 Jul 28, 2023
76d6266
`_argminmax` now creates a struct with the members ordered as accesse…
alexnick83 Jul 28, 2023
4c25648
Merge branch 'master' into nested-data-sdfg
alexnick83 Jul 28, 2023
9c97365
Merge branch 'master' into nested-data-sdfg
alexnick83 Aug 3, 2023
1cb9f9f
Added support for StructureViews.
alexnick83 Aug 17, 2023
5a2c460
Added tests for StructArrays.
alexnick83 Aug 17, 2023
230f799
Merge branch 'master' into nested-data-sdfg
alexnick83 Aug 17, 2023
f1b0c73
Fixed serialization.
alexnick83 Aug 17, 2023
c5889a4
Addressed comments.
alexnick83 Aug 21, 2023
b80be92
Merge branch 'master' into nested-data-sdfg
alexnick83 Aug 22, 2023
eabbd1d
Addressed comments.
alexnick83 Aug 22, 2023
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
14 changes: 8 additions & 6 deletions dace/codegen/compiled_sdfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,9 +452,10 @@ def _construct_args(self, kwargs) -> Tuple[Tuple[Any], Tuple[Any]]:
# GPU scalars are pointers, so this is fine
if atype.storage != dtypes.StorageType.GPU_Global:
raise TypeError('Passing an array to a scalar (type %s) in argument "%s"' % (atype.dtype.ctype, a))
elif not isinstance(atype, dt.Array) and not isinstance(atype.dtype, dtypes.callback) and not isinstance(
arg,
(atype.dtype.type, sp.Basic)) and not (isinstance(arg, symbolic.symbol) and arg.dtype == atype.dtype):
elif (not isinstance(atype, (dt.Array, dt.Structure)) and
not isinstance(atype.dtype, dtypes.callback) and
not isinstance(arg, (atype.dtype.type, sp.Basic)) and
not (isinstance(arg, symbolic.symbol) and arg.dtype == atype.dtype)):
if isinstance(arg, int) and atype.dtype.type == np.int64:
pass
elif isinstance(arg, float) and atype.dtype.type == np.float64:
Expand All @@ -472,7 +473,7 @@ def _construct_args(self, kwargs) -> Tuple[Tuple[Any], Tuple[Any]]:
else:
warnings.warn(f'Casting scalar argument "{a}" from {type(arg).__name__} to {atype.dtype.type}')
arglist[i] = atype.dtype.type(arg)
elif (isinstance(atype, dt.Array) and isinstance(arg, np.ndarray)
elif (isinstance(atype, dt.Array) and isinstance(arg, np.ndarray) and not isinstance(atype, dt.StructArray)
and atype.dtype.as_numpy_dtype() != arg.dtype):
# Make exception for vector types
if (isinstance(atype.dtype, dtypes.vector) and atype.dtype.vtype.as_numpy_dtype() == arg.dtype):
Expand Down Expand Up @@ -521,7 +522,7 @@ def _construct_args(self, kwargs) -> Tuple[Tuple[Any], Tuple[Any]]:
# Construct init args, which only consist of the symbols
symbols = self._free_symbols
initargs = tuple(
actype(arg) if (not isinstance(arg, ctypes._SimpleCData)) else arg
actype(arg) if not isinstance(arg, ctypes._SimpleCData) else arg
for arg, actype, atype, aname in callparams if aname in symbols)

# Replace arrays with their base host/device pointers
Expand All @@ -531,7 +532,8 @@ def _construct_args(self, kwargs) -> Tuple[Tuple[Any], Tuple[Any]]:

try:
newargs = tuple(
actype(arg) if (not isinstance(arg, ctypes._SimpleCData)) else arg for arg, actype, atype in newargs)
actype(arg) if not isinstance(arg, (ctypes._SimpleCData)) else arg
for arg, actype, atype in newargs)
except TypeError:
# Pinpoint bad argument
for i, (arg, actype, _) in enumerate(newargs):
Expand Down
4 changes: 2 additions & 2 deletions dace/codegen/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,11 +505,11 @@ def get_copy_dispatcher(self, src_node, dst_node, edge, sdfg, state):
dst_is_data = True

# Skip copies to/from views where edge matches
if src_is_data and isinstance(src_node.desc(sdfg), dt.View):
if src_is_data and isinstance(src_node.desc(sdfg), (dt.StructureView, dt.View)):
e = sdutil.get_view_edge(state, src_node)
if e is edge:
return None
if dst_is_data and isinstance(dst_node.desc(sdfg), dt.View):
if dst_is_data and isinstance(dst_node.desc(sdfg), (dt.StructureView, dt.View)):
e = sdutil.get_view_edge(state, dst_node)
if e is edge:
return None
Expand Down
4 changes: 4 additions & 0 deletions dace/codegen/targets/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,10 @@ def make_const(expr: str) -> str:
# Register defined variable
dispatcher.defined_vars.add(pointer_name, defined_type, typedef, allow_shadowing=True)

# NOTE: `expr` may only be a name or a sequence of names and dots. The latter indicates nested data and structures.
# NOTE: Since structures are implemented as pointers, we replace dots with arrows.
expr = expr.replace('.', '->')
alexnick83 marked this conversation as resolved.
Show resolved Hide resolved

return (typedef + ref, pointer_name, expr)


Expand Down
64 changes: 56 additions & 8 deletions dace/codegen/targets/cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,29 @@ def __init__(self, frame_codegen, sdfg):
# Keep track of generated NestedSDG, and the name of the assigned function
self._generated_nested_sdfg = dict()

# Keeps track of generated connectors, so we know how to access them in
# nested scopes
# NOTE: Multi-nesting with StructArrays must be further investigated.
def _visit_structure(struct: data.Structure, args: dict, prefix: str = ''):
for k, v in struct.members.items():
if isinstance(v, data.Structure):
_visit_structure(v, args, f'{prefix}.{k}')
elif isinstance(v, data.StructArray):
_visit_structure(v.stype, args, f'{prefix}.{k}')
elif isinstance(v, data.Data):
args[f'{prefix}.{k}'] = v

# Keeps track of generated connectors, so we know how to access them in nested scopes
arglist = dict(self._frame.arglist)
for name, arg_type in self._frame.arglist.items():
if isinstance(arg_type, data.Scalar):
if isinstance(arg_type, data.Structure):
desc = sdfg.arrays[name]
_visit_structure(arg_type, arglist, name)
elif isinstance(arg_type, data.StructArray):
desc = sdfg.arrays[name]
desc = desc.stype
_visit_structure(desc, arglist, name)

for name, arg_type in arglist.items():
if isinstance(arg_type, (data.Scalar, data.Structure)):
# GPU global memory is only accessed via pointers
# TODO(later): Fix workaround somehow
if arg_type.storage is dtypes.StorageType.GPU_Global:
Expand Down Expand Up @@ -195,9 +214,21 @@ def allocate_view(self, sdfg: SDFG, dfg: SDFGState, state_id: int, node: nodes.A
ancestor=0,
is_write=is_write)
if not declared:
declaration_stream.write(f'{atype} {aname};', sdfg, state_id, node)
ctypedef = dtypes.pointer(nodedesc.dtype).ctype
self._dispatcher.declared_arrays.add(aname, DefinedType.Pointer, ctypedef)
if isinstance(nodedesc, data.StructureView):
for k, v in nodedesc.members.items():
if isinstance(v, data.Data):
ctypedef = dtypes.pointer(v.dtype).ctype if isinstance(v, data.Array) else v.dtype.ctype
defined_type = DefinedType.Scalar if isinstance(v, data.Scalar) else DefinedType.Pointer
self._dispatcher.declared_arrays.add(f"{name}.{k}", defined_type, ctypedef)
self._dispatcher.defined_vars.add(f"{name}.{k}", defined_type, ctypedef)
# TODO: Find a better way to do this (the issue is with pointers of pointers)
if atype.endswith('*'):
atype = atype[:-1]
if value.startswith('&'):
value = value[1:]
tbennun marked this conversation as resolved.
Show resolved Hide resolved
declaration_stream.write(f'{atype} {aname};', sdfg, state_id, node)
allocation_stream.write(f'{aname} = {value};', sdfg, state_id, node)

def allocate_reference(self, sdfg: SDFG, dfg: SDFGState, state_id: int, node: nodes.AccessNode,
Expand Down Expand Up @@ -268,16 +299,19 @@ def allocate_array(self, sdfg, dfg, state_id, node, nodedesc, function_stream, d
name = node.data
alloc_name = cpp.ptr(name, nodedesc, sdfg, self._frame)
name = alloc_name
# NOTE: `expr` may only be a name or a sequence of names and dots. The latter indicates nested data and
# NOTE: structures. Since structures are implemented as pointers, we replace dots with arrows.
alloc_name = alloc_name.replace('.', '->')
alexnick83 marked this conversation as resolved.
Show resolved Hide resolved

if nodedesc.transient is False:
return

# Check if array is already allocated
if self._dispatcher.defined_vars.has(alloc_name):
if self._dispatcher.defined_vars.has(name):
return

# Check if array is already declared
declared = self._dispatcher.declared_arrays.has(alloc_name)
declared = self._dispatcher.declared_arrays.has(name)

define_var = self._dispatcher.defined_vars.add
if nodedesc.lifetime in (dtypes.AllocationLifetime.Persistent, dtypes.AllocationLifetime.External):
Expand All @@ -290,7 +324,18 @@ def allocate_array(self, sdfg, dfg, state_id, node, nodedesc, function_stream, d
if not isinstance(nodedesc.dtype, dtypes.opaque):
arrsize_bytes = arrsize * nodedesc.dtype.bytes

if isinstance(nodedesc, data.View):
if isinstance(nodedesc, data.Structure) and not isinstance(nodedesc, data.StructureView):
declaration_stream.write(f"{nodedesc.ctype} {name} = new {nodedesc.dtype.base_type};\n")
define_var(name, DefinedType.Pointer, nodedesc.ctype)
for k, v in nodedesc.members.items():
if isinstance(v, data.Data):
ctypedef = dtypes.pointer(v.dtype).ctype if isinstance(v, data.Array) else v.dtype.ctype
defined_type = DefinedType.Scalar if isinstance(v, data.Scalar) else DefinedType.Pointer
self._dispatcher.declared_arrays.add(f"{name}.{k}", defined_type, ctypedef)
self.allocate_array(sdfg, dfg, state_id, nodes.AccessNode(f"{name}.{k}"), v, function_stream,
declaration_stream, allocation_stream)
return
if isinstance(nodedesc, (data.StructureView, data.View)):
return self.allocate_view(sdfg, dfg, state_id, node, function_stream, declaration_stream, allocation_stream)
if isinstance(nodedesc, data.Reference):
return self.allocate_reference(sdfg, dfg, state_id, node, function_stream, declaration_stream,
Expand Down Expand Up @@ -455,7 +500,7 @@ def deallocate_array(self, sdfg, dfg, state_id, node, nodedesc, function_stream,
dtypes.AllocationLifetime.External)
self._dispatcher.declared_arrays.remove(alloc_name, is_global=is_global)

if isinstance(nodedesc, (data.Scalar, data.View, data.Stream, data.Reference)):
if isinstance(nodedesc, (data.Scalar, data.StructureView, data.View, data.Stream, data.Reference)):
return
elif (nodedesc.storage == dtypes.StorageType.CPU_Heap
or (nodedesc.storage == dtypes.StorageType.Register and symbolic.issymbolic(arrsize, sdfg.constants))):
Expand Down Expand Up @@ -1139,6 +1184,9 @@ def memlet_definition(self,
if not types:
types = self._dispatcher.defined_vars.get(ptr, is_global=True)
var_type, ctypedef = types
# NOTE: `expr` may only be a name or a sequence of names and dots. The latter indicates nested data and
# NOTE: structures. Since structures are implemented as pointers, we replace dots with arrows.
ptr = ptr.replace('.', '->')
tbennun marked this conversation as resolved.
Show resolved Hide resolved

if fpga.is_fpga_array(desc):
decouple_array_interfaces = Config.get_bool("compiler", "xilinx", "decouple_array_interfaces")
Expand Down
20 changes: 14 additions & 6 deletions dace/codegen/targets/framecode.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,23 @@ def generate_fileheader(self, sdfg: SDFG, global_stream: CodeIOStream, backend:
for _, arrname, arr in sdfg.arrays_recursive():
if arr is not None:
datatypes.add(arr.dtype)

def _emit_definitions(dtype: dtypes.typeclass, wrote_something: bool) -> bool:
if isinstance(dtype, dtypes.pointer):
wrote_something = _emit_definitions(dtype._typeclass, wrote_something)
elif isinstance(dtype, dtypes.struct):
for field in dtype.fields.values():
wrote_something = _emit_definitions(field, wrote_something)
if hasattr(dtype, 'emit_definition'):
if not wrote_something:
global_stream.write("", sdfg)
global_stream.write(dtype.emit_definition(), sdfg)
return wrote_something

# Emit unique definitions
wrote_something = False
for typ in datatypes:
if hasattr(typ, 'emit_definition'):
if not wrote_something:
global_stream.write("", sdfg)
wrote_something = True
global_stream.write(typ.emit_definition(), sdfg)
wrote_something = _emit_definitions(typ, wrote_something)
if wrote_something:
global_stream.write("", sdfg)

Expand Down Expand Up @@ -741,7 +749,7 @@ def determine_allocation_lifetime(self, top_sdfg: SDFG):
instances = access_instances[sdfg.sdfg_id][name]

# A view gets "allocated" everywhere it appears
if isinstance(desc, data.View):
if isinstance(desc, (data.StructureView, data.View)):
for s, n in instances:
self.to_allocate[s].append((sdfg, s, n, False, True, False))
self.to_allocate[s].append((sdfg, s, n, False, False, True))
Expand Down
Loading
Loading