Skip to content

Commit

Permalink
Merge branch 'master' into mpi4py_dev
Browse files Browse the repository at this point in the history
  • Loading branch information
alexnick83 authored Sep 7, 2023
2 parents a0259f7 + f95f816 commit 69213b3
Show file tree
Hide file tree
Showing 26 changed files with 2,563 additions and 91 deletions.
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
77 changes: 58 additions & 19 deletions dace/codegen/control_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ class ControlFlow:
# a string with its generated code.
dispatch_state: Callable[[SDFGState], str]

# The parent control flow block of this one, used to avoid generating extraneous ``goto``s
parent: Optional['ControlFlow']

@property
def first_state(self) -> SDFGState:
"""
Expand Down Expand Up @@ -222,11 +225,18 @@ def as_cpp(self, codegen, symbols) -> str:
out_edges = sdfg.out_edges(elem.state)
for j, e in enumerate(out_edges):
if e not in self.gotos_to_ignore:
# If this is the last generated edge and it leads
# to the next state, skip emitting goto
# Skip gotos to immediate successors
successor = None
if (j == (len(out_edges) - 1) and (i + 1) < len(self.elements)):
successor = self.elements[i + 1].first_state
# If this is the last generated edge
if j == (len(out_edges) - 1):
if (i + 1) < len(self.elements):
# If last edge leads to next state in block
successor = self.elements[i + 1].first_state
elif i == len(self.elements) - 1:
# If last edge leads to first state in next block
next_block = _find_next_block(self)
if next_block is not None:
successor = next_block.first_state

expr += elem.generate_transition(sdfg, e, successor)
else:
Expand Down Expand Up @@ -478,13 +488,14 @@ def children(self) -> List[ControlFlow]:

def _loop_from_structure(sdfg: SDFG, guard: SDFGState, enter_edge: Edge[InterstateEdge],
leave_edge: Edge[InterstateEdge], back_edges: List[Edge[InterstateEdge]],
dispatch_state: Callable[[SDFGState], str]) -> Union[ForScope, WhileScope]:
dispatch_state: Callable[[SDFGState],
str], parent_block: GeneralBlock) -> Union[ForScope, WhileScope]:
"""
Helper method that constructs the correct structured loop construct from a
set of states. Can construct for or while loops.
"""

body = GeneralBlock(dispatch_state, [], [], [], [], [], True)
body = GeneralBlock(dispatch_state, parent_block, [], [], [], [], [], True)

guard_inedges = sdfg.in_edges(guard)
increment_edges = [e for e in guard_inedges if e in back_edges]
Expand Down Expand Up @@ -535,10 +546,10 @@ def _loop_from_structure(sdfg: SDFG, guard: SDFGState, enter_edge: Edge[Intersta
# Also ignore assignments in increment edge (handled in for stmt)
body.assignments_to_ignore.append(increment_edge)

return ForScope(dispatch_state, itvar, guard, init, condition, update, body, init_edges)
return ForScope(dispatch_state, parent_block, itvar, guard, init, condition, update, body, init_edges)

# Otherwise, it is a while loop
return WhileScope(dispatch_state, guard, condition, body)
return WhileScope(dispatch_state, parent_block, guard, condition, body)


def _cases_from_branches(
Expand Down Expand Up @@ -617,6 +628,31 @@ def _child_of(node: SDFGState, parent: SDFGState, ptree: Dict[SDFGState, SDFGSta
return False


def _find_next_block(block: ControlFlow) -> Optional[ControlFlow]:
"""
Returns the immediate successor control flow block.
"""
# Find block in parent
parent = block.parent
if parent is None:
return None
ind = next(i for i, b in enumerate(parent.children) if b is block)
if ind == len(parent.children) - 1 or isinstance(parent, (IfScope, IfElseChain, SwitchCaseScope)):
# If last block, or other children are not reachable from current node (branches),
# recursively continue upwards
return _find_next_block(parent)
return parent.children[ind + 1]


def _reset_block_parents(block: ControlFlow):
"""
Fixes block parents after processing.
"""
for child in block.children:
child.parent = block
_reset_block_parents(child)


def _structured_control_flow_traversal(sdfg: SDFG,
start: SDFGState,
ptree: Dict[SDFGState, SDFGState],
Expand Down Expand Up @@ -645,7 +681,7 @@ def _structured_control_flow_traversal(sdfg: SDFG,
"""

