Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions src/psyclone/domain/gocean/kernel/psyir.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
from psyclone.domain.gocean import GOceanConstants
from psyclone.errors import InternalError
from psyclone.parse.utils import ParseError
from psyclone.psyir.backend.fortran import FortranWriter
from psyclone.psyir.frontend.fortran import FortranReader
from psyclone.psyir.nodes import Container
from psyclone.psyir.symbols import DataTypeSymbol, UnsupportedFortranType
from psyclone.psyir.symbols import (
DataTypeSymbol, StructureType, UnsupportedFortranType)


class GOceanContainer(Container):
Expand Down Expand Up @@ -193,16 +195,21 @@ def create_from_psyir(symbol):

datatype = symbol.datatype

if not isinstance(datatype, UnsupportedFortranType):
raise InternalError(
f"Expected kernel metadata to be stored in the PSyIR as "
f"an UnsupportedFortranType, but found "
f"{type(datatype).__name__}.")

# In an UnsupportedFortranType, the declaration is stored as a
# string, so use create_from_fortran_string()
return GOceanKernelMetadata.create_from_fortran_string(
datatype.declaration)
if isinstance(datatype, StructureType):
# TODO #239: GOceanKernelMetadata.create_from_psyir will
# replace this
declaration = FortranWriter().gen_typedecl(
symbol, include_visibility=False)
# Preserve the spelling
declaration = declaration.replace(
"go_stencil(", "GO_STENCIL(")
return GOceanKernelMetadata.create_from_fortran_string(
declaration)

raise InternalError(
f"Expected kernel metadata to be stored in the PSyIR as "
f"an StructureType, but found "
f"{type(datatype).__name__}.")

@staticmethod
def create_from_fortran_string(fortran_string):
Expand Down
25 changes: 14 additions & 11 deletions src/psyclone/domain/lfric/kernel/lfric_kernel_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@
from psyclone.domain.lfric.kernel.shapes_metadata import ShapesMetadata
from psyclone.errors import InternalError
from psyclone.parse.utils import ParseError
from psyclone.psyir.backend.fortran import FortranWriter
from psyclone.psyir.frontend.fortran import FortranReader
from psyclone.psyir.symbols import DataTypeSymbol, UnsupportedFortranType
from psyclone.psyir.symbols import (
DataTypeSymbol, StructureType, UnsupportedFortranType)

# pylint: disable=too-many-lines
# pylint: disable=too-many-instance-attributes
Expand Down Expand Up @@ -669,16 +671,17 @@ def create_from_psyir(symbol):

datatype = symbol.datatype

if not isinstance(datatype, UnsupportedFortranType):
raise InternalError(
f"Expected kernel metadata to be stored in the PSyIR as "
f"an UnsupportedFortranType, but found "
f"{type(datatype).__name__}.")

# In an UnsupportedFortranType, the declaration is stored as a
# string, so use create_from_fortran_string()
return LFRicKernelMetadata.create_from_fortran_string(
datatype.declaration)
if isinstance(datatype, StructureType):
# TODO #239: LFricKernelMetadata.create_from_psyir will
# replace this
declaration = FortranWriter().gen_typedecl(
symbol, include_visibility=False)
return LFRicKernelMetadata.create_from_fortran_string(declaration)

raise InternalError(
f"Expected kernel metadata to be stored in the PSyIR as "
f"an StructureType, but found "
f"{type(datatype).__name__}.")

@staticmethod
def create_from_fparser2(fparser2_tree):
Expand Down
16 changes: 10 additions & 6 deletions src/psyclone/psyad/domain/lfric/lfric_adjoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
from psyclone.psyad import AdjointVisitor
from psyclone.psyad.domain.common import create_adjoint_name
from psyclone.psyir.nodes import Routine
from psyclone.psyir.symbols import ContainerSymbol, UnsupportedFortranType
from psyclone.psyir.symbols.symbol import ArgumentInterface, ImportInterface
from psyclone.psyir.symbols import ContainerSymbol, StructureType
from psyclone.psyir.symbols.symbol import (
ArgumentInterface, ImportInterface, UnresolvedInterface)


