Skip to content

Commit

Permalink
Merge branch 'master' into trivial_map_elimination_init
Browse files Browse the repository at this point in the history
  • Loading branch information
Sajohn-CH authored Oct 10, 2023
2 parents 9e08b03 + 291e98d commit 1b198f4
Show file tree
Hide file tree
Showing 88 changed files with 4,989 additions and 786 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/general-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7,'3.11']
python-version: [3.7,'3.12']
simplify: [0,1,autoopt]

steps:
Expand Down
5 changes: 3 additions & 2 deletions dace/codegen/compiled_sdfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ def get_workspace_sizes(self) -> Dict[dtypes.StorageType, int]:
result: Dict[dtypes.StorageType, int] = {}
for storage in self.external_memory_types:
func = self._lib.get_symbol(f'__dace_get_external_memory_size_{storage.name}')
func.restype = ctypes.c_size_t
result[storage] = func(self._libhandle, *self._lastargs[1])

return result
Expand Down Expand Up @@ -449,8 +450,8 @@ def _construct_args(self, kwargs) -> Tuple[Tuple[Any], Tuple[Any]]:
raise TypeError('Passing an object (type %s) to an array in argument "%s"' %
(type(arg).__name__, a))
elif dtypes.is_array(arg) and not isinstance(atype, dt.Array):
# GPU scalars are pointers, so this is fine
if atype.storage != dtypes.StorageType.GPU_Global:
# GPU scalars and return values are pointers, so this is fine
if atype.storage != dtypes.StorageType.GPU_Global and not a.startswith('__return'):
raise TypeError('Passing an array to a scalar (type %s) in argument "%s"' % (atype.dtype.ctype, a))
elif (not isinstance(atype, (dt.Array, dt.Structure)) and
not isinstance(atype.dtype, dtypes.callback) and
Expand Down
87 changes: 64 additions & 23 deletions dace/codegen/control_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
x < 5
/------>[s2]--------\\
[s1] \ ->[s5]
[s1] \\ ->[s5]
------>[s3]->[s4]--/
x >= 5
Expand Down 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 @@ -350,6 +360,9 @@ class ForScope(ControlFlow):
init_edges: List[InterstateEdge] #: All initialization edges

def as_cpp(self, codegen, symbols) -> str:

sdfg = self.guard.parent

# Initialize to either "int i = 0" or "i = 0" depending on whether
# the type has been defined
defined_vars = codegen.dispatcher.defined_vars
Expand All @@ -359,9 +372,8 @@ def as_cpp(self, codegen, symbols) -> str:
init = self.itervar
else:
init = f'{symbols[self.itervar]} {self.itervar}'
init += ' = ' + self.init

sdfg = self.guard.parent
init += ' = ' + unparse_interstate_edge(self.init_edges[0].data.assignments[self.itervar],
sdfg, codegen=codegen)

preinit = ''
if self.init_edges:
Expand Down Expand Up @@ -478,13 +490,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 +548,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 +630,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 +683,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 +695,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 +746,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 +779,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 +876,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
67 changes: 58 additions & 9 deletions dace/codegen/cppunparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import numpy as np
import os
import tokenize
import warnings

import sympy
import dace
Expand All @@ -86,6 +87,21 @@
from dace import dtypes
from dace.codegen.tools import type_inference


if sys.version_info < (3, 8):
BytesConstant = ast.Bytes
EllipsisConstant = ast.Ellipsis
NameConstant = ast.NameConstant
NumConstant = ast.Num
StrConstant = ast.Str
else:
BytesConstant = ast.Constant
EllipsisConstant = ast.Constant
NameConstant = ast.Constant
NumConstant = ast.Constant
StrConstant = ast.Constant


# Large float and imaginary literals get turned into infinities in the AST.
# We unparse those infinities to INFSTR.
INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
Expand Down Expand Up @@ -573,7 +589,7 @@ def _generic_FunctionDef(self, t, is_async=False):
self.write('/* async */ ')

if getattr(t, "returns", False):
if isinstance(t.returns, ast.NameConstant):
if isinstance(t.returns, NameConstant):
if t.returns.value is None:
self.write('void')
else:
Expand Down Expand Up @@ -728,11 +744,27 @@ def _Repr(self, t):
raise NotImplementedError('Invalid C++')

def _Num(self, t):
repr_n = repr(t.n)
t_n = t.value if sys.version_info >= (3, 8) else t.n
repr_n = repr(t_n)
# For complex values, use DTYPE_TO_TYPECLASS dictionary
if isinstance(t.n, complex):
if isinstance(t_n, complex):
dtype = dtypes.DTYPE_TO_TYPECLASS[complex]

# Handle large integer values
if isinstance(t_n, int):
bits = t_n.bit_length()
if bits == 32: # Integer, potentially unsigned
if t_n >= 0: # unsigned
repr_n += 'U'
else: # signed, 64-bit
repr_n += 'LL'
elif 32 < bits <= 63:
repr_n += 'LL'
elif bits == 64 and t_n >= 0:
repr_n += 'ULL'
elif bits >= 64:
warnings.warn(f'Value wider than 64 bits encountered in expression ({t_n}), emitting as-is')

if repr_n.endswith("j"):
self.write("%s(0, %s)" % (dtype, repr_n.replace("inf", INFSTR)[:-1]))
else:
Expand Down Expand Up @@ -831,8 +863,23 @@ def _Tuple(
self.write(")")

unop = {"Invert": "~", "Not": "!", "UAdd": "+", "USub": "-"}
unop_lambda = {'Invert': (lambda x: ~x), 'Not': (lambda x: not x), 'UAdd': (lambda x: +x), 'USub': (lambda x: -x)}

def _UnaryOp(self, t):
# Dispatch constants after applying the operation
if sys.version_info[:2] < (3, 8):
if isinstance(t.operand, ast.Num):
newval = self.unop_lambda[t.op.__class__.__name__](t.operand.n)
newnode = ast.Num(n=newval)
self.dispatch(newnode)
return
else:
if isinstance(t.operand, ast.Constant):
newval = self.unop_lambda[t.op.__class__.__name__](t.operand.value)
newnode = ast.Constant(value=newval)
self.dispatch(newnode)
return

self.write("(")
self.write(self.unop[t.op.__class__.__name__])
self.write(" ")
Expand Down Expand Up @@ -867,13 +914,13 @@ def _BinOp(self, t):
self.write(")")
# Special cases for powers
elif t.op.__class__.__name__ == 'Pow':
if isinstance(t.right, (ast.Num, ast.Constant, ast.UnaryOp)):
if isinstance(t.right, (NumConstant, ast.Constant, ast.UnaryOp)):
power = None
if isinstance(t.right, (ast.Num, ast.Constant)):
power = t.right.n
if isinstance(t.right, (NumConstant, ast.Constant)):
power = t.right.value if sys.version_info >= (3, 8) else t.right.n
elif isinstance(t.right, ast.UnaryOp) and isinstance(t.right.op, ast.USub):
if isinstance(t.right.operand, (ast.Num, ast.Constant)):
power = -t.right.operand.n
if isinstance(t.right.operand, (NumConstant, ast.Constant)):
power = - (t.right.operand.value if sys.version_info >= (3, 8) else t.right.operand.n)

if power is not None and int(power) == power:
negative = power < 0
Expand Down Expand Up @@ -953,7 +1000,9 @@ def _Attribute(self, t):
# Special case: 3.__abs__() is a syntax error, so if t.value
# is an integer literal then we need to either parenthesize
# it or add an extra space to get 3 .__abs__().
if (isinstance(t.value, (ast.Num, ast.Constant)) and isinstance(t.value.n, int)):
if isinstance(t.value, ast.Constant) and isinstance(t.value.value, int):
self.write(" ")
elif sys.version_info < (3, 8) and isinstance(t.value, ast.Num) and isinstance(t.value.n, int):
self.write(" ")
if (isinstance(t.value, ast.Name) and t.value.id in ('dace', 'dace::math', 'dace::cmath')):
self.write("::")
Expand Down
2 changes: 1 addition & 1 deletion dace/codegen/instrumentation/papi.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ class PAPIUtils(object):
def available_counters() -> Dict[str, int]:
"""
Returns the available PAPI counters on this machine. Only works on
\*nix based systems with ``grep`` and ``papi-tools`` installed.
*nix based systems with ``grep`` and ``papi-tools`` installed.
:return: A set of available PAPI counters in the form of a dictionary
mapping from counter name to the number of native hardware
Expand Down
Loading

0 comments on commit 1b198f4

Please sign in to comment.