Beispiel #1
0
def modify_psyir_tree():
    ''' Apply modifications to the PSyIR tree created in create.py

    :returns: a modified PSyIR tree.
    :rtype: :py:class:`psyclone.psyir.nodes.Container`

    '''
    file_container = create_psyir_tree()
    container = file_container.children[0]
    subroutine = container.children[0]

    # Rename one of the subroutine local symbols.
    tmp_symbol = subroutine.symbol_table.lookup("psyir_tmp")
    subroutine.symbol_table.rename_symbol(tmp_symbol, "new_variable")

    # The type of a symbol might be unknown
    symbol = Symbol("unused")
    container.symbol_table.add(symbol)
    # later its type could be determined. However, we don't want to
    # replace the existing symbol instance with a new instance as it
    # may have references, which could then lead to inconsistencies. Therefore
    # we support the specialise method, which transforms the existing
    # node type to a subclass of type without changing the memory
    # location of the instance. Note, any additional subclass properties would
    # have to be added manually.
    symbol.specialise(RoutineSymbol)

    # In some cases we may want to replace one node with another. This
    # can be simply done using a node's replace_with method.
    assignment = subroutine.children[2]
    assignment_rhs = assignment.rhs
    assignment_rhs.replace_with(Reference(tmp_symbol))

    return file_container
Beispiel #2
0
def test_symbol_specialise():
    '''Test the Symbol.specialise() method.'''
    # pylint: disable = unidiomatic-typecheck
    asym = Symbol("a")
    assert type(asym) is Symbol
    assert str(asym) == "a"
    asym.specialise(RoutineSymbol)
    assert type(asym) is RoutineSymbol
    assert str(asym) == "a : RoutineSymbol"
Beispiel #3
0
def test_symbol_copy():
    '''
    Test the Symbol.copy() method.
    '''
    csym = ContainerSymbol("some_mod")
    asym = Symbol("a", visibility=Symbol.Visibility.PRIVATE,
                  interface=GlobalInterface(csym))
    new_sym = asym.copy()
    assert new_sym is not asym
    assert new_sym.name == asym.name
    assert new_sym.interface == asym.interface
    assert new_sym.visibility == asym.visibility
Beispiel #4
0
def test_kernelfunctor_str():
    '''Check the str method of the KernelFunctor class.'''

    symbol = DataTypeSymbol("hello", StructureType())
    arg = Reference(Symbol("dummy"))
    klr = KernelFunctor.create(symbol, [arg])
    assert klr.__str__() == "KernelFunctor[name='hello']"
Beispiel #5
0
def test_kernelfunctor_node_str():
    '''Check the node_str method of the KernelFunctor class.'''

    symbol = DataTypeSymbol("hello", StructureType())
    arg = Reference(Symbol("dummy"))
    klr = KernelFunctor.create(symbol, [arg])
    coloredtext = colored("KernelFunctor", KernelFunctor._colour)
    assert klr.node_str() == coloredtext + "[name='hello']"
Beispiel #6
0
def test_create_struct_reference():
    ''' Tests for the _create_struct_reference() utility. '''
    one = Literal("1", INTEGER_TYPE)
    with pytest.raises(InternalError) as err:
        _create_struct_reference(None, StructureReference, Symbol("fake"),
                                 ["hello", 1], [])
    assert ("List of members must contain only strings or tuples but found "
            "entry of type 'int'" in str(err.value))
    with pytest.raises(NotImplementedError) as err:
        _create_struct_reference(None, StructureType, Symbol("fake"),
                                 ["hello"], [])
    assert "Cannot create structure reference for type '" in str(err.value)
    with pytest.raises(InternalError) as err:
        _create_struct_reference(None, StructureReference,
                                 DataSymbol("fake", DeferredType()), ["hello"],
                                 [one.copy()])
    assert ("Creating a StructureReference but array indices have been "
            "supplied" in str(err.value))
    with pytest.raises(InternalError) as err:
        _create_struct_reference(None, ArrayOfStructuresReference,
                                 DataSymbol("fake", DeferredType()), ["hello"],
                                 [])
    assert ("Cannot create an ArrayOfStructuresReference without one or more "
            "index expressions" in str(err.value))
    ref = _create_struct_reference(None, StructureReference,
                                   DataSymbol("fake", DeferredType()),
                                   ["hello"], [])
    assert isinstance(ref, StructureReference)
    assert isinstance(ref.member, Member)
    assert ref.member.name == "hello"
    # Check that we can create an ArrayOfStructuresReference and that any
    # PSyIR nodes are copied.
    idx_var = one.copy()
    idx_var2 = one.copy()
    aref = _create_struct_reference(None, ArrayOfStructuresReference,
                                    DataSymbol("fake", DeferredType()),
                                    ["a", ("b", [idx_var2])], [idx_var])
    assert isinstance(aref, ArrayOfStructuresReference)
    assert isinstance(aref.member, StructureMember)
    assert aref.member.name == "a"
    assert aref.member.member.name == "b"
    assert len(aref.member.member.indices) == 1
    assert aref.member.member.indices[0] is not idx_var2
    assert len(aref.indices) == 1
    assert aref.indices[0] is not idx_var
