def test_variables_access_info(): '''Test the implementation of VariablesAccessInfo, a class that manages a list of variables, each with a list of accesses. ''' var_accesses = VariablesAccessInfo() node1 = Node() var_accesses.add_access(Signature("read"), AccessType.READ, node1) node2 = Node() var_accesses.add_access(Signature("written"), AccessType.WRITE, node2) assert str(var_accesses) == "read: READ, written: WRITE" var_accesses.next_location() node = Node() var_accesses.add_access(Signature("written"), AccessType.WRITE, node) var_accesses.next_location() var_accesses.add_access(Signature("read_written"), AccessType.WRITE, node) var_accesses.add_access(Signature("read_written"), AccessType.READ, node) assert str(var_accesses) == "read: READ, read_written: READ+WRITE, "\ "written: WRITE" assert set(var_accesses.all_signatures) == set([Signature("read"), Signature("written"), Signature("read_written")]) all_accesses = var_accesses[Signature("read")].all_accesses assert all_accesses[0].node == node1 written_accesses = var_accesses[Signature("written")].all_accesses assert written_accesses[0].location == 0 assert written_accesses[1].location == 1 # Check that the location pointer is pointing to the next statement: assert var_accesses.location == 2 # Create a new instance var_accesses2 = VariablesAccessInfo() var_accesses2.add_access(Signature("new_var"), AccessType.READ, node) var_accesses2.add_access(Signature("written"), AccessType.READ, node) # Now merge the new instance with the previous instance: var_accesses.merge(var_accesses2) assert str(var_accesses) == "new_var: READ, read: READ, " \ "read_written: READ+WRITE, written: READ+WRITE" with pytest.raises(KeyError): _ = var_accesses[Signature("does_not_exist")] with pytest.raises(KeyError): var_accesses.is_read(Signature("does_not_exist")) with pytest.raises(KeyError): var_accesses.is_written(Signature("does_not_exist")) assert "READWRITE" not in str(var_accesses) var_accesses.add_access(Signature("readwrite"), AccessType.READWRITE, node) assert "READWRITE" in str(var_accesses) with pytest.raises(InternalError) as err: var_accesses.add_access("no-signature", AccessType.READWRITE, node) assert "Got 'no-signature' of type 'str' but expected it to be of type " \ "psyclone.core.Signature." in str(err.value)
def test_variables_access_info_merge(): '''Tests the merge operation of VariablesAccessInfo. ''' # First create one instance representing for example: # a=b; c=d var_accesses1 = VariablesAccessInfo() node = Node() var_accesses1.add_access(Signature("b"), AccessType.READ, node) var_accesses1.add_access(Signature("a"), AccessType.WRITE, node) var_accesses1.next_location() var_accesses1.add_access(Signature("d"), AccessType.READ, node) var_accesses1.add_access(Signature("c"), AccessType.WRITE, node) c_accesses = var_accesses1[Signature("c")] assert len(c_accesses.all_accesses) == 1 assert c_accesses[0].access_type == AccessType.WRITE # First create one instance representing for example: # e=f; g=h var_accesses2 = VariablesAccessInfo() var_accesses2.add_access(Signature("f"), AccessType.READ, node) var_accesses2.add_access(Signature("e"), AccessType.WRITE, node) var_accesses2.next_location() var_accesses2.add_access(Signature("h"), AccessType.READ, node) var_accesses2.add_access(Signature("g"), AccessType.WRITE, node) # Now merge the second instance into the first one var_accesses1.merge(var_accesses2) # The e=f access pattern should have the same location # as the c=d (since there is no next_location after # adding the b=a access): c_accesses = var_accesses1[Signature("c")] e_accesses = var_accesses1[Signature("e")] assert c_accesses[0].access_type == AccessType.WRITE assert e_accesses[0].access_type == AccessType.WRITE assert c_accesses[0].location == e_accesses[0].location # Test that the g=h part has a higher location than the # c=d data. This makes sure that merge() increases the # location number of accesses when merging. c_accesses = var_accesses1[Signature("c")] g_accesses = var_accesses1[Signature("g")] h_accesses = var_accesses1[Signature("h")] assert c_accesses[0].location < g_accesses[0].location assert g_accesses[0].location == h_accesses[0].location # Also make sure that the access location was properly increased # Originally we had locations 0,1. Then we merged accesses with # location 0,1 in - the one at 0 is merged with the current 1, # and the new location 1 increases the current location from # 1 to 2: assert var_accesses1.location == 2
def reference_accesses(self, var_accesses): '''Get all variable access information from this node. The assigned-to variable will be set to 'WRITE'. :param var_accesses: VariablesAccessInfo instance that stores the \ information about variable accesses. :type var_accesses: \ :py:class:`psyclone.core.access_info.VariablesAccessInfo` ''' # It is important that a new instance is used to handle the LHS, # since a check in 'change_read_to_write' makes sure that there # is only one access to the variable! accesses_left = VariablesAccessInfo() self.lhs.reference_accesses(accesses_left) # Now change the (one) access to the assigned variable to be WRITE: if isinstance(self.lhs, StructureReference): # TODO #1028: Assignment to user defined type, not supported yet. # Here an absolute hack to get at least some information out # from the AST - though indices are just strings, which will # likely cause problems later as well. # Here a very bad hack to reach full coverage: re-create the # Fortran string using a Fortran writer, then the code below # will still work as expected. Note that this whole code # section will be removed in #1028. Leaving the local import # here to make sure it will be removed as well. # pylint: disable=import-outside-toplevel from psyclone.psyir.backend.fortran import FortranWriter writer = FortranWriter() name = writer(self.lhs) # A regular expression that tries to find the last parenthesis # pair in the name ("a(i,j)" --> "(i,j)") ind = re.search(r"\([^\(]+\)$", name) if ind: # Remove the index part of the name name = name.replace(ind.group(0), "") # The index must be added as a list accesses_left.add_access(Signature(name), AccessType.WRITE, self, [ind.group(0)]) else: accesses_left.add_access(Signature(name), AccessType.WRITE, self) else: var_info = accesses_left[Signature(self.lhs.name)] try: var_info.change_read_to_write() except InternalError as err: # An internal error typically indicates that the same variable # is used twice on the LHS, e.g.: g(g(1)) = ... This is not # supported in PSyclone. six.raise_from( ParseError("The variable '{0}' appears more than once " "on the left-hand side of an assignment." .format(self.lhs.name)), err) # Merge the data (that shows now WRITE for the variable) with the # parameter to this function. It is important that first the # RHS is added, so that in statements like 'a=a+1' the read on # the RHS comes before the write on the LHS (they have the same # location otherwise, but the order is still important) self.rhs.reference_accesses(var_accesses) var_accesses.merge(accesses_left) var_accesses.next_location()