def make_empty_block():
return GeneralBlock(dispatch_state, [], [], [], [], [], True)
return GeneralBlock(dispatch_state, parent_block, [], [], [], [], [], True)

# Traverse states in custom order
visited = set() if visited is None else visited
Expand All @@ -657,7 +693,7 @@ def make_empty_block():
if node in visited or node is stop:
continue
visited.add(node)
stateblock = SingleState(dispatch_state, node)
stateblock = SingleState(dispatch_state, parent_block, node)

oe = sdfg.out_edges(node)
if len(oe) == 0: # End state
Expand Down Expand Up @@ -708,23 +744,25 @@ def make_empty_block():
if (len(oe) == 2 and oe[0].data.condition_sympy() == sp.Not(oe[1].data.condition_sympy())):
# If without else
if oe[0].dst is mergestate:
branch_block = IfScope(dispatch_state, sdfg, node, oe[1].data.condition, cblocks[oe[1]])
branch_block = IfScope(dispatch_state, parent_block, sdfg, node, oe[1].data.condition,
cblocks[oe[1]])
elif oe[1].dst is mergestate:
branch_block = IfScope(dispatch_state, sdfg, node, oe[0].data.condition, cblocks[oe[0]])
branch_block = IfScope(dispatch_state, parent_block, sdfg, node, oe[0].data.condition,
cblocks[oe[0]])
else:
branch_block = IfScope(dispatch_state, sdfg, node, oe[0].data.condition, cblocks[oe[0]],
cblocks[oe[1]])
branch_block = IfScope(dispatch_state, parent_block, sdfg, node, oe[0].data.condition,
cblocks[oe[0]], cblocks[oe[1]])
else:
# If there are 2 or more edges (one is not the negation of the
# other):
switch = _cases_from_branches(oe, cblocks)
if switch:
# If all edges are of form "x == y" for a single x and
# integer y, it is a switch/case
branch_block = SwitchCaseScope(dispatch_state, sdfg, node, switch[0], switch[1])
branch_block = SwitchCaseScope(dispatch_state, parent_block, sdfg, node, switch[0], switch[1])
else:
# Otherwise, create if/else if/.../else goto exit chain
branch_block = IfElseChain(dispatch_state, sdfg, node,
branch_block = IfElseChain(dispatch_state, parent_block, sdfg, node,
[(e.data.condition, cblocks[e] if e in cblocks else make_empty_block())
for e in oe])
# End of branch classification
Expand All @@ -739,11 +777,11 @@ def make_empty_block():
loop_exit = None
scope = None
if ptree[oe[0].dst] == node and ptree[oe[1].dst] != node:
scope = _loop_from_structure(sdfg, node, oe[0], oe[1], back_edges, dispatch_state)
scope = _loop_from_structure(sdfg, node, oe[0], oe[1], back_edges, dispatch_state, parent_block)
body_start = oe[0].dst
loop_exit = oe[1].dst
elif ptree[oe[1].dst] == node and ptree[oe[0].dst] != node:
scope = _loop_from_structure(sdfg, node, oe[1], oe[0], back_edges, dispatch_state)
scope = _loop_from_structure(sdfg, node, oe[1], oe[0], back_edges, dispatch_state, parent_block)
body_start = oe[1].dst
loop_exit = oe[0].dst

Expand Down Expand Up @@ -836,7 +874,8 @@ def structured_control_flow_tree(sdfg: SDFG, dispatch_state: Callable[[SDFGState
if len(common_frontier) == 1:
branch_merges[state] = next(iter(common_frontier))

root_block = GeneralBlock(dispatch_state, [], [], [], [], [], True)
root_block = GeneralBlock(dispatch_state, None, [], [], [], [], [], True)
_structured_control_flow_traversal(sdfg, sdfg.start_state, ptree, branch_merges, back_edges, dispatch_state,
root_block)
_reset_block_parents(root_block)
return root_block
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('.', '->')

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:]
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('.', '->')

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('.', '->')

if fpga.is_fpga_array(desc):
decouple_array_interfaces = Config.get_bool("compiler", "xilinx", "decouple_array_interfaces")
Expand Down
Loading

0 comments on commit 69213b3

Please sign in to comment.