Beispiel #7
0
def test_kernelfunctor_invalid_symbol():
    '''Check KernelFunctor raises the expected exception if the type of
    the symbol argument is invalid.

    '''
    with pytest.raises(TypeError) as info:
        _ = KernelFunctor(Symbol("hello"))
    assert ("KernelFunctor symbol argument should be a DataTypeSymbol but "
            "found 'Symbol'." in str(info.value))
Beispiel #8
0
def test_kernelfunctor_create_invalid_symbol():
    '''Check that the create method of KernelFunctor raises the expected
    exception if the provided symbol argument is not the correct type.

    '''
    symbol = Symbol("hello")
    with pytest.raises(GenerationError) as info:
        _ = KernelFunctor.create(symbol, [])
    assert ("KernelFunctor create() symbol argument should be a DataTypeSymbol"
            " but found 'Symbol'." in str(info.value))
Beispiel #9
0
def test_symbol_interface_setter():
    '''Test that the Symbol interface setter behaves as expected,
    including raising an exception if the input is of the wrong
    type. Also use this to test the is_local, is_global and
    is_argument and is_unresolved properties.

    '''
    symbol = Symbol('sym1')
    assert symbol.is_local
    assert not symbol.is_global
    assert not symbol.is_argument
    assert not symbol.is_unresolved

    symbol.interface = GlobalInterface(ContainerSymbol("my_mod"))
    assert not symbol.is_local
    assert symbol.is_global
    assert not symbol.is_argument
    assert not symbol.is_unresolved

    symbol.interface = ArgumentInterface()
    assert not symbol.is_local
    assert not symbol.is_global
    assert symbol.is_argument
    assert not symbol.is_unresolved

    symbol.interface = UnresolvedInterface()
    assert not symbol.is_local
    assert not symbol.is_global
    assert not symbol.is_argument
    assert symbol.is_unresolved

    with pytest.raises(TypeError) as info:
        symbol.interface = "hello"
    assert ("The interface to a Symbol must be a SymbolInterface but got "
            "'str'" in str(info.value))
Beispiel #10
0
def test_symbol_initialisation():
    '''Test that a Symbol instance can be created when valid arguments are
    given, otherwise raise relevant exceptions. Also tests the
    internal Visibility class, the name, visibility and interface properties.

    '''
    sym = Symbol("sym1")
    assert isinstance(sym, Symbol)
    assert sym.name == "sym1"
    assert sym.visibility == Symbol.DEFAULT_VISIBILITY
    assert isinstance(sym.interface, LocalInterface)
    # Check that the default visibility is public
    assert Symbol.DEFAULT_VISIBILITY == Symbol.Visibility.PUBLIC

    sym = Symbol("sym2", Symbol.Visibility.PRIVATE)
    assert sym.visibility == Symbol.Visibility.PRIVATE

    sym = Symbol("sym3", interface=UnresolvedInterface())
    assert isinstance(sym.interface, UnresolvedInterface)

    with pytest.raises(TypeError) as error:
        sym = Symbol(None)
    assert ("Symbol 'name' attribute should be of type 'str'"
            in str(error.value))

    with pytest.raises(TypeError) as error:
        Symbol('sym1', visibility="hello")
    assert ("Symbol 'visibility' attribute should be of type "
            "psyir.symbols.Symbol.Visibility but" in str(error.value))

    with pytest.raises(TypeError) as error:
        Symbol('sym1', interface="hello")
    assert ("The interface to a Symbol must be a SymbolInterface but got "
            "'str'" in str(error.value))