# pylint: disable=too-many-locals
Expand All @@ -52,8 +53,9 @@ def generate_lfric_adjoint(tl_psyir, active_variables):
# linear kernel.
tl_container = find_container(tl_psyir)
for sym in tl_container.symbol_table.datatypesymbols:
if (isinstance(sym.datatype, UnsupportedFortranType) and
"extends(kernel_type)" in sym.datatype.declaration.lower()):
if (isinstance(sym.datatype, StructureType) and
sym.datatype.extends and
sym.datatype.extends.name.lower() == "kernel_type"):
tl_metadata_name = sym.name
break
else:
Expand Down Expand Up @@ -246,8 +248,12 @@ def _check_or_add_access_symbol(container, access):
'''
kernel = container.children[0]
symbol_table = kernel.symbol_table
arg_mod_symbol = symbol_table.find_or_create(
"argument_mod", symbol_type=ContainerSymbol)
try:
argument_mod_symbol = symbol_table.lookup(access)
if isinstance(argument_mod_symbol.interface, UnresolvedInterface):
argument_mod_symbol.interface = ImportInterface(arg_mod_symbol)
if not isinstance(argument_mod_symbol.interface, ImportInterface):
raise GenerationError(
f"The existing symbol '{access}' is not imported from a use "
Expand All @@ -259,8 +265,6 @@ def _check_or_add_access_symbol(container, access):
f"'{argument_mod_symbol.interface.container_symbol.name}' but "
f"should be imported from 'argument_mod'.")
except KeyError:
arg_mod_symbol = symbol_table.find_or_create(
"argument_mod", symbol_type=ContainerSymbol)
symbol_table = arg_mod_symbol.find_symbol_table(kernel)
symbol_table.new_symbol(
root_name=access, interface=ImportInterface(arg_mod_symbol))
41 changes: 24 additions & 17 deletions src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from psyclone.domain.lfric.algorithm.lfric_alg import LFRicAlg
from psyclone.domain.lfric.algorithm.psyir import (
LFRicAlgorithmInvokeCall, LFRicBuiltinFunctorFactory, LFRicKernelFunctor)
from psyclone.domain.lfric.kernel import LFRicKernelMetadata
from psyclone.domain.lfric.transformations import RaisePSyIR2LFRicKernTrans
from psyclone.errors import InternalError, GenerationError
from psyclone.psyad.domain.common.adjoint_utils import (
Expand Down Expand Up @@ -538,32 +539,38 @@ def generate_lfric_adjoint_harness(tl_psyir, coord_arg_idx=None,
tl_subroutine_table = tl_subroutine.symbol_table
tl_argument_list = tl_subroutine_table.argument_list

# Parse the kernel metadata. This still uses fparser1 as that's what
# the meta-data handling is currently based upon. We therefore have to
# convert back from PSyIR to Fortran for the moment.
# TODO #2151 - replace this with the new PSyIR-based metadata handling.
# pylint: disable=import-outside-toplevel
from psyclone.psyir.backend.fortran import FortranWriter
writer = FortranWriter()
tl_source = writer(tl_container)
parse_tree = fpapi.parse(tl_source)

# Get the name of the module that contains the kernel and create a
# ContainerSymbol for it.
# Validate the module name before attempting to serialise its metadata.
kernel_mod_name = tl_container.name.lower()
if not kernel_mod_name.endswith("_mod"):
raise ValueError(
f"The supplied LFRic TL kernel is contained within a module named "
f"'{kernel_mod_name}'. This does not end in '_mod' and as such "
f"does not comply with the LFRic naming convention.")

kernel_mod = table.new_symbol(kernel_mod_name, symbol_type=ContainerSymbol)
# Assume the LFRic naming convention is followed in order to infer the name
# of the TL kernel. (If this convention isn't followed in the supplied code
# then the call to `kernel_from_metadata` below will raise an appropriate
# exception.)
# Assume the LFRic naming convention is followed in order to infer the
# name of the TL kernel.
kernel_name = kernel_mod_name.replace("_mod", "_type")

# Parse the kernel metadata. This still uses fparser1 as that's what
# the metadata handling is currently based upon. Serialise only the
# metadata and a placeholder implementation because unresolved metadata
# names are now visible in the language-level PSyIR.
# TODO #2151 - replace this with the new PSyIR-based metadata handling.
metadata_symbol = tl_container.symbol_table.lookup(kernel_name)
metadata = LFRicKernelMetadata.create_from_psyir(metadata_symbol)
procedure_name = metadata.procedure_name
tl_source = (
f"module {kernel_mod_name}\n"
f"{metadata.fortran_string()}\n"
"contains\n"
f"subroutine {procedure_name}()\n"
f"end subroutine {procedure_name}\n"
f"end module {kernel_mod_name}\n")
parse_tree = fpapi.parse(tl_source)

# Create a ContainerSymbol for the module containing the kernel.
kernel_mod = table.new_symbol(kernel_mod_name, symbol_type=ContainerSymbol)

adj_mod = table.new_symbol(create_adjoint_name(kernel_mod_name),
symbol_type=ContainerSymbol)
kernel_routine = table.new_symbol(kernel_name,
Expand Down
67 changes: 61 additions & 6 deletions src/psyclone/psyir/backend/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,52 @@ def gen_use(self, symbol, symbol_table):
f"{renames}\n")
return f"{self._nindent}use{intrinsic_str}{symbol.name}\n"

def gen_proceduredecl(
self, symbol: StructureType.ComponentType,
) -> str:
'''Create the Fortran declaration for a type-bound procedure.

