def generate_transition(self, sdfg: SDFG, edge: Edge[InterstateEdge], successor: SDFGState = None) -> str: """ Helper function that generates a state transition (conditional goto) from a state and an SDFG edge. :param sdfg: The parent SDFG. :param edge: The state transition edge to generate. :param successor: If not None, the state that will be generated right after the current state (used to avoid extraneous gotos). :return: A c++ string representing the state transition code. """ expr = '' condition_string = cpp.unparse_interstate_edge( edge.data.condition.code[0], sdfg) if not edge.data.is_unconditional(): expr += f'if ({condition_string}) {{\n' if len(edge.data.assignments) > 0: expr += ';\n'.join([ "{} = {}".format(variable, cpp.unparse_interstate_edge(value, sdfg)) for variable, value in edge.data.assignments.items() ] + ['']) if successor is None or edge.dst is not successor: expr += 'goto __state_{}_{};\n'.format(sdfg.sdfg_id, edge.dst.label) if not edge.data.is_unconditional(): expr += '}\n' return expr
def as_cpp(self, defined_vars, symbols) -> str: # Initialize to either "int i = 0" or "i = 0" depending on whether # the type has been defined init = '' if self.init is not None: if defined_vars.has(self.itervar): init = self.itervar else: init = f'{symbols[self.itervar]} {self.itervar}' init += ' = ' + self.init sdfg = self.guard.parent preinit = '' if self.init_edges: for edge in self.init_edges: for k, v in edge.data.assignments.items(): if k != self.itervar: cppinit = cpp.unparse_interstate_edge(v, sdfg) preinit += f'{k} = {cppinit};\n' if self.condition is not None: cond = cpp.unparse_interstate_edge(self.condition.code[0], sdfg) else: cond = '' update = '' if self.update is not None: update = f'{self.itervar} = {self.update}' expr = f'{preinit}\nfor ({init}; {cond}; {update}) {{\n' expr += _clean_loop_body(self.body.as_cpp(defined_vars, symbols)) expr += '\n}\n' return expr
def as_cpp(self, defined_vars, symbols) -> str: # Initialize to either "int i = 0" or "i = 0" depending on whether # the type has been defined init = '' if self.init is not None: if defined_vars.has(self.itervar): init = self.itervar else: init = f'{symbols[self.itervar]} {self.itervar}' init += ' = ' + self.init if self.condition is not None: sdfg = self.guard.parent cond = cpp.unparse_interstate_edge(self.condition.code[0], sdfg) else: cond = '' update = '' if self.update is not None: update = f'{self.itervar} = {self.update}' expr = f'for ({init}; {cond}; {update}) {{\n' expr += self.body.as_cpp(defined_vars, symbols) expr += '\n}\n' return expr
def as_cpp(self, defined_vars, symbols) -> str: if self.test is not None: test = cpp.unparse_interstate_edge(self.test.code[0], self.sdfg) else: test = 'true' expr = 'do {\n' expr += self.body.as_cpp(defined_vars, symbols) expr += f'\n}} while ({test});\n' return expr
def as_cpp(self, codegen, symbols) -> str: if self.test is not None: test = cpp.unparse_interstate_edge(self.test.code[0], self.sdfg, codegen=codegen) else: test = 'true' expr = 'do {\n' expr += _clean_loop_body(self.body.as_cpp(codegen, symbols)) expr += f'\n}} while ({test});\n' return expr
def as_cpp(self, defined_vars, symbols) -> str: if self.test is not None: sdfg = self.guard.parent test = cpp.unparse_interstate_edge(self.test.code[0], sdfg) else: test = 'true' expr = f'while ({test}) {{\n' expr += self.body.as_cpp(defined_vars, symbols) expr += '\n}\n' return expr
def as_cpp(self, codegen, symbols) -> str: condition_string = cpp.unparse_interstate_edge(self.condition.code[0], self.sdfg, codegen=codegen) expr = f'if ({condition_string}) {{\n' expr += self.body.as_cpp(codegen, symbols) expr += '\n}' if self.orelse: expr += ' else {\n' expr += self.orelse.as_cpp(codegen, symbols) expr += '\n}' expr += '\n' return expr
def as_cpp(self, codegen, symbols) -> str: if self.test is not None: sdfg = self.guard.parent test = cpp.unparse_interstate_edge(self.test.code[0], sdfg, codegen=codegen) else: test = 'true' expr = f'while ({test}) {{\n' expr += _clean_loop_body(self.body.as_cpp(codegen, symbols)) expr += '\n}\n' return expr
def generate_transition(self, sdfg: SDFG, edge: Edge[InterstateEdge], successor: SDFGState = None, assignments_only: bool = False, framecode: 'DaCeCodeGenerator' = None) -> str: """ Helper function that generates a state transition (conditional goto) from a state and an SDFG edge. :param sdfg: The parent SDFG. :param edge: The state transition edge to generate. :param successor: If not None, the state that will be generated right after the current state (used to avoid extraneous gotos). :param assignments_only: If True, generates only the assignments of the inter-state edge. :param framecode: Code generator object (used for allocation information). :return: A c++ string representing the state transition code. """ expr = '' condition_string = cpp.unparse_interstate_edge(edge.data.condition.code[0], sdfg, codegen=framecode) if not edge.data.is_unconditional() and not assignments_only: expr += f'if ({condition_string}) {{\n' if len(edge.data.assignments) > 0: expr += ';\n'.join([ "{} = {}".format(variable, cpp.unparse_interstate_edge(value, sdfg, codegen=framecode)) for variable, value in edge.data.assignments.items() ] + ['']) if ((successor is None or edge.dst is not successor) and not assignments_only): expr += 'goto __state_{}_{};\n'.format(sdfg.sdfg_id, edge.dst.label) if not edge.data.is_unconditional() and not assignments_only: expr += '}\n' return expr
def as_cpp(self, codegen, symbols) -> str: expr = '' for i, (condition, body) in enumerate(self.body): # First block in the chain is just "if", rest are "else if" prefix = '' if i == 0 else ' else ' condition_string = cpp.unparse_interstate_edge(condition.code[0], self.sdfg, codegen=codegen) expr += f'{prefix}if ({condition_string}) {{\n' expr += body.as_cpp(codegen, symbols) expr += '\n}' # If we generate an if/else if blocks, we cannot guarantee that all # cases have been covered. In SDFG semantics, this means that the SDFG # execution should end, so we emit an "else goto exit" here. if len(self.body) > 0: expr += ' else {\n' expr += 'goto __state_exit_{};\n'.format(self.sdfg.sdfg_id) if len(self.body) > 0: expr += '\n}' return expr
def _generate_transition(self, sdfg, sid, callsite_stream, edge, assignments): condition_string = unparse_interstate_edge(edge.data.condition.code[0], sdfg) 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) + [""]), sdfg, sid) callsite_stream.write( "goto __state_{}_{};".format(sdfg.sdfg_id, edge.dst.label), sdfg, sid) if not always_true: callsite_stream.write("}")
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.sdfg_id, state.label), sdfg, sid) # Don't generate brackets and comments for empty states if len([n for n in state.nodes()]) > 0: callsite_stream.write('{', sdfg, sid) self._dispatcher.dispatch_state(sdfg, state, global_stream, callsite_stream) callsite_stream.write('}', sdfg, sid) 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, cflow.LoopAssignment): # Generate the transition, but leave the # assignments to the loop generate_transition &= True generate_assignments &= False elif isinstance(control, cflow.LoopBack): generate_transition &= False generate_assignments &= False elif isinstance(control, cflow.LoopExit): # Need to strip the condition, so generate it from # the loop entry generate_transition &= False generate_assignments &= True elif isinstance(control, cflow.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, sdfg)) generated_edges.add(assignment_edge) else: init_assignments = "" back_edge = control.scope.back.edge continue_assignments = ", ".join( DaCeCodeGenerator._generate_assignments( back_edge.data.assignments, sdfg)) generated_edges.add(back_edge) entry_edge = control.scope.entry.edge condition = unparse_interstate_edge( entry_edge.data.condition.code[0], sdfg) 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.sdfg_id, control.scope.exit.edge.dst)) generated_edges.add(control.scope.exit.edge) elif isinstance(control, cflow.IfExit): generate_transition &= True generate_assignments &= True elif isinstance(control, cflow.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 = unparse_interstate_edge( then_entry.data.condition.code[0], sdfg) 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.sdfg_id, 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) + [""]), sdfg, sid) generated_edges.add(edge) # End of out_edges loop if (((len(out_edges) == 0) or (not isinstance(scope, cflow.ControlFlowScope) and (len(states_to_generate) == 0))) and (len(states_generated) != sdfg.number_of_nodes())): callsite_stream.write( "goto __state_exit_{}_{};".format(sdfg.sdfg_id, scope_label), sdfg, sid) # Write exit state callsite_stream.write( "__state_exit_{}_{}:;".format(sdfg.sdfg_id, scope_label), sdfg)
def _generate_assignments(assignments, sdfg): return [ "{} = {}".format(variable, unparse_interstate_edge(value, sdfg)) for variable, value in assignments.items() ]