Beispiel #11
0
def test_kernelfunctor_create(cls):
    '''Check that the create method of KernelFunctor works as expected.

    '''
    symbol = DataTypeSymbol("hello", StructureType())
    klr = cls.create(symbol, [])
    # pylint: disable=unidiomatic-typecheck
    assert type(klr) is cls
    assert klr._symbol == symbol
    assert len(klr.children) == 0

    arg = Reference(Symbol("dummy"))
    klr = KernelFunctor.create(symbol, [arg])
    assert len(klr.children) == 1
    assert klr.children[0] == arg
    assert arg.parent == klr
Beispiel #12
0
def test_specialise_symbol():
    '''Test that the specialise_symbol method work as expected.

    '''
    symbol = Symbol("hello")

    # Check that a Symbol is specialised
    InvokeCallTrans._specialise_symbol(symbol)
    assert isinstance(symbol, TypeSymbol)
    # pylint: disable=no-member
    assert isinstance(symbol.datatype, StructureType)

    # Check that something that is not a symbol is ignored
    test = "hello"
    InvokeCallTrans._specialise_symbol(test)
    assert isinstance(test, str)
    assert test == "hello"
Beispiel #13
0
    def name_from_tag(self, tag, root=None, check_ancestors=True):
        '''If the supplied tag exists in this symbol table (if the
        `check_ancestors` argument is False) or in this or any
        ancestor symbol table (if the `check_ancestors` argument is
        True), then return the symbol name associated with it,
        otherwise create a new symbol associated with this tag (using
        the tag as name or optionally the provided root) and return
        the name of the new symbol.

        Note that this method creates generic Symbols without properties like
        datatypes and just returns the name string (not the Symbol object).
        This is commonly needed on the current psy-layer implementation but not
        recommended on new style PSyIR. This method may be deprecated in the
        future. (TODO #720)

        There is no need to check the argument types as this method
        calls methods which check the argument types.

        :param str tag: tag identifier.
        :param str root: optional name of the new symbols if this needs to \
            be created.
        :param bool check_ancestors: optional logical flag indicating \
            whether the tag should be from just this symbol table \
            (False) or this and all ancestor symbol tables \
            (True). Defaults to True.

        :returns: name associated with the given tag.
        :rtype: str

        '''
        try:
            return self.lookup_with_tag(tag,
                                        check_ancestors=check_ancestors).name
        except KeyError:
            if root:
                name = self.new_symbol_name(root,
                                            check_ancestors=check_ancestors)
            else:
                name = self.new_symbol_name(tag,
                                            check_ancestors=check_ancestors)
            # No need to check ancestors as this has already been done
            # when creating the name.
            self.add(Symbol(name), tag=tag, check_ancestors=False)
            return name
Beispiel #14
0
def test_symbol_array_handling(fortran_reader):
    '''Verifies the handling of arrays together with access information.

    '''
    # Make sure that a normal `Symbol` raises an exception if it is tested
    # if it is an array. A `Symbol` is only used if there is no type
    # information is available, e.g. because it is imported from another
    # module:
    asym = Symbol("a")
    with pytest.raises(ValueError) as error:
        _ = asym.is_array
    assert "No array information is available for the symbol 'a'." \
        in str(error.value)

    # Import additional tests from access_info_test to reach 100% coverage
    # for the is_array_access function. Import these tests locally only.
    # pylint: disable=import-outside-toplevel
    from psyclone.tests.core.access_info_test import \
        test_symbol_array_detection
    test_symbol_array_detection(fortran_reader)
Beispiel #15
0
def test_symbol_resolve_deferred(monkeypatch):
    ''' Test the resolve_deferred method. '''
    # resolve_deferred() for a local symbol has nothing to do so should
    # just return itself.
    asym = Symbol("a")
    assert asym.resolve_deferred() is asym
    # Now test for a symbol that is imported from another Container
    other_container = ContainerSymbol("some_mod")
    bsym = Symbol("b", visibility=Symbol.Visibility.PRIVATE,
                  interface=GlobalInterface(other_container))
    # Monkeypatch the get_external_symbol() method so that it just returns
    # a new DataSymbol
    monkeypatch.setattr(bsym, "get_external_symbol",
                        lambda: DataSymbol("b", INTEGER_SINGLE_TYPE))
    new_sym = bsym.resolve_deferred()
    # We should have a brand new symbol but with some of the properties
    # of the original 'bsym' symbol.
    assert new_sym is not bsym
    assert new_sym.datatype == INTEGER_SINGLE_TYPE
    assert new_sym.visibility == Symbol.Visibility.PRIVATE
    assert new_sym.is_global
