def _structured_control_flow_traversal( sdfg: SDFG, start: SDFGState, ptree: Dict[SDFGState, SDFGState], branch_merges: Dict[SDFGState, SDFGState], back_edges: List[Edge[InterstateEdge]], dispatch_state: Callable[[SDFGState], str], parent_block: GeneralBlock, stop: SDFGState = None, generate_children_of: SDFGState = None) -> Set[SDFGState]: """ Helper function for ``structured_control_flow_tree``. :param sdfg: SDFG. :param start: Starting state for traversal. :param ptree: State parent tree (computed from ``state_parent_tree``). :param branch_merges: Dictionary mapping from branch state to its merge state. :param dispatch_state: A function that dispatches code generation for a single state. :param parent_block: The block to append children to. :param stop: Stopping state to not traverse through (merge state of a branch or guard state of a loop). :return: Generator that yields states in state-order from ``start`` to ``stop``. """ # Traverse states in custom order visited = set() if stop is not None: visited.add(stop) stack = [start] while stack: node = stack.pop() if (generate_children_of is not None and not _child_of(node, generate_children_of, ptree)): continue if node in visited: continue visited.add(node) stateblock = SingleState(dispatch_state, node) oe = sdfg.out_edges(node) if len(oe) == 0: # End state # If there are no remaining nodes, this is the last state and it can # be marked as such if len(stack) == 0: stateblock.last_state = True parent_block.elements.append(stateblock) continue elif len(oe) == 1: # No traversal change stack.append(oe[0].dst) parent_block.elements.append(stateblock) continue # Potential branch or loop if node in branch_merges: mergestate = branch_merges[node] # Add branching node and ignore outgoing edges parent_block.elements.append(stateblock) parent_block.edges_to_ignore.extend(oe) stateblock.last_state = True # Parse all outgoing edges recursively first cblocks: Dict[Edge[InterstateEdge], GeneralBlock] = {} for branch in oe: cblocks[branch] = GeneralBlock(dispatch_state, [], []) visited |= _structured_control_flow_traversal( sdfg, branch.dst, ptree, branch_merges, back_edges, dispatch_state, cblocks[branch], stop=mergestate, generate_children_of=node) # Classify branch type: branch_block = None # If there are 2 out edges, one negation of the other: # * if/else in case both branches are not merge state # * if without else in case one branch is merge state 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]]) elif oe[1].dst is mergestate: branch_block = IfScope(dispatch_state, 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]]) 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]) else: # Otherwise, create if/else if/.../else goto exit chain branch_block = IfElseChain(dispatch_state, sdfg, node, [(e.data.condition, cblocks[e]) for e in oe]) # End of branch classification parent_block.elements.append(branch_block) if mergestate != stop: stack.append(mergestate) elif len(oe) == 2: # Potential loop # TODO(later): Recognize do/while loops # If loop, traverse body, then exit body_start = None 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) 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) body_start = oe[1].dst loop_exit = oe[0].dst if scope: visited |= _structured_control_flow_traversal( sdfg, body_start, ptree, branch_merges, back_edges, dispatch_state, scope.body, stop=node, generate_children_of=node) # Add branching node and ignore outgoing edges parent_block.elements.append(stateblock) parent_block.edges_to_ignore.extend(oe) parent_block.elements.append(scope) # If for loop, ignore certain edges if isinstance(scope, ForScope): # Mark init edge(s) to ignore in parent_block and all children _ignore_recursive([ e for e in sdfg.in_edges(node) if e not in back_edges ], parent_block) # Mark back edge for ignoring in all children of loop body _ignore_recursive( [e for e in sdfg.in_edges(node) if e in back_edges], scope.body) stack.append(loop_exit) continue # No proper loop detected: Unstructured control flow parent_block.elements.append(stateblock) stack.extend([e.dst for e in oe]) else: # No merge state: Unstructured control flow parent_block.elements.append(stateblock) stack.extend([e.dst for e in oe]) return visited - {stop}
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]: """ Helper method that constructs the correct structured loop construct from a set of states. Can construct for or while loops. """ body = GeneralBlock(dispatch_state, [], []) guard_inedges = sdfg.in_edges(guard) increment_edges = [e for e in guard_inedges if e in back_edges] init_edges = [e for e in guard_inedges if e not in back_edges] # If no back edge found (or more than one, indicating a "continue" # statement), disregard if len(increment_edges) > 1 or len(increment_edges) == 0: return None increment_edge = increment_edges[0] # Mark increment edge to be ignored in body body.edges_to_ignore.append(increment_edge) # Outgoing edges must be a negation of each other if enter_edge.data.condition_sympy() != (sp.Not( leave_edge.data.condition_sympy())): return None # Body of guard state must be empty if not guard.is_empty(): return None if not increment_edge.data.is_unconditional(): return None if len(enter_edge.data.assignments) > 0: return None condition = enter_edge.data.condition # Detect whether this loop is a for loop: # All incoming edges to the guard must set the same variable itvars = None for iedge in guard_inedges: if itvars is None: itvars = set(iedge.data.assignments.keys()) else: itvars &= iedge.data.assignments.keys() if itvars and len(itvars) == 1: itvar = next(iter(itvars)) init = init_edges[0].data.assignments[itvar] # Check that all init edges are the same and that increment edge only # increments if (all(e.data.assignments[itvar] == init for e in init_edges) and len(increment_edge.data.assignments) == 1): update = increment_edge.data.assignments[itvar] return ForScope(dispatch_state, itvar, guard, init, condition, update, body) # Otherwise, it is a while loop return WhileScope(dispatch_state, guard, condition, body)