Skip to content

Commit

Permalink
Merge branch 'master' into frontend-preprocessing-unnest-subscripts-a…
Browse files Browse the repository at this point in the history
…ttributes
  • Loading branch information
alexnick83 committed Sep 5, 2023
2 parents bb6f164 + f95f816 commit b609eb6
Show file tree
Hide file tree
Showing 68 changed files with 5,592 additions and 1,111 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ Reid Wahl
Yihang Luo
Alexandru Calotoiu
Phillip Lane
Samuel Martin

and other contributors listed in https://github.com/spcl/dace/graphs/contributors
3 changes: 2 additions & 1 deletion dace/cli/daceprof.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ def make_sequential(sdfg: dace.SDFG):
for n, _ in sdfg.all_nodes_recursive():
if isinstance(n, dace.nodes.EntryNode):
sched = getattr(n, 'schedule', False)
if sched == dace.ScheduleType.CPU_Multicore or sched == dace.ScheduleType.Default:
if sched in (dace.ScheduleType.CPU_Multicore, dace.ScheduleType.CPU_Persistent,
dace.ScheduleType.Default):
n.schedule = dace.ScheduleType.Sequential

registered.append(dace.hooks.register_sdfg_call_hook(before_hook=make_sequential))
Expand Down
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
17 changes: 9 additions & 8 deletions dace/codegen/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ class DefinedType(aenum.AutoNumberEnum):
:see: DefinedMemlets
"""
Pointer = ()
Scalar = ()
Stream = ()
StreamArray = ()
FPGA_ShiftRegister = ()
ArrayInterface = ()
Pointer = () # Pointer
Scalar = () # A copyable scalar moved by value (e.g., POD)
Object = () # An object moved by reference
Stream = () # A stream object moved by reference and accessed via a push/pop API
StreamArray = () # An array of Streams
FPGA_ShiftRegister = () # A shift-register object used in FPGA code generation
ArrayInterface = () # An object representing an interface to an array, used mostly in FPGA


class DefinedMemlets:
Expand Down Expand Up @@ -504,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
2 changes: 1 addition & 1 deletion dace/codegen/instrumentation/likwid.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class LIKWIDInstrumentationCPU(InstrumentationProvider):
the Likwid tool.
"""

perf_whitelist_schedules = [dtypes.ScheduleType.CPU_Multicore, dtypes.ScheduleType.Sequential]
perf_whitelist_schedules = [dtypes.ScheduleType.CPU_Multicore, dtypes.ScheduleType.CPU_Persistent, dtypes.ScheduleType.Sequential]

def __init__(self):
self._likwid_used = False
Expand Down
6 changes: 3 additions & 3 deletions dace/codegen/instrumentation/papi.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class PAPIInstrumentation(InstrumentationProvider):

_counters: Optional[Set[str]] = None

perf_whitelist_schedules = [dtypes.ScheduleType.CPU_Multicore, dtypes.ScheduleType.Sequential]
perf_whitelist_schedules = [dtypes.ScheduleType.CPU_Multicore, dtypes.ScheduleType.CPU_Persistent, dtypes.ScheduleType.Sequential]

def __init__(self):
self._papi_used = False
Expand Down Expand Up @@ -350,7 +350,7 @@ def on_consume_entry(self, sdfg, state, node, outer_stream, inner_stream):

@staticmethod
def perf_get_supersection_start_string(node, dfg, unified_id):
if node.map.schedule == dtypes.ScheduleType.CPU_Multicore:
if node.map.schedule in (dtypes.ScheduleType.CPU_Multicore, dtypes.ScheduleType.CPU_Persistent):
# Nested SuperSections are not supported. Therefore, we mark the
# outermost section and disallow internal scopes from creating it.
if not hasattr(node.map, '_can_be_supersection_start'):
Expand All @@ -360,7 +360,7 @@ def perf_get_supersection_start_string(node, dfg, unified_id):
for x in children:
if not hasattr(x.map, '_can_be_supersection_start'):
x.map._can_be_supersection_start = True
if x.map.schedule == dtypes.ScheduleType.CPU_Multicore:
if x.map.schedule in (dtypes.ScheduleType.CPU_Multicore, dtypes.ScheduleType.CPU_Persistent):

x.map._can_be_supersection_start = False
elif x.map.schedule == dtypes.ScheduleType.Sequential:
Expand Down
10 changes: 7 additions & 3 deletions dace/codegen/targets/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def copy_expr(
elif def_type == DefinedType.FPGA_ShiftRegister:
return expr

elif def_type in [DefinedType.Scalar, DefinedType.Stream]:
elif def_type in [DefinedType.Scalar, DefinedType.Stream, DefinedType.Object]:

if add_offset:
raise TypeError("Tried to offset address of scalar {}: {}".format(data_name, offset_cppstr))
Expand Down Expand Up @@ -327,7 +327,7 @@ def make_const(expr: str) -> str:
ref = '&' if is_scalar else ''
defined_type = DefinedType.Scalar if is_scalar else DefinedType.Pointer
offset_expr = ''
elif defined_type == DefinedType.Stream:
elif defined_type in (DefinedType.Stream, DefinedType.Object):
typedef = defined_ctype
ref = '&'
offset_expr = ''
Expand Down 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 Expand Up @@ -1232,7 +1236,7 @@ def visit_Name(self, node: ast.Name):
defined_type = None
if (self.allow_casts and isinstance(dtype, dtypes.pointer) and memlet.subset.num_elements() == 1):
return ast.parse(f"{name}[0]").body[0].value
elif (self.allow_casts and (defined_type == DefinedType.Stream or defined_type == DefinedType.StreamArray)
elif (self.allow_casts and (defined_type in (DefinedType.Stream, DefinedType.StreamArray))
and memlet.dynamic):
return ast.parse(f"{name}.pop()").body[0].value
else:
Expand Down
Loading

0 comments on commit b609eb6

Please sign in to comment.