Beispiel #16
0
    def create_driver(self, input_list, output_list):
        # pylint: disable=too-many-locals, too-many-statements
        '''This function creates a driver that can read the
        output created by the extraction code. This is a stand-alone
        program that will read the input data, calls the kernels/
        instrumented region, and then compares the results with the
        stored results in the file.

        TODO: #644: we need type information here.

        :param input_list: list of variables that are input parameters.
        :type input_list: list of str
        :param output_list: list of variables that are output parameters.
        :type output_list: list or str
        '''

        from psyclone.f2pygen import AllocateGen, AssignGen, CallGen,\
            CommentGen, DeclGen, ModuleGen, SubroutineGen, UseGen, \
            TypeDeclGen
        from psyclone.gocean1p0 import GOSymbolTable
        from psyclone.psyir.symbols import Symbol

        all_vars = list(set(input_list).union(set(output_list)))
        all_vars.sort()

        module_name, region_name = self.region_identifier
        module = ModuleGen(name=module_name)
        prog = SubroutineGen(parent=module, name=module_name+"_code",
                             implicitnone=True)
        module.add(prog)
        use = UseGen(prog, self.add_psydata_class_prefix("psy_data_mod"),
                     only=True,
                     funcnames=[self.add_psydata_class_prefix("PSyDataType")])
        prog.add(use)

        # Use a symbol table to make sure all variable names are unique
        sym_table = GOSymbolTable()
        sym = Symbol("PSyDataType")
        sym_table.add(sym)

        psy_data = sym_table.new_symbol_name(self.add_psydata_class_prefix
                                             ("psy_data"))
        sym_table.add(Symbol(psy_data))
        var_decl = TypeDeclGen(prog, datatype=self.add_psydata_class_prefix
                               ("PSyDataType"),
                               entity_decls=[psy_data])
        prog.add(var_decl)

        call = CallGen(prog,
                       "{0}%OpenRead(\"{1}\", \"{2}\")"
                       .format(psy_data, module_name, region_name))
        prog.add(call)

        post_suffix = self._post_name

        # Variables might need to be renamed in order to guarantee unique
        # variable names in the driver: An example of this would be if the
        # user code contains a variable 'dx', and the kernel takes a
        # property 'dx' as well. In the original code that is no problem,
        # since the property is used via field%grid%dx. But the stand-alone
        # driver renames field%grid%dx to dx, which can cause a name clash.
        # Similar problems can exist with any user defined type, since all
        # user defined types are rewritten to just use the field name.
        # We use a mapping to support renaming of variables: it takes as
        # key the variable as used in the original program (e.g. 'dx' from
        # an expression like field%grid%dx), and maps it to a unique local
        # name (e.g. dx_0).

        rename_variable = {}
        for var_name in all_vars:
            # TODO #644: we need to identify arrays!!
            # Support GOcean properties, which are accessed via a
            # derived type (e.g. 'fld%grid%dx'). In this stand-alone
            # driver we don't have the derived type, instead we create
            # variable based on the field in the derived type ('dx'
            # in the example above), and pass this variable to the
            # instrumented code.
            last_percent = var_name.rfind("%")
            if last_percent > -1:
                # Strip off the derived type, and only leave the last
                # field, which is used as the local variable name.
                local_name = var_name[last_percent+1:]
            else:
                # No derived type, so we can just use the
                # variable name directly in the driver
                local_name = var_name
            unique_local_name = sym_table.new_symbol_name(local_name)
            rename_variable[local_name] = unique_local_name
            sym_table.add(Symbol(unique_local_name))
            local_name = unique_local_name

            # TODO: #644 - we need to identify arrays!!
            # Any variable used needs to be defined. We also need
            # to handle the kind property better and not rely on
            # a hard-coded value.
            decl = DeclGen(prog, "real", [local_name], kind="8",
                           dimension=":,:", allocatable=True)
            prog.add(decl)
            is_input = var_name in input_list
            is_output = var_name in output_list

            if is_input and not is_output:
                # We only need the pre-variable, and we can read
                # it from the file (this call also allocates space for it).
                call = CallGen(prog,
                               "{0}%ReadVariable(\"{1}\", {2})"
                               .format(psy_data, var_name, local_name))
                prog.add(call)
            elif is_input:
                # Now must be input and output:
                # First read the pre-variable (which also allocates it):
                call = CallGen(prog,
                               "{0}%ReadVariable(\"{1}\", {2})"
                               .format(psy_data, var_name, local_name))
                prog.add(call)
                # Then declare the post variable, and and read its values
                # (ReadVariable will also allocate it)
                sym = Symbol(local_name+post_suffix)
                sym_table.add(sym)
                decl = DeclGen(prog, "real", [local_name+post_suffix],
                               dimension=":,:", kind="8", allocatable=True)
                prog.add(decl)
                call = CallGen(prog,
                               "{0}%ReadVariable(\"{1}{3}\", {2}{3})"
                               .format(psy_data, var_name, local_name,
                                       post_suffix))
                prog.add(call)
            else:
                # Now the variable is output only. We need to read the
                # post variable in, and create and allocate a pre variable
                # with the same size as the post
                sym = Symbol(local_name+post_suffix)
                sym_table.add(sym)
                decl = DeclGen(prog, "real", [local_name+post_suffix],
                               dimension=":,:", kind="8", allocatable=True)
                prog.add(decl)
                call = CallGen(prog,
                               "{0}%ReadVariable(\"{1}{3}\", {2}{3})"
                               .format(psy_data, var_name, local_name,
                                       post_suffix))
                prog.add(call)
                decl = DeclGen(prog, "real", [local_name], kind="8",
                               dimension=":,:", allocatable=True)
                prog.add(decl)
                alloc = AllocateGen(prog, [var_name],
                                    mold="{0}".format(local_name +
                                                      post_suffix))
                prog.add(alloc)
                # Initialise the variable with 0, since it might contain
                # values that are not set at all (halo regions, or a
                # kernel might not set all values). This way the array
                # comparison with the post value works as expected
                # TODO #644 - create the right "0.0" type here (e.g.
                # 0.0d0, ...)
                assign = AssignGen(prog, local_name, "0.0d0")
                prog.add(assign)

        # Now add the region that was extracted here:
        prog.add(CommentGen(prog, ""))
        prog.add(CommentGen(prog, " RegionStart"))

        # For the driver we have to re-create the code of the
        # instrumented region, but in this stand-alone driver the
        # arguments are not dl_esm_inf fields anymore, but simple arrays.
        # Similarly, for properties we cannot use e.g. 'fld%grid%dx'
        # anymore, we have to use e.g. a local variable 'dx' that has
        # been created. Since we are using the existing way of creating
        # the code for the instrumented region, we need to modify how
        # these variables are created. We do this by temporarily
        # modifying the properties in the config file.
        api_config = Config.get().api_conf("gocean1.0")
        all_props = api_config.grid_properties
        # Keep a copy of the original values, so we can restore
        # them later
        orig_props = dict(all_props)

        # 1) A grid property is defined like "{0}%grid%dx". This is
        #    changed to be just 'dx', i.e. the final component of
        #    the current value (but we also take renaming into account,
        #    so 'dx' might become 'dx_0').
        #    If a property is not used, it doesn't matter if we modify
        #    its definition, so we just change all properties.
        for name, prop in all_props.items():
            last_percent = prop.fortran.rfind("%")
            if last_percent > -1:
                # Get the last field name, which will be the
                # local variable name
                local_name = prop.fortran[last_percent+1:]
                unique_name = rename_variable.get(local_name, local_name)
                all_props[name] = GOceanConfig.make_property(
                    unique_name, prop.type, prop.intrinsic_type)

        # 2) The property 'grid_data' is a reference to the data on the
        #    grid (i.e. the actual field) , and it is defined as "{0}%data".
        #    This just becomes {0} ('a_fld%data' in the original program
        #    becomes just 'a_fld', and 'a_fld' is declared to be a plain
        #    Fortran 2d-array)
        all_props["go_grid_data"] = GOceanConfig.make_property(
            "{0}", "array", "real")

        # Each kernel caches the argument code, so we also
        # need to clear this cached data to make sure the new
        # value for "go_grid_data" is actually used.
        from psyclone.psyGen import CodedKern
        for kernel in self.psy_data_body.walk(CodedKern):
            kernel.clear_cached_data()

        # Recreate the instrumented region. Due to the changes in the
        # config files, fields and properties will now become local
        # plain arrays and variables:
        for child in self.psy_data_body:
            child.gen_code(prog)

        # Now reset all properties back to the original values:
        for name in all_props.keys():
            all_props[name] = orig_props[name]

        prog.add(CommentGen(prog, " RegionEnd"))
        prog.add(CommentGen(prog, ""))

        for var_name in output_list:
            prog.add(CommentGen(prog, " Check {0}".format(var_name)))

        code = str(module.root)

        with open("driver-{0}-{1}.f90".
                  format(module_name, region_name), "w") as out:
            out.write(code)
