def visit_Assign(self, node): target = rname(node.targets[0]) if target not in self.memlets: return self.generic_visit(node) memlet, nc, wcr = self.memlets[target] value = self.visit(node.value) if not isinstance(node.targets[0], ast.Subscript): # Dynamic accesses -> every access counts try: if memlet is not None and memlet.num_accesses < 0: if wcr is not None: newnode = ast.Name(id=write_and_resolve_expr( self.sdfg, memlet, nc, '__' + target, cppunparse.cppunparse(value, expr_semicolon=False))) else: newnode = ast.Name(id="__%s.write(%s);" % ( target, cppunparse.cppunparse(value, expr_semicolon=False), )) return ast.copy_location(newnode, node) except TypeError: # cannot determine truth value of Relational pass return self.generic_visit(node) slice = self.visit(node.targets[0].slice) if not isinstance(slice, ast.Index): raise NotImplementedError("Range subscripting not implemented") if isinstance(slice.value, ast.Tuple): subscript = unparse(slice)[1:-1] else: subscript = unparse(slice) if wcr is not None: newnode = ast.Name(id=write_and_resolve_expr( self.sdfg, memlet, nc, "__" + target, cppunparse.cppunparse(value, expr_semicolon=False), indices=subscript, )) else: newnode = ast.Name(id="__%s.write(%s, %s);" % ( target, cppunparse.cppunparse(value, expr_semicolon=False), subscript, )) return ast.copy_location(newnode, node)
def _generate_transition(self, sdfg, sid, callsite_stream, edge, assignments): condition_string = cppunparse.cppunparse(edge.data.condition, False) always_true = self._is_always_true(condition_string) if not always_true: callsite_stream.write("if ({}) {{".format(condition_string), sdfg, sid) if len(assignments) > 0: callsite_stream.write( ";\n".join( DaCeCodeGenerator._generate_assignments(assignments) + [""]), sdfg, sid) callsite_stream.write( "goto __state_{}_{};".format(sdfg.name, edge.dst.label), sdfg, sid) if not always_true: callsite_stream.write("}")
def unparse_cr_split(sdfg, wcr_ast): """ Parses various types of WCR functions, returning a 2-tuple of body (in C++), and a list of arguments. """ if isinstance(wcr_ast, ast.Lambda): # Convert the lambda expression into a function that we can parse funcdef = LambdaToFunction().visit(wcr_ast) return unparse_cr_split(sdfg, funcdef) elif isinstance(wcr_ast, ast.FunctionDef): # Process data structure initializers sinit = StructInitializer(sdfg) body = [sinit.visit(stmt) for stmt in wcr_ast.body] # Construct a C++ lambda function out of a function args = [n.arg for n in wcr_ast.args.args] return cppunparse.cppunparse(body, expr_semicolon=False), args elif isinstance(wcr_ast, ast.Module): return unparse_cr_split(sdfg, wcr_ast.body[0].value) elif isinstance(wcr_ast, str): return unparse_cr_split(sdfg, LambdaProperty.from_string(wcr_ast)) else: raise NotImplementedError("INVALID TYPE OF WCR: " + type(wcr_ast).__name__)
def visit_Assign(self, node): target = rname(node.targets[-1]) if target not in self.memlets: return self.generic_visit(node) memlet, nc, wcr, dtype = self.memlets[target] value = self.visit(node.value) if not isinstance(node.targets[-1], ast.Subscript): # Dynamic accesses or streams -> every access counts try: if memlet and memlet.data and (memlet.dynamic or isinstance( self.sdfg.arrays[memlet.data], data.Stream)): if wcr is not None: newnode = ast.Name( id=self.codegen.write_and_resolve_expr( self.sdfg, memlet, nc, target, cppunparse.cppunparse(value, expr_semicolon=False), dtype=dtype)) node.value = ast.copy_location(newnode, node.value) return node elif isinstance(self.sdfg.arrays[memlet.data], data.Stream): newnode = ast.Name(id="%s.push(%s);" % ( memlet.data, cppunparse.cppunparse(value, expr_semicolon=False), )) else: var_type, ctypedef = self.codegen._dispatcher.defined_vars.get( memlet.data) if var_type == DefinedType.Scalar: newnode = ast.Name(id="%s = %s;" % ( memlet.data, cppunparse.cppunparse(value, expr_semicolon=False), )) else: newnode = ast.Name(id="%s = %s;" % ( cpp_array_expr(self.sdfg, memlet), cppunparse.cppunparse(value, expr_semicolon=False), )) return self._replace_assignment(newnode, node) except TypeError: # cannot determine truth value of Relational pass return self.generic_visit(node) subscript = self._subscript_expr(node.targets[-1].slice, target) if wcr is not None: newnode = ast.Name(id=self.codegen.write_and_resolve_expr( self.sdfg, memlet, nc, target, cppunparse.cppunparse(value, expr_semicolon=False), indices=sym2cpp(subscript), dtype=dtype) + ';') else: newnode = ast.Name( id="%s[%s] = %s;" % (target, sym2cpp(subscript), cppunparse.cppunparse(value, expr_semicolon=False))) return self._replace_assignment(newnode, node)
def generate_states(self, sdfg, scope_label, control_flow, global_stream, callsite_stream, scope, states_generated, generated_edges): states_topological = list(sdfg.topological_sort(sdfg.start_state)) states_to_generate = collections.deque([ s for s in states_topological if s in scope and s not in states_generated ]) if len(states_to_generate) == 0: return while len(states_to_generate) > 0: state = states_to_generate.popleft() # When generating control flow constructs, we will not necessarily # move in topological order, so make sure this state has not # already been generated. if state in states_generated or state not in scope: continue states_generated.add(state) sid = sdfg.node_id(state) callsite_stream.write( "__state_{}_{}:\n".format(sdfg.name, state.label), sdfg, sid) # Don't generate brackets and comments for empty states if len([ n for n in state.nodes() if not isinstance(n, dace.graph.nodes.EmptyTasklet) ]) > 0: callsite_stream.write('{', sdfg, sid) self._dispatcher.dispatch_state(sdfg, state, global_stream, callsite_stream) callsite_stream.write('}', sdfg, sid) else: callsite_stream.write(";") out_edges = sdfg.out_edges(state) # Write conditional branches to next states for edge in out_edges: generate_assignments = True generate_transition = True # Handle specialized control flow if (dace.config.Config.get_bool('optimizer', 'detect_control_flow')): for control in control_flow[edge]: if isinstance(control, dace.graph.edges.LoopAssignment): # Generate the transition, but leave the # assignments to the loop generate_transition &= True generate_assignments &= False elif isinstance(control, dace.graph.edges.LoopBack): generate_transition &= False generate_assignments &= False elif isinstance(control, dace.graph.edges.LoopExit): # Need to strip the condition, so generate it from # the loop entry generate_transition &= False generate_assignments &= True elif isinstance(control, dace.graph.edges.LoopEntry): generate_transition &= False generate_assignments &= False if control.scope.assignment is not None: assignment_edge = control.scope.assignment.edge init_assignments = ", ".join( DaCeCodeGenerator._generate_assignments( assignment_edge.data.assignments)) generated_edges.add(assignment_edge) else: init_assignments = "" back_edge = control.scope.back.edge continue_assignments = ", ".join( DaCeCodeGenerator._generate_assignments( back_edge.data.assignments)) generated_edges.add(back_edge) entry_edge = control.scope.entry.edge condition = cppunparse.cppunparse( entry_edge.data.condition, False) generated_edges.add(entry_edge) if (len(init_assignments) > 0 or len(continue_assignments) > 0): callsite_stream.write( "for ({}; {}; {}) {{".format( init_assignments, condition, continue_assignments), sdfg, sid) else: callsite_stream.write( "while ({}) {{".format(condition), sdfg, sid) # Generate loop body self.generate_states( sdfg, entry_edge.src.label + "_loop", control_flow, global_stream, callsite_stream, control.scope, states_generated, generated_edges) callsite_stream.write("}", sdfg, sid) exit_edge = control.scope.exit.edge # Update states to generate after nested call states_to_generate = collections.deque([ s for s in states_to_generate if s not in states_generated ]) # If the next state to be generated is the exit # state, we can omit the goto if (len(states_to_generate) > 0 and states_to_generate[0] == exit_edge.dst and exit_edge.dst not in states_generated): pass elif edge in generated_edges: # This edge has more roles, goto doesn't apply pass else: callsite_stream.write( "goto __state_{}_{};".format( sdfg.name, control.scope.exit.edge.dst)) generated_edges.add(control.scope.exit.edge) elif isinstance(control, dace.graph.edges.IfExit): generate_transition &= True generate_assignments &= True elif isinstance(control, dace.graph.edges.IfEntry): generate_transition &= False generate_assignments &= True if len(set(control.scope) - states_generated) == 0: continue then_scope = control.scope.if_then_else.then_scope else_scope = control.scope.if_then_else.else_scope then_entry = then_scope.entry.edge condition = cppunparse.cppunparse( then_entry.data.condition, False) callsite_stream.write( "if ({}) {{".format(condition), sdfg, sid) generated_edges.add(then_entry) # Generate the then-scope self.generate_states(sdfg, state.label + "_then", control_flow, global_stream, callsite_stream, then_scope, states_generated, generated_edges) callsite_stream.write("} else {", sdfg, sid) generated_edges.add(else_scope.entry.edge) # Generate the else-scope self.generate_states(sdfg, state.label + "_else", control_flow, global_stream, callsite_stream, else_scope, states_generated, generated_edges) callsite_stream.write("}", sdfg, sid) generated_edges.add(else_scope.exit.edge) # Update states to generate after nested call states_to_generate = collections.deque([ s for s in states_to_generate if s not in states_generated ]) if_exit_state = control.scope.exit.edge.dst if ((if_exit_state not in states_generated) and ((len(states_to_generate) > 0) and (states_to_generate[0] == if_exit_state))): pass else: callsite_stream.write( "goto __state_{}_{};".format( sdfg.name, control.scope.exit.edge.dst)) else: raise TypeError( "Unknown control flow \"{}\"".format( type(control).__name__)) if generate_assignments and len(edge.data.assignments) > 0: assignments_to_generate = edge.data.assignments else: assignments_to_generate = {} if generate_transition: if ((len(out_edges) == 1) and (edge.dst not in states_generated) and ((len(states_to_generate) > 0) and (states_to_generate[0] == edge.dst))): # If there is only one outgoing edge, the target will # be generated next, we can omit the goto pass elif (len(out_edges) == 1 and len(states_to_generate) == 0 and (edge.dst not in scope)): # This scope has ended, and we don't need to generate # any output edge pass else: self._generate_transition(sdfg, sid, callsite_stream, edge, assignments_to_generate) # Assignments will be generated in the transition generate_assignments = False if generate_assignments: callsite_stream.write( ";\n".join( DaCeCodeGenerator._generate_assignments( assignments_to_generate) + [""]), sdfg, sid) generated_edges.add(edge) # End of out_edges loop if (((len(out_edges) == 0) or (not isinstance(scope, dace.graph.edges.ControlFlowScope) and (len(states_to_generate) == 0))) and (len(states_generated) != sdfg.number_of_nodes())): callsite_stream.write( "goto __state_exit_{}_{};".format(sdfg.name, scope_label), sdfg, sid) # Write exit state callsite_stream.write( "__state_exit_{}_{}:;".format(sdfg.name, scope_label), sdfg)