39 '''PSyIR Fortran backend. Implements a visitor that generates Fortran code
40 from a PSyIR tree. '''
48 Fparser2Reader, TYPE_MAP_FROM_FORTRAN)
50 BinaryOperation, Call, CodeBlock, DataNode, IntrinsicCall, Literal,
51 Operation, Range, Routine, Schedule, UnaryOperation)
53 ArgumentInterface, ArrayType, ContainerSymbol, DataSymbol, DataTypeSymbol,
54 GenericInterfaceSymbol, IntrinsicSymbol, PreprocessorInterface,
55 RoutineSymbol, ScalarType, StructureType, Symbol, SymbolTable,
56 UnresolvedInterface, UnresolvedType, UnsupportedFortranType,
63 TYPE_MAP_TO_FORTRAN = {}
64 for key, item
in TYPE_MAP_FROM_FORTRAN.items():
65 if key !=
"double precision":
66 TYPE_MAP_TO_FORTRAN[item] = key
69 def gen_intent(symbol):
70 '''Given a DataSymbol instance as input, determine the Fortran intent that
71 the DataSymbol should have and return the value as a string.
73 :param symbol: the symbol instance.
74 :type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol`
76 :returns: the Fortran intent of the symbol instance in lower case, \
77 or None if the access is unknown or if this is a local variable.
78 :rtype: str or NoneType
81 mapping = {ArgumentInterface.Access.UNKNOWN:
None,
82 ArgumentInterface.Access.READ:
"in",
83 ArgumentInterface.Access.WRITE:
"out",
84 ArgumentInterface.Access.READWRITE:
"inout"}
86 if symbol.is_argument:
88 return mapping[symbol.interface.access]
89 except KeyError
as excinfo:
91 f
"Unsupported access '{excinfo}' found.")
from excinfo
95 def gen_datatype(datatype, name):
96 '''Given a DataType instance as input, return the Fortran datatype
97 of the symbol including any specific precision properties.
99 :param datatype: the DataType or DataTypeSymbol describing the type of \
101 :type datatype: :py:class:`psyclone.psyir.symbols.DataType` or \
102 :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
103 :param str name: the name of the symbol being declared (only used for \
106 :returns: the Fortran representation of the symbol's datatype \
107 including any precision properties.
110 :raises NotImplementedError: if the symbol has an unsupported \
112 :raises VisitorError: if the symbol specifies explicit precision \
113 and this is not supported for the datatype.
114 :raises VisitorError: if the size of the explicit precision is not \
115 supported for the datatype.
116 :raises VisitorError: if the size of the symbol is specified by \
117 another variable and the datatype is not one that supports the \
119 :raises NotImplementedError: if the type of the precision object \
120 is an unsupported type.
123 if isinstance(datatype, DataTypeSymbol):
125 return f
"type({datatype.name})"
127 if (isinstance(datatype, ArrayType)
and
128 isinstance(datatype.intrinsic, DataTypeSymbol)):
130 return f
"type({datatype.intrinsic.name})"
133 fortrantype = TYPE_MAP_TO_FORTRAN[datatype.intrinsic]
134 except KeyError
as error:
135 raise NotImplementedError(
136 f
"Unsupported datatype '{datatype.intrinsic}' for symbol '{name}' "
137 f
"found in gen_datatype().")
from error
139 precision = datatype.precision
141 if isinstance(precision, int):
142 if fortrantype
not in [
'real',
'integer',
'logical']:
143 raise VisitorError(f
"Explicit precision not supported for datatype"
144 f
" '{fortrantype}' in symbol '{name}' in "
146 if fortrantype ==
'real' and precision
not in [4, 8, 16]:
148 f
"Datatype 'real' in symbol '{name}' supports fixed precision "
149 f
"of [4, 8, 16] but found '{precision}'.")
150 if fortrantype
in [
'integer',
'logical']
and precision
not in \
153 f
"Datatype '{fortrantype}' in symbol '{name}' supports fixed "
154 f
"precision of [1, 2, 4, 8, 16] but found '{precision}'.")
159 return f
"{fortrantype}*{precision}"
161 if isinstance(precision, ScalarType.Precision):
166 if fortrantype.lower() ==
"real" and \
167 precision == ScalarType.Precision.DOUBLE:
168 return "double precision"
178 if isinstance(precision, DataSymbol):
179 if fortrantype
not in [
"real",
"integer",
"logical"]:
181 f
"kind not supported for datatype '{fortrantype}' in symbol "
182 f
"'{name}' in Fortran backend.")
184 return f
"{fortrantype}(kind={precision.name})"
187 f
"Unsupported precision type '{type(precision).__name__}' found for "
188 f
"symbol '{name}' in Fortran backend.")
191 def precedence(fortran_operator):
192 '''Determine the relative precedence of the supplied Fortran operator.
193 Relative Operator precedence is taken from the Fortran 2008
194 specification document and encoded as a list.
196 :param str fortran_operator: the supplied Fortran operator.
198 :returns: an integer indicating the relative precedence of the \
199 supplied Fortran operator. The higher the value, the higher \
202 :raises KeyError: if the supplied operator is not in the \
215 fortran_precedence = [
220 [
'.EQ.',
'.NE.',
'.LT.',
'.LE.',
'.GT.',
'.GE.',
'==',
'/=',
'<',
226 for oper_list
in fortran_precedence:
227 if fortran_operator
in oper_list:
228 return fortran_precedence.index(oper_list)
232 def add_accessibility_to_unsupported_declaration(symbol):
234 Utility that manipulates the UnsupportedFortranType declaration for the
235 supplied Symbol so as to ensure that it has the correct accessibility
237 (This is required because we capture an UnsupportedFortranType declaration
238 as is and this may or may not include accessibility information.)
240 :param symbol: the symbol for which the declaration is required.
241 :type symbol: :py:class:`psyclone.psyir.symbols.Symbol`
243 :returns: Fortran declaration of the supplied symbol with accessibility \
244 information included (public/private).
247 :raises TypeError: if the supplied argument is not a Symbol of \
248 UnsupportedFortranType.
249 :raises InternalError: if the declaration associated with the Symbol is \
251 :raises NotImplementedError: if the original declaration does not use \
252 '::' to separate the entity name from its type.
253 :raises InternalError: if the declaration stored for the supplied symbol \
254 contains accessibility information which does not match the \
255 visibility of the supplied symbol.
258 if not isinstance(symbol, Symbol):
259 raise TypeError(f
"Expected a Symbol but got '{type(symbol).__name__}'")
261 if not isinstance(symbol.datatype, UnsupportedFortranType):
262 raise TypeError(f
"Expected a Symbol of UnsupportedFortranType but "
263 f
"symbol '{symbol.name}' has type '{symbol.datatype}'")
265 if not symbol.datatype.declaration:
267 f
"Symbol '{symbol.name}' is of UnsupportedFortranType but the "
268 f
"associated declaration text is empty.")
272 first_line = symbol.datatype.declaration.split(
"\n")[0]
273 if "::" not in first_line:
274 raise NotImplementedError(
275 f
"Cannot add accessibility information to an UnsupportedFortran"
276 f
"Type that does not have '::' in its original declaration: "
277 f
"'{symbol.datatype.declaration}'")
279 parts = symbol.datatype.declaration.split(
"::")
280 first_part = parts[0].lower()
281 if symbol.visibility == Symbol.Visibility.PUBLIC:
282 if "public" not in first_part:
283 if "private" in first_part:
285 f
"Symbol '{symbol.name}' of UnsupportedFortranType has "
286 f
"public visibility but its associated declaration "
287 f
"specifies that it is private: "
288 f
"'{symbol.datatype.declaration}'")
289 first_part = first_part.rstrip() +
", public "
291 if "private" not in first_part:
292 if "public" in first_part:
294 f
"Symbol '{symbol.name}' of UnsupportedFortranType has "
295 f
"private visibility but its associated declaration "
296 f
"specifies that it is public: "
297 f
"'{symbol.datatype.declaration}'")
298 first_part = first_part.rstrip() +
", private "
299 return "::".join([first_part]+parts[1:])
304 '''Implements a PSyIR-to-Fortran back end for PSyIR kernel code (not
305 currently PSyIR algorithm code which has its own gen method for
308 :param bool skip_nodes: If skip_nodes is False then an exception \
309 is raised if a visitor method for a PSyIR node has not been \
310 implemented, otherwise the visitor silently continues. This is an \
311 optional argument which defaults to False.
312 :param str indent_string: Specifies what to use for indentation. This \
313 is an optional argument that defaults to two spaces.
314 :param int initial_indent_depth: Specifies how much indentation to \
315 start with. This is an optional argument that defaults to 0.
316 :param bool check_global_constraints: whether or not to validate all \
317 global constraints when walking the tree. Defaults to True.
320 _COMMENT_PREFIX =
"! "
322 def __init__(self, skip_nodes=False, indent_string=" ",
323 initial_indent_depth=0, check_global_constraints=True):
326 super().__init__((
"(",
")"),
"%", skip_nodes,
328 initial_indent_depth,
329 check_global_constraints)
334 Fparser2Reader.unary_operators)
336 Fparser2Reader.binary_operators)
346 def _reverse_map(reverse_dict, op_map):
348 Reverses the supplied fortran2psyir mapping to make a psyir2fortran
349 mapping. Any key that does already exist in `reverse_dict`
350 is not overwritten, only new keys are added.
352 :param reverse_dict: the dictionary to which the new mapping of \
353 operator to string is added.
354 :type reverse_dict: dict from \
355 :py:class:`psyclone.psyir.nodes.BinaryOperation`, \
356 :py:class:`psyclone.psyir.nodes.NaryOperation` or \
357 :py:class:`psyclone.psyir.nodes.UnaryOperation` to str
359 :param op_map: mapping from string representation of operator to \
361 :type op_map: :py:class:`collections.OrderedDict`
364 for operator
in op_map:
365 mapping_key = op_map[operator]
366 mapping_value = operator
369 if mapping_key
not in reverse_dict:
370 reverse_dict[mapping_key] = mapping_value.upper()
373 '''Determine the Fortran operator that is equivalent to the provided
374 PSyIR operator. This is achieved by reversing the Fparser2Reader
375 maps that are used to convert from Fortran operator names to PSyIR
378 :param operator: a PSyIR operator.
379 :type operator: :py:class:`psyclone.psyir.nodes.Operation.Operator`
381 :returns: the Fortran operator.
384 :raises KeyError: if the supplied operator is not known.
390 '''Given a list of PSyIR nodes representing the dimensions of an
391 array, return a list of strings representing those array dimensions.
392 This is used both for array references and array declarations. Note
393 that 'indices' can also be a shape in case of Fortran.
395 :param indices: list of PSyIR nodes.
396 :type indices: list of :py:class:`psyclone.psyir.symbols.Node`
397 :param str var_name: name of the variable for which the dimensions \
398 are created. Not used in the Fortran implementation.
400 :returns: the Fortran representation of the dimensions.
403 :raises NotImplementedError: if the format of the dimension is not \
408 for index
in indices:
409 if isinstance(index, (DataNode, Range)):
412 expression = self.
_visit_visit(index)
413 dims.append(expression)
414 elif isinstance(index, ArrayType.ArrayBounds):
417 lower_expression = self.
_visit_visit(index.lower)
418 if isinstance(index.upper, ArrayType.Extent):
421 upper_expression =
""
423 upper_expression = self.
_visit_visit(index.upper)
424 if lower_expression ==
"1":
426 dims.append(upper_expression)
428 dims.append(lower_expression+
":"+upper_expression)
429 elif isinstance(index, ArrayType.Extent):
433 raise NotImplementedError(
434 f
"unsupported gen_indices index '{index}'")
438 ''' Performs consistency checks and then creates and returns the
439 Fortran use statement(s) for this ContainerSymbol as required for
440 the supplied symbol table.
442 :param symbol: the container symbol instance.
443 :type symbol: :py:class:`psyclone.psyir.symbols.ContainerSymbol`
444 :param symbol_table: the symbol table containing this container symbol.
445 :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
447 :returns: the Fortran use statement(s) as a string.
450 :raises VisitorError: if the symbol argument is not a ContainerSymbol.
451 :raises VisitorError: if the symbol_table argument is not a \
453 :raises VisitorError: if the supplied symbol is not in the supplied \
455 :raises VisitorError: if the supplied symbol has the same name as an \
456 entry in the SymbolTable but is a different object.
458 if not isinstance(symbol, ContainerSymbol):
460 f
"gen_use() expects a ContainerSymbol as its first argument "
461 f
"but got '{type(symbol).__name__}'")
462 if not isinstance(symbol_table, SymbolTable):
464 f
"gen_use() expects a SymbolTable as its second argument but "
465 f
"got '{type(symbol_table).__name__}'")
466 if symbol.name
not in symbol_table:
468 f
"gen_use() - the supplied symbol ('{symbol.name}') is not"
469 f
" in the supplied SymbolTable.")
470 if symbol_table.lookup(symbol.name)
is not symbol:
472 f
"gen_use() - the supplied symbol ('{symbol.name}') is not "
473 f
"the same object as the entry with that name in the supplied "
478 for dsym
in symbol_table.symbols_imported_from(symbol):
479 if dsym.interface.orig_name:
482 only_list.append(f
"{dsym.name}=>{dsym.interface.orig_name}")
485 only_list.append(dsym.name)
488 if not only_list
and not symbol.wildcard_import:
490 return f
"{self._nindent}use {symbol.name}, only :\n"
491 if only_list
and not symbol.wildcard_import:
492 return f
"{self._nindent}use {symbol.name}, only : " + \
493 ", ".join(sorted(only_list)) +
"\n"
495 return f
"{self._nindent}use {symbol.name}\n"
498 '''Create and return the Fortran variable declaration for this Symbol
499 or derived-type member.
501 :param symbol: the symbol or member instance.
502 :type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol` or
503 :py:class:`psyclone.psyir.nodes.MemberReference`
504 :param bool include_visibility: whether to include the visibility of
505 the symbol in the generated declaration (default False).
507 :returns: the Fortran variable declaration as a string.
510 :raises VisitorError: if the symbol is of UnresolvedType.
511 :raises VisitorError: if the symbol is of UnsupportedType other than
512 UnsupportedFortranType.
513 :raises VisitorError: if the symbol is of known type but does not
514 specify a variable declaration (it is not a local declaration or
515 an argument declaration).
516 :raises VisitorError: if the symbol is a runtime constant but does not
517 have a StaticInterface.
518 :raises InternalError: if the symbol is a ContainerSymbol or an import.
519 :raises InternalError: if the symbol is a RoutineSymbol other than
520 UnsupportedFortranType.
521 :raises InternalError: if visibility is to be included but is not
522 either PUBLIC or PRIVATE.
526 if isinstance(symbol.datatype, UnresolvedType):
527 raise VisitorError(f
"Symbol '{symbol.name}' has a UnresolvedType "
528 f
"and we can not generate a declaration for "
530 if isinstance(symbol, ContainerSymbol)
or \
531 isinstance(symbol, Symbol)
and symbol.is_import:
532 raise InternalError(f
"Symbol '{symbol.name}' is brought into scope"
533 f
" from a Fortran USE statement and should be "
534 f
"generated by 'gen_use' instead of "
536 if (isinstance(symbol, RoutineSymbol)
and
537 not isinstance(symbol.datatype, UnsupportedFortranType)):
538 raise InternalError(f
"Symbol '{symbol.name}' is a RoutineSymbol "
539 f
"which is not imported or of "
540 f
"UnsupportedFortranType. This is already "
541 f
"implicitly declared by the routine itself "
542 f
"and should not be provided to 'gen_vardecl'."
547 if isinstance(symbol.datatype, ArrayType):
548 array_shape = symbol.datatype.shape
552 if isinstance(symbol.datatype, UnsupportedType):
553 if isinstance(symbol.datatype, UnsupportedFortranType):
555 if (include_visibility
and
556 not isinstance(symbol, RoutineSymbol)
and
557 not symbol.name.startswith(
"_PSYCLONE_INTERNAL")):
561 decln = add_accessibility_to_unsupported_declaration(
563 return f
"{self._nindent}{decln}\n"
565 decln = symbol.datatype.declaration
566 return f
"{self._nindent}{decln}\n"
570 f
"{type(symbol).__name__} '{symbol.name}' is of "
571 f
"'{type(symbol.datatype).__name__}' type. This is not "
572 f
"supported by the Fortran backend.")
574 datatype = gen_datatype(symbol.datatype, symbol.name)
575 result = f
"{self._nindent}{datatype}"
577 if ArrayType.Extent.DEFERRED
in array_shape:
579 result +=
", allocatable"
584 result +=
", dimension(" +
",".join(dims) +
")"
586 if isinstance(symbol, DataSymbol)
and symbol.is_argument:
587 intent = gen_intent(symbol)
589 result += f
", intent({intent})"
591 if isinstance(symbol, DataSymbol)
and symbol.is_constant:
592 result +=
", parameter"
593 elif isinstance(symbol, DataSymbol)
and symbol.is_static:
599 if include_visibility:
600 if symbol.visibility == Symbol.Visibility.PRIVATE:
601 result +=
", private"
602 elif symbol.visibility == Symbol.Visibility.PUBLIC:
606 f
"A Symbol must be either public or private but symbol "
607 f
"'{symbol.name}' has visibility '{symbol.visibility}'")
610 result += f
" :: {symbol.name}"
613 if (isinstance(symbol, StructureType.ComponentType)
and
614 symbol.initial_value):
615 result +=
" = " + self.
_visit_visit(symbol.initial_value)
616 elif isinstance(symbol, DataSymbol)
and symbol.initial_value:
617 if not symbol.is_static:
619 f
"{type(symbol).__name__} '{symbol.name}' has an initial "
620 f
"value ({self._visit(symbol.initial_value)}) and "
621 f
"therefore (in Fortran) must have a StaticInterface. "
622 f
"However it has an interface of '{symbol.interface}'.")
623 result +=
" = " + self.
_visit_visit(symbol.initial_value)
629 Generate the declaration for a generic interface.
631 Since a GenericInterfaceSymbol is a subclass of RoutineSymbol, any
632 necessary accessibility statement will be generated in
635 :param symbol: the GenericInterfaceSymbol to be declared.
636 :type symbol: :py:class:`psyclone.psyir.symbols.GenericInterfaceSymbol`
638 :returns: the corresponding Fortran declaration.
641 :raises InternalError: if passed something that is not a
642 GenericInterfaceSymbol.
645 if not isinstance(symbol, GenericInterfaceSymbol):
647 f
"gen_interfacedecl only supports 'GenericInterfaceSymbol's "
648 f
"but got '{type(symbol).__name__}'")
650 decln = f
"{self._nindent}interface {symbol.name}\n"
653 routines =
", ".join([rsym.name
for rsym
in symbol.container_routines])
655 decln += f
"{self._nindent}module procedure :: {routines}\n"
657 routines =
", ".join([rsym.name
for rsym
in symbol.external_routines])
659 decln += f
"{self._nindent}procedure :: {routines}\n"
661 decln += f
"{self._nindent}end interface {symbol.name}\n"
667 Creates a derived-type declaration for the supplied DataTypeSymbol.
669 :param symbol: the derived-type to declare.
670 :type symbol: :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
671 :param bool include_visibility: whether or not to include visibility
672 information in the declaration. (Default is True.)
674 :returns: the Fortran declaration of the derived type.
677 :raises VisitorError: if the supplied symbol is not a DataTypeSymbol.
678 :raises VisitorError: if the datatype of the symbol is of
679 UnsupportedType but is not of UnsupportedFortranType.
680 :raises InternalError: if include_visibility is True and the
681 visibility of the symbol is not of the correct type.
682 :raises VisitorError: if the supplied symbol is of UnresolvedType.
685 if not isinstance(symbol, DataTypeSymbol):
687 f
"gen_typedecl expects a DataTypeSymbol as argument but "
688 f
"got: '{type(symbol).__name__}'")
690 if isinstance(symbol.datatype, UnsupportedType):
691 if isinstance(symbol.datatype, UnsupportedFortranType):
694 if include_visibility:
695 decln = add_accessibility_to_unsupported_declaration(
698 decln = symbol.datatype.declaration
699 return f
"{self._nindent}{decln}\n"
702 f
"Fortran backend cannot generate code for symbol "
703 f
"'{symbol.name}' of type '{type(symbol.datatype).__name__}'")
705 result = f
"{self._nindent}type"
707 if include_visibility:
708 if symbol.visibility == Symbol.Visibility.PRIVATE:
709 result +=
", private"
710 elif symbol.visibility == Symbol.Visibility.PUBLIC:
714 f
"A Symbol's visibility must be one of Symbol.Visibility."
715 f
"PRIVATE/PUBLIC but '{symbol.name}' has visibility of "
716 f
"type '{type(symbol.visibility).__name__}'")
717 result += f
" :: {symbol.name}\n"
719 if isinstance(symbol.datatype, UnresolvedType):
721 f
"Local Symbol '{symbol.name}' is of UnresolvedType and "
722 f
"therefore no declaration can be created for it. Should it "
723 f
"have an ImportInterface?")
727 for member
in symbol.datatype.components.values():
732 include_visibility=include_visibility)
735 result += f
"{self._nindent}end type {symbol.name}\n"
740 Generates the access statement for a module - either "private" or
741 "public". Although the PSyIR captures the visibility of every Symbol
742 explicitly, this information is required in order
743 to ensure the correct visibility of symbols that have been imported
744 into the current module from another one using a wildcard import
745 (i.e. a `use` without an `only` clause) and also for those Symbols
746 that are of UnsupportedFortranType (because their declaration may or
747 may not include visibility information).
749 :returns: text containing the access statement line.
752 :raises InternalError: if the symbol table has an invalid default \
757 if symbol_table.default_visibility
in [
None, Symbol.Visibility.PUBLIC]:
758 return self.
_nindent_nindent +
"public\n"
759 if symbol_table.default_visibility == Symbol.Visibility.PRIVATE:
760 return self.
_nindent_nindent +
"private\n"
763 f
"Unrecognised visibility ('{symbol_table.default_visibility}') "
764 f
"found when attempting to generate access statement. Should be "
765 f
"either 'Symbol.Visibility.PUBLIC' or "
766 f
"'Symbol.Visibility.PRIVATE'\n")
770 Creates the accessibility statements (R518) for any routine or
771 imported symbols in the supplied symbol table.
773 :param symbol_table: the symbol table for which to generate \
774 accessibility statements.
775 :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
777 :returns: the accessibility statements for any routine or imported \
785 itself = symbol_table.lookup_with_tag(
'own_routine_symbol')
791 for symbol
in symbol_table.symbols:
792 if (isinstance(symbol, RoutineSymbol)
or
793 symbol.is_unresolved
or symbol.is_import):
797 if isinstance(symbol, RoutineSymbol)
and symbol
is itself:
807 if (symbol_table.default_visibility
in
808 [
None, Symbol.Visibility.PUBLIC]):
809 if symbol.visibility == Symbol.Visibility.PRIVATE:
811 private_symbols.append(symbol.name)
813 if symbol.visibility == Symbol.Visibility.PUBLIC:
815 public_symbols.append(symbol.name)
819 result += f
"{self._nindent}public :: {', '.join(public_symbols)}\n"
821 result += (f
"{self._nindent}private :: "
822 f
"{', '.join(private_symbols)}\n")
829 def _gen_parameter_decls(self, symbol_table, is_module_scope=False):
830 ''' Create the declarations of all parameters present in the supplied
831 symbol table. Declarations are ordered so as to satisfy any inter-
832 dependencies between them.
834 :param symbol_table: the SymbolTable instance.
835 :type symbol: :py:class:`psyclone.psyir.symbols.SymbolTable`
836 :param bool is_module_scope: whether or not the declarations are in \
837 a module scoping unit. Default is False.
839 :returns: Fortran code declaring all parameters.
842 :raises VisitorError: if there is no way of resolving \
843 interdependencies between parameter declarations.
848 for sym
in symbol_table.datasymbols:
849 if sym.is_import
or sym.is_unresolved:
852 local_constants.append(sym)
860 for symbol
in local_constants:
861 decln_inputs[symbol.name] = set()
862 read_write_info = ReadWriteInfo()
864 symbol.initial_value)
867 for lit
in symbol.initial_value.walk(Literal):
868 if isinstance(lit.datatype.precision, DataSymbol):
869 read_write_info.add_read(
873 if isinstance(symbol.datatype.precision, DataSymbol):
874 read_write_info.add_read(
875 Signature(symbol.datatype.precision.name))
878 for sig
in read_write_info.signatures_read:
879 if symbol_table.lookup(sig.var_name)
in local_constants:
880 decln_inputs[symbol.name].add(sig)
885 while local_constants:
886 for symbol
in local_constants[:]:
887 inputs = decln_inputs[symbol.name]
888 if inputs.issubset(declared):
891 local_constants.remove(symbol)
893 symbol, include_visibility=is_module_scope)
899 f
"Unable to satisfy dependencies for the declarations of "
900 f
"{[sym.name for sym in local_constants]}")
903 def gen_decls(self, symbol_table, is_module_scope=False):
904 '''Create and return the Fortran declarations for the supplied
907 :param symbol_table: the SymbolTable instance.
908 :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
909 :param bool is_module_scope: whether or not the declarations are in
910 a module scoping unit. Default is False.
912 :returns: the Fortran declarations for the table.
915 :raises VisitorError: if one of the symbols is a RoutineSymbol which
916 does not have an ImportInterface or UnresolvedInterface (
917 representing named and unqualified imports respectively) or
918 ModuleDefaultInterface (representing routines declared in the
919 same module) or is not a Fortran intrinsic.
920 :raises VisitorError: if args_allowed is False and one or more
921 argument declarations exist in symbol_table.
922 :raises VisitorError: if there are any symbols (other than
923 RoutineSymbols) in the supplied table that do not have an
924 explicit declaration (UnresolvedInterface) and there are no
925 wildcard imports or unknown interfaces.
932 all_symbols = symbol_table.symbols
935 for sym
in all_symbols[:]:
938 if isinstance(sym, ContainerSymbol):
939 all_symbols.remove(sym)
942 all_symbols.remove(sym)
947 if isinstance(sym, IntrinsicSymbol)
or (
948 isinstance(sym, RoutineSymbol)
and
949 isinstance(sym.interface, UnresolvedInterface)):
950 all_symbols.remove(sym)
952 if isinstance(sym.interface, PreprocessorInterface):
953 all_symbols.remove(sym)
961 unresolved_symbols = []
962 for sym
in all_symbols[:]:
963 if isinstance(sym.interface, UnresolvedInterface):
964 unresolved_symbols.append(sym)
965 all_symbols.remove(sym)
967 internal_interface_symbol = symbol_table.lookup(
968 "_psyclone_internal_interface")
970 internal_interface_symbol =
None
971 if unresolved_symbols
and not (
972 symbol_table.wildcard_imports()
or internal_interface_symbol):
973 symbols_txt =
", ".join(
974 [
"'" + sym.name +
"'" for sym
in unresolved_symbols])
976 f
"The following symbols are not explicitly declared or "
977 f
"imported from a module and there are no wildcard "
978 f
"imports which could be bringing them into scope: "
986 for sym
in all_symbols[:]:
987 if not isinstance(sym, RoutineSymbol):
991 if isinstance(sym, GenericInterfaceSymbol):
993 elif isinstance(sym.datatype, UnsupportedType):
995 sym, include_visibility=is_module_scope)
996 elif not (sym.is_modulevar
or sym.is_automatic):
998 f
"Routine symbol '{sym.name}' has '{sym.interface}'. "
999 f
"This is not supported by the Fortran back-end.")
1000 all_symbols.remove(sym)
1005 for sym
in all_symbols[:]:
1006 if isinstance(sym, DataSymbol)
and sym.is_constant:
1007 all_symbols.remove(sym)
1010 if symbol_table.argument_datasymbols
and is_module_scope:
1012 f
"Arguments are not allowed in this context but this symbol "
1013 f
"table contains argument(s): "
1014 f
"'{[sym.name for sym in symbol_table.argument_datasymbols]}'."
1018 for symbol
in symbol_table.argument_datasymbols:
1020 symbol, include_visibility=is_module_scope)
1021 all_symbols.remove(symbol)
1025 for symbol
in all_symbols[:]:
1026 if isinstance(symbol, DataTypeSymbol):
1028 symbol, include_visibility=is_module_scope)
1029 all_symbols.remove(symbol)
1032 for symbol
in all_symbols:
1034 symbol, include_visibility=is_module_scope)
1039 '''This method is called when a FileContainer instance is found in
1042 A file container node requires no explicit text in the Fortran
1045 :param node: a Container PSyIR node.
1046 :type node: :py:class:`psyclone.psyir.nodes.FileContainer`
1048 :returns: the Fortran code as a string.
1051 :raises VisitorError: if the attached symbol table contains \
1053 :raises VisitorError: if more than one child is a Routine Node \
1054 with is_program set to True.
1057 if node.symbol_table.symbols:
1059 f
"In the Fortran backend, a file container should not have "
1060 f
"any symbols associated with it, but found "
1061 f
"{len(node.symbol_table.symbols)}.")
1063 program_nodes = len([child
for child
in node.children
if
1064 isinstance(child, Routine)
and child.is_program])
1065 if program_nodes > 1:
1067 f
"In the Fortran backend, a file container should contain at "
1068 f
"most one routine node that is a program, but found "
1069 f
"{program_nodes}.")
1072 for child
in node.children:
1073 result += self.
_visit_visit(child)
1077 '''This method is called when a Container instance is found in
1080 A container node is mapped to a module in the Fortran back end.
1082 :param node: a Container PSyIR node.
1083 :type node: :py:class:`psyclone.psyir.nodes.Container`
1085 :returns: the Fortran code as a string.
1088 :raises VisitorError: if the name attribute of the supplied \
1089 node is empty or None.
1090 :raises VisitorError: if any of the children of the supplied \
1091 Container node are not Routines or CodeBlocks.
1095 raise VisitorError(
"Expected Container node name to have a value.")
1099 if not all(isinstance(child, (Routine, CodeBlock))
for
1100 child
in node.children):
1102 f
"The Fortran back-end requires all children of a Container "
1103 f
"to be either CodeBlocks or sub-classes of Routine but found:"
1104 f
" {[type(child).__name__ for child in node.children]}.")
1106 result = f
"{self._nindent}module {node.name}\n"
1112 for symbol
in node.symbol_table.containersymbols:
1113 imports += self.
gen_usegen_use(symbol, node.symbol_table)
1116 declarations = self.
gen_declsgen_decls(node.symbol_table, is_module_scope=
True)
1126 for child
in node.children:
1127 subroutines += self.
_visit_visit(child)
1131 f
"{self._nindent}implicit none\n"
1133 f
"{self._nindent}contains\n"
1137 result += f
"{self._nindent}end module {node.name}\n"
1141 '''This method is called when a Routine node is found in
1144 :param node: a Routine PSyIR node.
1145 :type node: :py:class:`psyclone.psyir.nodes.Routine`
1147 :returns: the Fortran code for this node.
1150 :raises VisitorError: if the name attribute of the supplied \
1151 node is empty or None.
1155 raise VisitorError(
"Expected node name to have a value.")
1158 result = f
"{self._nindent}program {node.name}\n"
1159 routine_type =
"program"
1161 args = [symbol.name
for symbol
in node.symbol_table.argument_list]
1163 if node.return_symbol:
1165 routine_type =
"function"
1166 if node.return_symbol.name.lower() != node.name.lower():
1167 suffix = f
" result({node.return_symbol.name})"
1169 routine_type =
"subroutine"
1170 result = f
"{self._nindent}{routine_type} {node.name}("
1171 result +=
", ".join(args) + f
"){suffix}\n"
1179 whole_routine_scope = node.symbol_table
1187 whole_routine_scope = type(node.symbol_table)()
1189 for schedule
in node.walk(Schedule):
1190 sched_table = schedule.symbol_table
1194 rsym = sched_table.lookup_with_tag(
"own_routine_symbol")
1195 skip = [rsym]
if isinstance(rsym, RoutineSymbol)
else []
1198 whole_routine_scope.merge(sched_table, skip)
1199 if schedule
is node:
1204 node.symbol_table.detach()
1205 whole_routine_scope.attach(node)
1209 for symbol
in whole_routine_scope.containersymbols:
1210 imports += self.
gen_usegen_use(symbol, whole_routine_scope)
1213 declarations = self.
gen_declsgen_decls(whole_routine_scope)
1216 exec_statements =
""
1217 for child
in node.children:
1218 exec_statements += self.
_visit_visit(child)
1222 f
"{exec_statements}\n")
1225 result += f
"{self._nindent}end {routine_type} {node.name}\n"
1230 '''This method is called when an Assignment instance is found in the
1233 :param node: an Assignment PSyIR node.
1234 :type node: :py:class:`psyclone.psyir.nodes.Assignment``
1236 :returns: the Fortran code as a string.
1240 lhs = self.
_visit_visit(node.lhs)
1241 rhs = self.
_visit_visit(node.rhs)
1242 result = f
"{self._nindent}{lhs} = {rhs}\n"
1246 '''This method is called when a BinaryOperation instance is found in
1249 :param node: a BinaryOperation PSyIR node.
1250 :type node: :py:class:`psyclone.psyir.nodes.BinaryOperation`
1252 :returns: the Fortran code as a string.
1256 lhs = self.
_visit_visit(node.children[0])
1257 rhs = self.
_visit_visit(node.children[1])
1259 fort_oper = self.
get_operatorget_operator(node.operator)
1260 parent = node.parent
1261 if isinstance(parent, Operation):
1263 parent_fort_oper = self.
get_operatorget_operator(parent.operator)
1264 if precedence(fort_oper) < precedence(parent_fort_oper):
1266 return f
"({lhs} {fort_oper} {rhs})"
1267 if precedence(fort_oper) == precedence(parent_fort_oper):
1269 if (isinstance(parent, UnaryOperation)
or
1270 (isinstance(parent, BinaryOperation)
and
1271 parent.children[1] == node)):
1277 return f
"({lhs} {fort_oper} {rhs})"
1278 return f
"{lhs} {fort_oper} {rhs}"
1279 except KeyError
as error:
1281 f
"Unexpected binary op '{node.operator}'.")
from error
1284 '''This method is called when a Range instance is found in the PSyIR
1287 :param node: a Range PSyIR node.
1288 :type node: :py:class:`psyclone.psyir.nodes.Range`
1290 :returns: the Fortran code as a string.
1294 if node.parent
and node.parent.is_lower_bound(
1295 node.parent.indices.index(node)):
1301 start = self.
_visit_visit(node.start)
1303 if node.parent
and node.parent.is_upper_bound(
1304 node.parent.indices.index(node)):
1310 stop = self.
_visit_visit(node.stop)
1311 result = f
"{start}:{stop}"
1313 if isinstance(node.step, Literal)
and \
1314 node.step.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
and \
1315 node.step.value ==
"1":
1320 step = self.
_visit_visit(node.step)
1321 result += f
":{step}"
1325 '''This method is called when a Literal instance is found in the PSyIR
1328 :param node: a Literal PSyIR node.
1329 :type node: :py:class:`psyclone.psyir.nodes.Literal`
1331 :returns: the Fortran code for the literal.
1336 precision = node.datatype.precision
1338 if node.datatype.intrinsic == ScalarType.Intrinsic.BOOLEAN:
1340 result =
'.' + node.value +
'.'
1341 elif node.datatype.intrinsic == ScalarType.Intrinsic.CHARACTER:
1346 if "'" not in node.value:
1353 if '"' in node.value:
1354 raise NotImplementedError(
1355 f
"Character literals containing both single and double"
1356 f
" quotes are not supported but found >>{node.value}<<"
1359 result = f
"{quote_symbol}{node.value}{quote_symbol}"
1360 elif (node.datatype.intrinsic == ScalarType.Intrinsic.REAL
and
1361 precision == ScalarType.Precision.DOUBLE):
1365 result = node.value.replace(
"e",
"d", 1)
1369 if isinstance(precision, DataSymbol):
1371 if node.datatype.intrinsic == ScalarType.Intrinsic.CHARACTER:
1372 result = f
"{precision.name}_{result}"
1374 result = f
"{result}_{precision.name}"
1375 if isinstance(precision, int):
1377 if node.datatype.intrinsic == ScalarType.Intrinsic.CHARACTER:
1378 result = f
"{precision}_{result}"
1380 result = f
"{result}_{precision}"
1385 '''This method is called when an IfBlock instance is found in the
1388 :param node: an IfBlock PSyIR node.
1389 :type node: :py:class:`psyclone.psyir.nodes.IfBlock`
1391 :returns: the Fortran code as a string.
1395 condition = self.
_visit_visit(node.children[0])
1399 for child
in node.if_body:
1400 if_body += self.
_visit_visit(child)
1404 for child
in node.else_body:
1405 else_body += self.
_visit_visit(child)
1410 f
"{self._nindent}if ({condition}) then\n"
1412 f
"{self._nindent}else\n"
1414 f
"{self._nindent}end if\n")
1417 f
"{self._nindent}if ({condition}) then\n"
1419 f
"{self._nindent}end if\n")
1423 '''This method is called when a WhileLoop instance is found in the
1426 :param node: a WhileLoop PSyIR node.
1427 :type node: :py:class:`psyclone.psyir.nodes.WhileLoop`
1429 :returns: the Fortran code.
1433 condition = self.
_visit_visit(node.condition)
1437 for child
in node.loop_body:
1438 body += self.
_visit_visit(child)
1442 f
"{self._nindent}do while ({condition})\n"
1444 f
"{self._nindent}end do\n")
1448 '''This method is called when a Loop instance is found in the
1451 :param node: a Loop PSyIR node.
1452 :type node: :py:class:`psyclone.psyir.nodes.Loop`
1454 :returns: the loop node converted into a (language specific) string.
1458 start = self.
_visit_visit(node.start_expr)
1459 stop = self.
_visit_visit(node.stop_expr)
1460 step = self.
_visit_visit(node.step_expr)
1464 for child
in node.loop_body:
1465 body += self.
_visit_visit(child)
1471 variable_name = node.variable.name
1472 except GenerationError:
1479 f
"{self._nindent}do {variable_name} = {start}, {stop}, {step}\n"
1481 f
"{self._nindent}enddo\n")
1484 '''This method is called when a UnaryOperation instance is found in
1487 :param node: a UnaryOperation PSyIR node.
1488 :type node: :py:class:`psyclone.psyir.nodes.UnaryOperation`
1490 :returns: the Fortran code as a string.
1493 :raises VisitorError: if an unexpected Unary op is encountered.
1496 content = self.
_visit_visit(node.children[0])
1498 fort_oper = self.
get_operatorget_operator(node.operator)
1505 parent = node.parent
1506 if isinstance(parent, UnaryOperation):
1507 return f
"({fort_oper}{content})"
1508 if isinstance(parent, BinaryOperation):
1509 parent_fort_oper = self.
get_operatorget_operator(parent.operator)
1510 if (node
is parent.children[1]
or
1511 (parent_fort_oper ==
"**" and fort_oper ==
"-")):
1512 return f
"({fort_oper}{content})"
1513 grandparent = parent.parent
1517 if isinstance(grandparent, BinaryOperation):
1519 grandparent.operator
1521 if (parent
is grandparent.children[1]
1522 and node
is parent.children[0]
1523 and (precedence(parent_fort_oper)
1524 > precedence(grandparent_fort_oper))
1525 and fort_oper ==
"-"):
1526 return f
"({fort_oper}{content})"
1527 return f
"{fort_oper}{content}"
1529 except KeyError
as error:
1531 f
"Unexpected unary op '{node.operator}'.")
from error
1534 '''This method is called when a Return instance is found in
1537 :param node: a Return PSyIR node.
1538 :type node: :py:class:`psyclone.psyir.nodes.Return`
1540 :returns: the Fortran code as a string.
1544 return f
"{self._nindent}return\n"
1547 '''This method is called when a CodeBlock instance is found in the
1548 PSyIR tree. It returns the content of the CodeBlock as a
1549 Fortran string, indenting as appropriate.
1551 :param node: a CodeBlock PSyIR node.
1552 :type node: :py:class:`psyclone.psyir.nodes.CodeBlock`
1554 :returns: the Fortran code as a string.
1559 if node.structure == CodeBlock.Structure.STATEMENT:
1561 for ast_node
in node.get_ast_nodes:
1564 for line
in ast_node.tofortran().split(
"\n"):
1565 result += f
"{self._nindent}{line}\n"
1566 elif node.structure == CodeBlock.Structure.EXPRESSION:
1567 for ast_node
in node.get_ast_nodes:
1568 result += str(ast_node)
1571 f
"Unsupported CodeBlock Structure '{node.structure}' found.")
1575 '''This method is called when a OperandClause is
1576 found in the PSyIR tree. It returns the clause and its children
1579 :param node: an OperandClause PSyIR node.
1580 :type node: :py:class:`psyclone.psyir.nodes.OperandClause`
1582 :returns: the Fortran code for this node.
1586 if len(node.children) == 0:
1589 result = node.clause_string
1591 result = result +
"(" + node.operand +
": "
1594 for child
in node.children:
1595 child_list.append(self.
_visit_visit(child))
1597 result = result +
",".join(child_list) +
")"
1602 '''This method is called when a RegionDirective instance is found in
1603 the PSyIR tree. It returns the opening and closing directives, and
1604 the statements in between as a string.
1606 :param node: a RegionDirective PSyIR node.
1607 :type node: :py:class:`psyclone.psyir.nodes.RegionDirective`
1609 :returns: the Fortran code for this node.
1613 result = f
"{self._nindent}!${node.begin_string()}"
1616 for clause
in node.clauses:
1617 val = self.
_visit_visit(clause)
1621 clause_list.append(val)
1623 if len(clause_list) > 0:
1624 result = result +
" "
1625 result = result +
", ".join(clause_list)
1626 result = result +
"\n"
1628 for child
in node.dir_body:
1629 result = result + self.
_visit_visit(child)
1631 end_string = node.end_string()
1633 result = result + f
"{self._nindent}!${end_string}\n"
1637 '''This method is called when a StandaloneDirective instance is found
1638 in the PSyIR tree. It returns the directive as a string.
1640 :param node: a StandaloneDirective PSyIR node.
1641 :type node: :py:class:`psyclone.psyir.nodes.StandloneDirective`
1643 :returns: the Fortran code for this node.
1647 result = f
"{self._nindent}!${node.begin_string()}"
1658 result = result +
", ".join(clause_list)
1659 result = result +
"\n"
1663 def _gen_arguments(self, node):
1664 '''Utility function that check that all named args occur after all
1665 positional args. This is a Fortran restriction, not a PSyIR
1666 restriction. And if they are valid, it returns the whole list of
1669 :param node: the node to check.
1670 :type node: :py:class:`psyclone.psyir.nodes.Call`
1671 :returns: string representation of the complete list of arguments.
1674 raises TypeError: if the provided node is not a Call.
1675 raises VisitorError: if the all of the positional arguments are
1676 not before all of the named arguments.
1679 if not isinstance(node, Call):
1681 f
"The _gen_arguments utility function expects a "
1682 f
"Call node, but found '{type(node).__name__}'.")
1684 found_named_arg =
False
1685 for name
in node.argument_names:
1686 if found_named_arg
and not name:
1688 f
"Fortran expects all named arguments to occur after all "
1689 f
"positional arguments but this is not the case for "
1692 found_named_arg =
True
1696 for idx, child
in enumerate(node.arguments):
1697 if node.argument_names[idx]:
1699 f
"{node.argument_names[idx]}={self._visit(child)}")
1701 result_list.append(self.
_visit_visit(child))
1702 return ", ".join(result_list)
1705 '''Translate the PSyIR call node to Fortran.
1707 :param node: a Call PSyIR node.
1708 :type node: :py:class:`psyclone.psyir.nodes.Call`
1710 :returns: the equivalent Fortran code.
1715 if isinstance(node, IntrinsicCall)
and node.routine.name
in [
1716 "ALLOCATE",
"DEALLOCATE"]:
1718 return f
"{self._nindent}{node.routine.name}({args})\n"
1719 if not node.parent
or isinstance(node.parent, Schedule):
1720 return f
"{self._nindent}call {self._visit(node.routine)}({args})\n"
1723 return f
"{self._visit(node.routine)}({args})"
1727 Translate the Kernel functor into Fortran.
1729 :param node: the PSyIR node to translate.
1730 :type node: :py:class:`psyclone.domain.common.algorithm.KernelFunctor`
1732 :returns: the equivalent Fortran code.
1737 for child
in node.children:
1738 result_list.append(self.
_visit_visit(child))
1739 args =
", ".join(result_list)
1740 return f
"{node.name}({args})"
def loop_node(self, node)
def gen_interfacedecl(self, symbol)
def codeblock_node(self, node)
def assignment_node(self, node)
def kernelfunctor_node(self, node)
def standalonedirective_node(self, node)
def whileloop_node(self, node)
def filecontainer_node(self, node)
def gen_default_access_stmt(self, symbol_table)
def gen_decls(self, symbol_table, is_module_scope=False)
def range_node(self, node)
def container_node(self, node)
def unaryoperation_node(self, node)
def gen_indices(self, indices, var_name=None)
def _gen_arguments(self, node)
def binaryoperation_node(self, node)
def operandclause_node(self, node)
def get_operator(self, operator)
def ifblock_node(self, node)
def _gen_parameter_decls(self, symbol_table, is_module_scope=False)
def gen_typedecl(self, symbol, include_visibility=True)
def gen_use(self, symbol, symbol_table)
def call_node(self, node)
def routine_node(self, node)
def regiondirective_node(self, node)
def literal_node(self, node)
def gen_vardecl(self, symbol, include_visibility=False)
def gen_access_stmts(self, symbol_table)
def _reverse_map(reverse_dict, op_map)
def gen_indices(self, indices, var_name=None)