Beispiel #17
0
def test_symbol_str():
    '''Test that a Symbol instance can be stringified'''

    sym = Symbol("my_symbol")
    assert str(sym) == "my_symbol"
Beispiel #18
0
    def __init__(self, ast=None, children=None, parent=None, options=None):

        if not options:
            options = {}

        # This string stores a prefix to be used with all external PSyData
        # symbols (i.e. data types and module name), used in the
        # method 'add_psydata_class_prefix'.
        self._class_string = options.get("prefix", "")
        if self._class_string:
            self._class_string = self._class_string + "_"

        # Root of the name to use for variables associated with
        # PSyData regions
        self._psy_data_symbol_with_prefix = \
            self.add_psydata_class_prefix("psy_data")

        # The use statement that will be inserted. Any use of a module
        # of the same name that doesn't match this will result in a
        # NotImplementedError at code-generation time.
        self.use_stmt = "use {0}, only: "\
            .format(self.add_psydata_class_prefix("psy_data_mod")) + \
            ", ".join(self.add_psydata_class_prefix(symbol) for symbol in
                      PSyDataNode.symbols)

        if children:
            # We need to store the position of the original children,
            # i.e. before they are added to a schedule
            node_position = children[0].position

        # A PSyData node always contains a Schedule
        sched = self._insert_schedule(children)
        super(PSyDataNode, self).__init__(ast=ast,
                                          children=[sched],
                                          parent=parent)

        # Get or create a symbol table so we can avoid name clashes
        # when creating variables
        if parent and hasattr(self.root, 'symbol_table'):
            symtab = self.root.symbol_table
        else:
            # FIXME: This may not be a good solution
            symtab = SymbolTable()

        # Store the name of the PSyData variable that is used for this
        # PSyDataNode. This allows the variable name to be shown in str
        # (and also, calling create_name in gen() would result in the name
        # being changed every time gen() is called).
        self._var_name = symtab.new_symbol_name(
            self._psy_data_symbol_with_prefix)
        symtab.add(Symbol(self._var_name))

        if children and parent:
            # Correct the parent's list of children. Use a slice of the list
            # of nodes so that we're looping over a local copy of the list.
            # Otherwise things get confused when we remove children from
            # the list.
            for child in children[:]:
                # Remove child from the parent's list of children
                parent.children.remove(child)

            # Add this node as a child of the parent
            # of the nodes being enclosed and at the original location
            # of the first of these nodes
            parent.addchild(self, index=node_position)
        elif parent:
            parent.addchild(self)

        # Name of the region. In general at constructor time we might
        # not have a parent subroutine or any child nodes, so
        # the name is left empty, unless explicitly provided by the
        # user. If names are not provided here then the region and
        # module names are set the first time gen() is called (and
        # then remain unchanged).
        self._module_name = None
        self._region_name = None
        # The region identifier caches the computed module- and region-name
        # as a tuple of strings. This is required so that a derived class can
        # query the actual name of region (e.g. during generation of a driver
        # for an extract node). If the user does not define a name, i.e.
        # module_name and region_name are empty, a unique name will be
        # computed in gen_code(). If this name would then be stored in
        # module_name and region_name, and gen() is called again, the
        # names would not be computed again, since the code detects already
        # defined module and region names. This can then result in duplicated
        # region names: The test 'test_region' in profile_test triggers this.
        # gen()) is called first after one profile region is applied, then
        # another profile region is added, and gen() is called again. The
        # second profile region would compute a new name, which then happens
        # to be the same as the name computed for the first region in the
        # first gen_code call (which indeed implies that the name of the
        # first profile region is different the second time it is computed).
        # So in order to guarantee that the computed module and region names
        # are unique when gen_code is called more than once, we
        # cannot store a computed name in module_name and region_name.
        self._region_identifier = ("", "")

        name = options.get("region_name", None)

        if name:
            # pylint: disable=too-many-boolean-expressions
            if not isinstance(name, tuple) or not len(name) == 2 or \
               not name[0] or not isinstance(name[0], str) or \
               not name[1] or not isinstance(name[1], str):
                raise InternalError("Error in PSyDataNode. The name must be a "
                                    "tuple containing two non-empty strings.")
            # pylint: enable=too-many-boolean-expressions
            # Valid PSyData names have been provided by the user.
            self._module_name = name[0]
            self._region_name = name[1]
            self.set_region_identifier(self._module_name, self._region_name)
