def test_reference_property(self): from_string = dace.memlet.Memlet.__properties__["data"].from_string sdfg = dace.SDFG("test_sdfg", OrderedDict([("foo", dace.dtypes.float32)])) state0 = dace.SDFGState("s0", sdfg) state1 = dace.SDFGState("s1", sdfg) sdfg.add_node(state0) sdfg.add_node(state1) _, arr0 = sdfg.add_array("arr0", (16, 16), dace.dtypes.float32) data0 = dace.graph.nodes.AccessNode('arr0') state0.add_node(data0) sdfg.add_array("arr1", (16, 16), dace.dtypes.float32) state0.add_node(dace.graph.nodes.AccessNode('arr1')) sdfg.add_array("arr2", (16, 16), dace.dtypes.float32) state1.add_node(dace.graph.nodes.AccessNode('arr2')) memlet = dace.memlet.Memlet('arr2', 1, "0:N", 1) with self.assertRaises(TypeError): # Must pass SDFG as second argument memlet = dace.memlet.Memlet( dace.memlet.Memlet.__properties__["data"].from_string("arr0"), None, 1, "i", 1) memlet.data = 'arr0' self.assertEqual(sdfg.arrays[memlet.data], arr0)
def test_reference_property(self): from_string = dace.memlet.Memlet.__properties__["data"].from_string sdfg = dace.SDFG("test_sdfg", OrderedDict([("foo", dace.dtypes.float32)])) state0 = dace.SDFGState("s0", sdfg) state1 = dace.SDFGState("s1", sdfg) sdfg.add_node(state0) sdfg.add_node(state1) _, arr0 = sdfg.add_array("arr0", (16, 16), dace.dtypes.float32) data0 = dace.sdfg.nodes.AccessNode('arr0') state0.add_node(data0) sdfg.add_array("arr1", (16, 16), dace.dtypes.float32) state0.add_node(dace.sdfg.nodes.AccessNode('arr1')) sdfg.add_array("arr2", (16, 16), dace.dtypes.float32) state1.add_node(dace.sdfg.nodes.AccessNode('arr2')) memlet = dace.memlet.Memlet.simple('arr2', '0:N', num_accesses=1) memlet.data = 'arr0' self.assertEqual(sdfg.arrays[memlet.data], arr0)
def generate_code(self, sdfg=None, state=None): if sdfg is None: sdfg = dace.SDFG("dacelab", OrderedDict(), {}) prevstate = None for s in self.statements: state = len(sdfg.nodes()) newstate = dace.SDFGState("s" + str(state), sdfg, debuginfo=s.context) sdfg.add_node(newstate) last_state = s.generate_code(sdfg, state) if prevstate is not None: edge = dace.sdfg.InterstateEdge() sdfg.add_edge(prevstate, newstate, edge) if last_state is None: prevstate = newstate else: prevstate = sdfg.nodes()[last_state] return sdfg else: raise ValueError( "Appending statements to an SDFG is not supported.")
def AddState(self, statelabel): newstate = dace.SDFGState(statelabel, self.sdfg) self.sdfg.add_node(newstate) self.rendered_sdfg.set_dotcode(self.sdfg.draw()) self.sdfg_changed = True
def generate_code(self, sdfg, state): from .ast_range import AST_RangeExpression # This ignores matlab semantics and only works for loops of the form # for var = start:end where start and end are expressions which # evaluate to scalars. if isinstance(self.initializer, AST_RangeExpression): # Generate the initializer: # lhs and rhs of the iteration range as two transients, and a # transient for i (we also have a symbol for i which states will # use) initializer_state_num = state s = sdfg.nodes()[state] self.initializer.lhs.generate_code(sdfg, state) lhs_node = self.initializer.lhs.get_datanode(sdfg, state) self.initializer.rhs.generate_code(sdfg, state) rhs_node = self.initializer.rhs.get_datanode(sdfg, state) sdfg.add_transient(self.var.get_name_in_sdfg(sdfg), [1], self.initializer.lhs.get_basetype()) var_node = s.add_access(self.var.get_name_in_sdfg(sdfg)) s.add_edge(lhs_node, None, var_node, None, dace.memlet.Memlet.from_array(var_node.data, var_node.desc(sdfg))) loop_guard_var = '_loopiter_' + str(state) loop_end_var = '_loopend_' + str(state) # Generate guard state, write loop iter symbol into loop iter # datanode guard_state_num = initializer_state_num + 1 s_guard = sdfg.add_state('s' + str(guard_state_num)) task = s_guard.add_tasklet('reinitloopiter', {}, {'out'}, "out=" + loop_guard_var) if self.var.get_name_in_sdfg(sdfg) not in sdfg.arrays: sdfg.add_transient(self.var.get_name_in_sdfg(sdfg), [1], self.initializer.lhs.get_basetype()) trans = s_guard.add_access(self.var.get_name_in_sdfg(sdfg)) # Workaround until "condition for putting a variable as top-level # doesn't take inter-state edges into account" is solved. # When fixed, the line below can be removed. self.initializer.rhs.generate_code(sdfg, guard_state_num) s_guard.add_edge(task, 'out', trans, None, dace.memlet.Memlet.from_array(trans.data, trans.desc(sdfg))) lg_init = dace.sdfg.InterstateEdge( assignments={ loop_guard_var: self.var.get_name_in_sdfg(sdfg) + '(0)', loop_end_var: self.initializer.rhs.get_name_in_sdfg(sdfg) + '(0)' }) sdfg.add_edge(sdfg.nodes()[state], s_guard, lg_init) # Add state for each statement within the for loop prev = s_guard for s in self.stmts.statements: state = len(sdfg.nodes()) newstate = dace.SDFGState("s" + str(state), sdfg, debuginfo=s.context) sdfg.add_node(newstate) last_state = s.generate_code(sdfg, state) if last_state is None: last_state = state if prev != s_guard: edge = dace.sdfg.InterstateEdge() sdfg.add_edge(prev, newstate, edge) else: edge = dace.sdfg.InterstateEdge(condition=dace.properties.CodeProperty.from_string( loop_guard_var + " <= " + loop_end_var, language=dace.dtypes.Language.Python)) sdfg.add_edge(prev, newstate, edge) prev = sdfg.nodes()[last_state] # Create inter-state back-edge edge = dace.sdfg.InterstateEdge(assignments={loop_guard_var: loop_guard_var + '+1'}) sdfg.add_edge(prev, s_guard, edge) # Create the loop exit state state = len(sdfg.nodes()) s_lexit = dace.SDFGState("s" + str(state), sdfg, debuginfo=s.context) lend_val = str(self.initializer.get_dims()[-1]) for_exit = dace.sdfg.InterstateEdge(condition=dace.properties.CodeProperty.from_string( loop_guard_var + " > " + loop_end_var, language=dace.dtypes.Language.Python)) sdfg.add_edge(s_guard, s_lexit, for_exit) return state else: raise NotImplementedError("Loops over anything but ranges are not implemented.")
def generate_code_proper(self, sdfg, state): # This follows matlab semantics, i.e., a loop iterates over the columns # of a matrix. This does not work well for sdfgs for all but the # simplest case (a matrix which is a compile time constant, ie. 1:10). # To support programs like Cholesky, we try to transform the matlab for # loop into a C-style loop, this is implemented in generate_code(). # Generate the initializer: # Each iteration of the for loop will use one column initializer_state_num = state self.initializer.generate_code(sdfg, state) loop_guard_var = '_lg_' + str(state) # Generate an (empty) guard state guard_state_num = initializer_state_num + 1 s_guard = sdfg.add_state('s' + str(guard_state_num)) lg_init = dace.sdfg.InterstateEdge(assignments={loop_guard_var: '0'}) sdfg.add_edge(sdfg.nodes()[state], s_guard, lg_init) # Read a column of the initializer get_initializer_state_num = guard_state_num + 1 s_getinit = sdfg.add_state('s' + str(get_initializer_state_num)) initializer_name = self.initializer.get_name_in_sdfg(sdfg) loopvar_name = self.var.get_name_in_sdfg(sdfg) dims = self.initializer.get_dims()[:1] sdfg.add_transient(loopvar_name, dims, self.initializer.get_basetype()) part = s_getinit.add_access(loopvar_name) sdfg.add_transient(initializer_name, self.initializer.get_dims(), self.initializer.get_basetype()) full = s_getinit.add_read(initializer_name) s_getinit.add_edge(full, None, part, None, dace.memlet.Memlet.simple(initializer_name, 'i,0')) # Add edge from guard to getinit lend_val = str(self.initializer.get_dims()[-1]) for_entry = dace.sdfg.InterstateEdge(condition=dace.properties.CodeProperty.from_string( loop_guard_var + " < " + lend_val, language=dace.dtypes.Language.Python)) sdfg.add_edge(s_guard, s_getinit, for_entry) # Add state for each statement within the for loop prev = s_getinit for s in self.stmts.statements: state = len(sdfg.nodes()) newstate = dace.SDFGState("s" + str(state), sdfg, debuginfo=s.context) sdfg.add_node(newstate) last_state = s.generate_code(sdfg, state) if last_state is None: last_state = state edge = dace.sdfg.InterstateEdge() sdfg.add_edge(prev, newstate, edge) prev = sdfg.nodes()[last_state] # Create inter-state back-edge edge = dace.sdfg.InterstateEdge(assignments={loop_guard_var: loop_guard_var + '+1'}) sdfg.add_edge(prev, s_guard, edge) # Create the loop exit state state = len(sdfg.nodes()) s_lexit = dace.SDFGState("s" + str(state), sdfg, debuginfo=s.context) lend_val = str(self.initializer.get_dims()[-1]) for_exit = dace.sdfg.InterstateEdge(condition=dace.properties.CodeProperty.from_string( loop_guard_var + " >= " + lend_val, language=dace.dtypes.Language.Python)) sdfg.add_edge(s_guard, s_lexit, for_exit) return state
class BasicRegisterCache(Transformation): _before_state = dace.SDFGState() _loop_state = dace.SDFGState() _guard_state = dace.SDFGState() array = Property(dtype=str, desc='Name of the array to replace by a register cache') @staticmethod def expressions(): sdfg = dace.SDFG('_') before_state, loop_state, guard_state = ( BasicRegisterCache._before_state, BasicRegisterCache._loop_state, BasicRegisterCache._guard_state) sdfg.add_nodes_from((before_state, loop_state, guard_state)) sdfg.add_edge(before_state, guard_state, dace.InterstateEdge()) sdfg.add_edge(guard_state, loop_state, dace.InterstateEdge()) sdfg.add_edge(loop_state, guard_state, dace.InterstateEdge()) return [sdfg] @staticmethod def can_be_applied(graph, candidate, expr_index, sdfg, strict=False): return True def _buffer_memlets(self, states): for state in states: for edge in state.edges(): src, dst = edge.src, edge.dst if (isinstance(src, nodes.AccessNode) and src.data == self.array or isinstance(dst, nodes.AccessNode) and dst.data == self.array): yield edge.data def _get_loop_axis(self, loop_state, loop_var): def contains_loop_var(subset_range): return any(loop_var in {s.name for s in r.free_symbols} for r in subset_range) for memlet in self._buffer_memlets([loop_state]): return [contains_loop_var(r) for r in memlet.subset.ranges].index(True) def _get_buffer_size(self, state, loop_var, loop_axis): min_offset, max_offset = 1000, -1000 for memlet in self._buffer_memlets([state]): rb, re, _ = memlet.subset.ranges[loop_axis] rb_offset = rb - symbolic.symbol(loop_var) re_offset = re - symbolic.symbol(loop_var) min_offset = min(min_offset, rb_offset, re_offset) max_offset = max(max_offset, rb_offset, re_offset) return max_offset - min_offset + 1 def _replace_indices(self, states, loop_var, loop_axis, buffer_size): for memlet in self._buffer_memlets(states): rb, re, rs = memlet.subset.ranges[loop_axis] memlet.subset.ranges[loop_axis] = (rb % buffer_size, re % buffer_size, rs) def apply(self, sdfg: dace.SDFG): before_state = sdfg.node(self.subgraph[self._before_state]) loop_state = sdfg.node(self.subgraph[self._loop_state]) guard_state = sdfg.node(self.subgraph[self._guard_state]) loop_var = next(iter(sdfg.in_edges(guard_state)[0].data.assignments)) loop_axis = self._get_loop_axis(loop_state, loop_var) buffer_size = self._get_buffer_size(loop_state, loop_var, loop_axis) self._replace_indices(sdfg.states(), loop_var, loop_axis, buffer_size) array = sdfg.arrays[self.array] # TODO: generalize if array.shape[loop_axis] == array.total_size: array.shape = tuple(buffer_size if i == loop_axis else s for i, s in enumerate(array.shape)) array.total_size = buffer_size