Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
    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()