Beispiel #19
0
def test_get_external_symbol(monkeypatch):
    ''' Test the get_external_symbol() method. '''
    asym = Symbol("a")
    with pytest.raises(NotImplementedError) as err:
        asym.get_external_symbol()
    assert ("trying to resolve symbol 'a' properties, the lazy evaluation "
            "of 'Local' interfaces is not supported" in str(err.value))
    other_container = ContainerSymbol("some_mod")
    ctable = SymbolTable()
    ctable.add(other_container)
    # Create a Symbol that is imported from the "some_mod" Container
    bsym = Symbol("b", interface=GlobalInterface(other_container))
    ctable.add(bsym)
    _ = Container.create("test", ctable, [KernelSchedule("dummy")])

    # Monkeypatch the container's FortranModuleInterface so that it always
    # appears to be unable to find the "some_mod" module

    def fake_import(name):
        raise SymbolError("Oh dear")

    monkeypatch.setattr(other_container._interface, "import_container",
                        fake_import)
    with pytest.raises(SymbolError) as err:
        bsym.get_external_symbol()
    assert ("trying to resolve the properties of symbol 'b' in module "
            "'some_mod': PSyclone SymbolTable error: Oh dear"
            in str(err.value))
    # Now create a Container for the 'some_mod' module and attach this to
    # the ContainerSymbol
    ctable2 = SymbolTable()
    some_mod = Container.create("some_mod", ctable2,
                                [KernelSchedule("dummy2")])
    other_container._reference = some_mod
    # Currently the Container does not contain an entry for 'b'
    with pytest.raises(SymbolError) as err:
        bsym.get_external_symbol()
    assert ("trying to resolve the properties of symbol 'b'. The interface "
            "points to module 'some_mod' but could not find the definition"
            in str(err.value))
    # Add an entry for 'b' to the Container's symbol table
    ctable2.add(DataSymbol("b", INTEGER_SINGLE_TYPE))
    new_sym = bsym.resolve_deferred()
    assert isinstance(new_sym, DataSymbol)
    assert new_sym.datatype == INTEGER_SINGLE_TYPE
Beispiel #20
0
def test_find_symbol_table():
    ''' Test the find_symbol_table() method. '''
    sym = Symbol("a_var")
    with pytest.raises(TypeError) as err:
        sym.find_symbol_table("3")
    assert ("expected to be passed an instance of psyir.nodes.Node but got "
            "'str'" in str(err.value))
    # Search for a SymbolTable with only one level of hierarchy
    sched = KernelSchedule("dummy")
    table = sched.symbol_table
    table.add(sym)
    assert sym.find_symbol_table(sched) is table
    # Create a Container so that we have two levels of hierarchy
    ctable = SymbolTable()
    sym2 = Symbol("b_var")
    ctable.add(sym2)
    _ = Container.create("test", ctable, [sched])
    assert sym2.find_symbol_table(sched) is ctable
    # A Symbol that isn't in any table
    sym3 = Symbol("missing")
    assert sym3.find_symbol_table(sched) is None
    # When there is no SymbolTable associated with the PSyIR node
    orphan = Literal("1", INTEGER_SINGLE_TYPE)
    assert sym3.find_symbol_table(orphan) is None