:param symbol: the procedure component to declare.

:returns: the Fortran procedure declaration.

:raises VisitorError: if symbol is not a StructureType component.
:raises InternalError: if visibility is requested but is neither
PUBLIC nor PRIVATE.
'''
if not isinstance(symbol, StructureType.ComponentType):
raise VisitorError(
"gen_proceduredecl() expects a "
"'StructureType.ComponentType' as its first argument but "
f"got '{type(symbol).__name__}'")

result = self.gen_preceding_comments(symbol)

if isinstance(symbol.datatype, UnsupportedFortranType):
# The original declaration preserves attributes that have no
# representation in PSyIR, such as PASS, NOPASS and DEFERRED.
result += f"{self._nindent}{symbol.datatype.declaration}"
else:
result += f"{self._nindent}procedure"
if symbol.visibility == Symbol.Visibility.PRIVATE:
result += ", private"
elif symbol.visibility == Symbol.Visibility.PUBLIC:
result += ", public"
else:
raise InternalError(
"A type-bound procedure must be either public or "
f"private but procedure '{symbol.name}' has "
f"visibility '{symbol.visibility}'")

result += f" :: {symbol.name}"
if symbol.initial_value:
result += " => " + self._visit(symbol.initial_value)

if symbol.inline_comment:
result += f" {self._COMMENT_PREFIX}{symbol.inline_comment}"

return result + "\n"

def gen_vardecl(self,
symbol: Union[DataSymbol, Member],
include_visibility: bool = False) -> str:
Expand Down Expand Up @@ -723,6 +769,11 @@ def gen_typedecl(self, symbol, include_visibility=True):
f"gen_typedecl expects a DataTypeSymbol as argument but "
f"got: '{type(symbol).__name__}'")

if isinstance(symbol.datatype, UnresolvedType):
raise VisitorError(
f"Fortran backend cannot generate code for symbol "
f"'{symbol.name}' of type '{type(symbol.datatype).__name__}'")

if isinstance(symbol.datatype, UnsupportedType):
if isinstance(symbol.datatype, UnsupportedFortranType):
# This is a declaration of UnsupportedType. We have to ensure
Expand All @@ -740,6 +791,9 @@ def gen_typedecl(self, symbol, include_visibility=True):

result += f"{self._nindent}type"

if symbol.datatype.extends:
result += f", extends({symbol.datatype.extends.name})"

if include_visibility:
if symbol.visibility == Symbol.Visibility.PRIVATE:
result += ", private"
Expand All @@ -752,12 +806,6 @@ def gen_typedecl(self, symbol, include_visibility=True):
f"type '{type(symbol.visibility).__name__}'")
result += f" :: {symbol.name}\n"

if isinstance(symbol.datatype, UnresolvedType):
raise VisitorError(
f"Local Symbol '{symbol.name}' is of UnresolvedType and "
f"therefore no declaration can be created for it. Should it "
f"have an ImportInterface?")

self._depth += 1

for member in symbol.datatype.components.values():
Expand All @@ -766,6 +814,13 @@ def gen_typedecl(self, symbol, include_visibility=True):
# part of a module.
result += self.gen_vardecl(member,
include_visibility=include_visibility)

if symbol.datatype.procedure_components:
result += f"{self._nindent}contains\n"
self._depth += 1
for procedure in symbol.datatype.procedure_components.values():
result += self.gen_proceduredecl(procedure)
self._depth -= 1
self._depth -= 1

result += f"{self._nindent}end type {symbol.name}"
Expand Down
Loading
Loading